/*
 * This file is part of the Vamsas Client version 0.2. 
 * Copyright 2010 by Jim Procter, Iain Milne, Pierre Marguerite, 
 *  Andrew Waterhouse and Dominik Lindner.
 * 
 * Earlier versions have also been incorporated into Jalview version 2.4 
 * since 2008, and TOPALi version 2 since 2007.
 * 
 * The Vamsas Client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *  
 * The Vamsas Client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with the Vamsas Client.  If not, see <http://www.gnu.org/licenses/>.
 */
package uk.ac.vamsas.test.simpleclient;

import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import uk.ac.vamsas.objects.core.Alignment;
import uk.ac.vamsas.objects.core.ApplicationData;
import uk.ac.vamsas.objects.core.Entry;
import uk.ac.vamsas.objects.core.Instance;
import uk.ac.vamsas.objects.core.Provenance;
import uk.ac.vamsas.objects.core.VAMSAS;
import uk.ac.vamsas.objects.core.VamsasDocument;
import uk.ac.vamsas.objects.utils.ProvenanceStuff;
import uk.ac.vamsas.client.simpleclient.VamsasArchive;
import uk.ac.vamsas.client.simpleclient.VamsasArchiveReader;

public class ArchiveWriter {

  /**
   * Test program for writing archive files. form is ArchiveWriter new/modified
   * argive command list
   */

  static Log log = LogFactory.getLog(ArchiveWriter.class);

  private static void mergeVecs(Object[] destvec, Object[] svec1, Object[] svec2) {
    int i;
    for (i = 0; i < svec1.length; i++)
      destvec[i] = svec1[i];
    for (int j = 0; j < svec2.length; i++, j++)
      destvec[i] = svec2[j];
  }

  // Merge appDataReferences require transfer of jar entries, perhaps with a
  // renaming of the entry.
  // Merge appDatas require eventually unique URNS
  // TODO: merging global appdata from different documents where same app has
  // written them causes conflict

  public static Hashtable hashOfAppDatas(Hashtable ht, Instance[] appdatas) {
    if (ht == null)
      ht = new Hashtable();
    for (int i = 0, j = appdatas.length; i < j; i++) {
      if (!ht.containsKey(appdatas[i].getUrn())) {
        Hashtable aphash = new Hashtable();
        ht.put(appdatas[i].getUrn(), aphash);
        aphash.put(appdatas[i], appdatas[i].getDataReference());
      } else {
        // ensure urns and references are unique

      }

    }
    return ht;
  }

  /**
   * safely copies an appData from one archive to another.
   * 
   * @param darc
   *          destination archive
   * @param dest
   *          destination document Vobject
   * @param sarc
   *          source archive reader
   * @param entry
   *          application data to be copied from source archive
   */
  public static void addAppDataEntry(VamsasArchive darc, VamsasDocument dest,
      VamsasArchiveReader sarc, ApplicationData entry) {
    // TODO: fix instances
    // check uniqueness of instance's[] entry.urn amongst
    // dest.ApplicationData[].getInstances[].urn
    // check uniqueness of entry.user[].urn amongst
    // dest.ApplicationData[].user[].urn
    // check uniqueness of entry.user
    // entry.getAppDataChoice().getData() or getDataReference is unique
    ApplicationData newo = new ApplicationData();
    for (int i = 0, j = dest.getApplicationDataCount(); i < j; i++) {
      ApplicationData o = dest.getApplicationData()[i];
      // ensure new urn is really unique
      // String urn = entry.getUrn();
      int v = 1;
      // while (o.getUrn().equals(urn)) {
      // urn = entry.getUrn()+v++;
      // }
      // uniqueness of urn
      // check each user ApplicationData
      // uniqueness (again)
      // copy over valid objects
      // 
    }
  }

