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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.ac.vamsas.client.ClientHandle;
import uk.ac.vamsas.client.SessionHandle;
import uk.ac.vamsas.client.UserHandle;
import uk.ac.vamsas.client.Vobject;
import uk.ac.vamsas.client.VorbaId;
import uk.ac.vamsas.client.VorbaIdFactory;
import uk.ac.vamsas.objects.utils.document.VersionEntries;

import java.util.Hashtable;
import java.util.zip.CRC32;

/**
 * Simplest VorbaId constructor
 * 
 * @author jimp
 * 
 */
public class IdFactory extends VorbaIdFactory {
  static Log log = LogFactory.getLog(IdFactory.class);

  private SessionHandle session = null;

  private ClientHandle client;

  private UserHandle user;

  private CRC32 unique = new CRC32(); // used to attempt a unique but
                                      // predictable stream for IDs

  private String idstring;

  int sequence = 1; // incrementing value for next new ID

  /**
   * 
   */
  public IdFactory() {
    super();
    // TODO Auto-generated constructor stub
  }

  /**
   * @param session
   * @param client
   * @param user
   */
  protected IdFactory(SessionHandle session, ClientHandle client,
      UserHandle user) {
    super();
    this.session = session;
    this.client = client;
    this.user = user;
    unique.reset();
    unique.update(new Object[] { session, client, user }.toString().getBytes());
    // TODO: Ensure format of URNs and use standard composition methods.
    idstring = client.getClientNCname() + "_" + unique.getValue() + ".";
    extantids = new Hashtable();
    this.extanthashv = new Hashtable();
  }

  /**
   * Create IdFactory with existing object hashes and id set
   * 
   * @param session
   * @param client
   * @param user
   * @param extanthashv
   *          hash of existing VorbaIds from a previous read of same document
   */
  protected IdFactory(SessionHandle session, ClientHandle client,
      UserHandle user, Hashtable extanthashv) {
    this(session, client, user);
    this.extanthashv = extanthashv;
  }

  /**
   * values for keys in this hash can be used to reference the
   * uk.ac.vamsas.client.Vobject instance for the VorbaId string.
   * 
   * @return the hash of all VorbaIds
   */
  protected Hashtable getVorbaIdHash() {
    return extantids;
  }

  /**
   * values for keys in this hash are Vobjhash objects created for each Vobj
   * with a VorbaId after this factory has been used to write a vamsas archive.
   * 
   * @return the hash of all VorbaIds and their hash values.
   */
  protected Hashtable getVobjhashVals() {
    return extanthashv;
  }

  /*
   * (non-Javadoc)
   * 
   * @see uk.ac.vamsas.client.VorbaIdFactory#makeVorbaId()
   */
  public VorbaId makeVorbaId(Vobject vobject) {
    if (session == null)
      throw new Error(
          "makeVorbaId called on improperly initialised IdFactory Vobject!");
    if (!vobject.isRegisterable())
      throw new Error("makeVorbaId called on unregisterable object.");
    if (vobject.isRegistered())
      throw new Error("makeVorbaId called on already registered object.");
    String newidstring;
    do {
      if (sequence > 0) {
        sequence++;
      } else {
        idstring += "1/";
        sequence = 1;
      }
      newidstring = idstring + Integer.toString(sequence);
    } while (extantids.containsKey(newidstring));
    VorbaId id = newId(newidstring); // VorbaId.hash()==newidstring.hash() so we
                                     // can still recover vobject
    extantids.put(id, vobject); // hash the Vobject by its new Id
    return id;
  }

  /*
   * (non-Javadoc)
   * 
   * @see
   * uk.ac.vamsas.client.VorbaIdFactory#setSession(uk.ac.vamsas.client.SessionHandle
   * )
   */
  protected void setSession(SessionHandle sessionhandle) {
    if (sessionhandle != null)
      session = sessionhandle;
    else
      log.warn("setSession(null) called.");
  }

  /*
   * (non-Javadoc)
   * 
   * @see uk.ac.vamsas.client.VorbaIdFactory#getSessionHandle()
   */
  public SessionHandle getSessionHandle() {
    return session;
  }

  /*
   * (non-Javadoc)
   * 
   * @see
   * uk.ac.vamsas.client.VorbaIdFactory#setClient(uk.ac.vamsas.client.ClientHandle
   * )
   */
  protected void setClient(ClientHandle appHandle) {
    if (appHandle != null)
      client = appHandle;
    else
      log.warn("setClient(null) called.");
  }

  /*
   * (non-Javadoc)
   * 
   * @see uk.ac.vamsas.client.VorbaIdFactory#getClientHandle()
   */
  public ClientHandle getClientHandle() {
    return client;
  }

  /*
   * (non-Javadoc)
   * 
   * @see
   * uk.ac.vamsas.client.VorbaIdFactory#setUser(uk.ac.vamsas.client.UserHandle)
   */
  protected void setUser(UserHandle userHandle) {
    if (userHandle != null)
      user = userHandle;
    else
      log.warn("setUser(null) called.");
  }

  /*
   * (non-Javadoc)
   * 
   * @see uk.ac.vamsas.client.VorbaIdFactory#getUserHandle()
   */
  public UserHandle getUserHandle() {
    return user;
  }

  /**
   * Convenience method used for default behaviour in testing and any anonymous
   * internal vamsasDocument unmarshalling
   * 
   * @param clientname
   * @return
   */
  protected static IdFactory getDummyFactory(String clientname) {
    if (clientname == null)
      clientname = "uk.ac.vamsas.client.simpleclient.IdFactory";
    return new IdFactory(new SessionHandle("dummy.session"), new ClientHandle(
        clientname, VersionEntries.latestVersion()), new UserHandle(clientname,
        "Arnold User's Inc."));
  }
}
