/*
 * 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.simpleapp;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.Vector;
import java.util.jar.JarOutputStream;

import javax.swing.JInternalFrame;

import uk.ac.vamsas.client.UserHandle;
import uk.ac.vamsas.client.simpleclient.FileWatcher;
import uk.ac.vamsas.client.simpleclient.VamsasArchive;
import uk.ac.vamsas.client.simpleclient.VamsasFile;
import uk.ac.vamsas.objects.core.Entry;
import uk.ac.vamsas.objects.core.VamsasDocument;
import uk.ac.vamsas.test.simpleclient.ArchiveClient;
import uk.ac.vamsas.test.simpleclient.ClientDoc;

/**
 * @author jimp
 * 
 */
public class VamsasClient extends ArchiveClient {
  org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
      .getLog(VamsasClient.class);

  /**
   * create a new vamsas client session from the archive at sessionPath.
   * 
   * @param sessionPath
   */
  public VamsasClient(File sessionPath) {
    super(System.getProperty("user.name"), System.getProperty("host.name"),
        "SimpleVamsasClientApp", "0.1", sessionPath);
  }

  /**
   * Called by gui to read anything from the vamsas session into the apps
   * datamodel after it has started up.
   * 
   */
  public void initial_update() {
    log.info("Jalview loading the Vamsas Session.");
    // load in the vamsas archive for the first time
    ClientDoc cdoc = this.getUpdateable();
    updateJalview(cdoc);
    // TODO: flush any new VorbaIds to the document : updateVamsasClient may
    // actually generate new Vamsas Document data in the form of vamsas element
    // ids - these should be written back to the document.
    // doUpdate(cdoc); // JBPNote: this should flush new VorbaIds but I've not
    // tested it yet.
    cdoc.closeDoc();
    // then tell app to update its display based on the datamodel changes.
  }

  VamsasClientWatcher watcher = null;

  /**
   * Called by app when internal datamodel should exported (syncrhonised
   * outwards) to vamsas document
   * 
   */
  public void push_update() {

    watchForChange = false; // this makes any watch(long) loops return.
    // we should also wait arount for this.WATCH_SLEEP to really make sure the
    // watcher thread has stopped.
    try {
      Thread.sleep(WATCH_SLEEP);
    } catch (Exception e) {

    }
    ;

    ClientDoc cdoc = getUpdateable();
    updateVamsasDocument(cdoc);
    doUpdate(cdoc);
    cdoc.closeDoc();
    cdoc = null;
    watchForChange = true;
    startWatcher();
  }

  public void end_session() {
    watchForChange = false; // this makes any watch(long) loops return.
    // we should also wait arount for this.WATCH_SLEEP to really make sure the
    // watcher thread has stopped.
    try {
      Thread.sleep(WATCH_SLEEP);
    } catch (Exception e) {

    }
    ;

    // stop any update/watcher thread.
    log.info("VamsasClientApplication disconnecting from the Vamsas Session.");
  }

  public void updateJalview(ClientDoc cdoc) {
    ensureVamsasBindings();
    VamsasDatastore vds = new VamsasDatastore(cdoc, vobj2jv, jv2vobj,
        baseProvEntry());
    vds.updateToJalview();
  }

  private void ensureVamsasBindings() {
    if (jv2vobj == null) {
      jv2vobj = new IdentityHashMap();
      vobj2jv = new Hashtable();
    }
  }

  /**
   * App's object binding to VorbaIds
   */
  IdentityHashMap jv2vobj = null;

  Hashtable vobj2jv = null;

  /**
   * called with a vamsas document which will be updated with new data from the
   * app
   * 
   * @param doc
   */
  public void updateVamsasDocument(ClientDoc doc) {
    ensureVamsasBindings();
    VamsasDatastore vds = new VamsasDatastore(doc, vobj2jv, jv2vobj,
        baseProvEntry());
    // wander through frames
    vds.storeVAMSAS(new Object()); // Object is the apps datamodel ;)
  }

  /**
   * 
   * @return a base provenance entry used by the VamsasDatastore object to get
   *         attributes from. this isn't particularly elegant either.
   */
  private Entry baseProvEntry() {
    uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
    pentry.setUser(this.getProvenanceUser());
    pentry.setApp(this.getClientHandle().getClientName());
    pentry.setDate(new java.util.Date());
    pentry.setAction("created");
    return pentry;
  }

  protected class VamsasClientWatcher extends Thread implements Runnable {
    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Thread#run()
     */
    VamsasClient client = null;

    VamsasClientWatcher(VamsasClient client) {
      this.client = client;
    }

    boolean running = false;

    public void run() {
      running = true;
      while (client.watchForChange) {
        ClientDoc docio = client.watch(0);
        if (docio != null) {
          // VamsasClient GUI bits should be disabled whilst an update is in
          // progress so the user doesn't screw anything up.
          client.disableGui(true);
          log.debug("Updating VamsasClient app from changed vamsas document.");
          client.updateJalview(docio);
          log.debug("Finished updating from document change.");
          docio.closeDoc();
          docio = null;
          client.disableGui(false);
        }
      }
      running = false;

    }

  }

  /**
   * @param args
   */
  public static void main(String[] args) {
    // TODO Auto-generated method stub

  }

  /**
   * disable (if b is true) or enable (if b is true) the VamsasClient's vamsas
   * session gui bits whilst a document change is being updated to the app.
   * 
   * @param b
   */
  public void disableGui(boolean b) {
    // in jalview, we turn off the VAMSAS Session menu :
    // Desktop.instance.setVamsasUpdate(b);
  }

  /**
   * spawn a new thread to start the VamsasClientWatcher.
   * 
   */
  public void startWatcher() {
    if (watcher == null)
      watcher = new VamsasClientWatcher(this);
    Thread thr = new Thread() {
      public void run() {
        watcher.start();
      }
    };
    thr.start();
  }

}