  /**
   * Copy new datasets and appdatas from one vamsas document to another.
   * 
   * @param darc
   * @param dest
   * @param sarc
   * @param source
   * @return true if merge was successful.
   */
  public static boolean mergeDocs(VamsasArchive darc, VamsasDocument dest,
      VamsasArchiveReader sarc, VamsasDocument source) {
    log.debug("mergeDocs entered.");
    // search for appDatas in cdoc
    VAMSAS[] newr = new VAMSAS[dest.getVAMSASCount() + source.getVAMSASCount()];
    mergeVecs(newr, dest.getVAMSAS(), source.getVAMSAS());
    dest.setVAMSAS(newr);
    /**
     * TODO: LATER: should verify that all ids really are unique in newly merged
     * document. If not then what ? investigate possibility of having an id
     * translation between appDatas and the core document - the mapping is
     * stored when an external application performs a merge, but when the owning
     * Application accesses the Vobject, the vorba_id is updated to the new one
     * when it writes its references in to its appdata again
     */
    if (source.getApplicationDataCount() > 0) {
      ApplicationData[] newdat = new ApplicationData[source
          .getApplicationDataCount()
          + dest.getApplicationDataCount()];
      ApplicationData[] sappd = source.getApplicationData();
      // check refs and update/modify if necessary
      for (int i = 0; i < sappd.length; i++) {
        addAppDataEntry(darc, dest, sarc, sappd[i]);
      }

    }

    return true; // success
  }

  private static CommandProcessor cproc;
  static {
    cproc.addCommand("new", 0, "no args");
    cproc.addCommand("add", 1,
        "Need another vamsas document archive filename as argument.");
    cproc.addCommand("repair", 0, "no args");
    cproc.addCommand("list", 0, "no args");
    cproc.addCommand("monitor", 0, "no args");
  }

  public static void main(String argv[]) {
    /**
     * TODO: switches for setting user identities for writing to vamsas document
     */
    if (argv.length < 1) {
      log.fatal("Usage : <archive to create> [(commands)]");
      return;
    }
    File newarch = new File(argv[0]);
    int argpos = 0;
    try {
      // test fully fledged doc construction
      VamsasArchive varc = new VamsasArchive(newarch, true);
      VamsasDocument docroot;
      docroot = new VamsasDocument();
      docroot.setProvenance(ProvenanceStuff.newProvenance("ArchiveWriter",
          "user", "Created new Vamsas Document"));
      while (++argpos < argv.length) {
        File archive = new File(argv[argpos]);
        InputStream istream;
        if (archive.exists()) {
          VamsasArchiveReader vdoc = new VamsasArchiveReader(archive);
          if (vdoc.isValid()) {
            istream = vdoc.getVamsasDocumentStream();
            if (istream != null) {
              VamsasDocument cdocroot = VamsasDocument
                  .unmarshal(new InputStreamReader(istream));
              if (cdocroot != null)
                mergeDocs(varc, docroot, vdoc, cdocroot);
            } else
              log
                  .warn("Unexpectedly null document stream from existing document "
                      + archive);
          } else {
            // updating an oldformat stream ?
            if ((istream = vdoc.getVamsasXmlStream()) != null) {
              // make a new vamsas document from the vamsas.xml entry
              VAMSAS root = VAMSAS.unmarshal(new InputStreamReader(istream)); // TODO:
                                                                              // verify
                                                                              // only
                                                                              // one
                                                                              // VAMSAS
                                                                              // element
                                                                              // per
                                                                              // vamsas.xml
                                                                              // entry.
              docroot.getProvenance().addEntry(
                  ProvenanceStuff.newProvenanceEntry("ArchiveWriter", "user",
                      "added vamsas.xml from " + argv[argpos - 1]));
              docroot.addVAMSAS(root);
            }
          }
        } else {
          // Begin a new vamsas document
          PrintWriter docwriter = varc.getDocumentOutputStream();
        }
      }
    } catch (Exception e) {
      log.error("Whilst manipulating " + argv[0], e);
    }
  }
}
