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

import java.util.Vector;

import uk.ac.vamsas.client.ClientHandle;
import uk.ac.vamsas.client.UserHandle;
import uk.ac.vamsas.client.simpleclient.VamsasArchive;
import uk.ac.vamsas.client.simpleclient.VamsasArchiveReader;
import uk.ac.vamsas.objects.core.*;

/**
 * Form, accessors and validation for ApplicationData references in vamsas
 * document. TODO: LATER:extend XML Schema to properly validate against the same
 * forms required by this class TODO: VAMSAS: URNS for appDatas are supposed to
 * be unique, aren't they ?
 */
public class AppDataReference {
  /**
   * search interface for collecting particular types of AppDatas in a vamsas
   * document
   * 
   * @author jimp
   * 
   */
  interface IAppDSearch {
    /**
     * process the appData Vobject d
     * 
     * @param d
     * @return true if appData should be collected
     */
    public boolean process(AppData d);
  }

  /**
   * collect all appData reference strings in a vamsas document
   * 
   * @param doc
   * @return vector of String objects
   */
  static public Vector getAppDataReferences(VamsasDocument doc) {
    if ((doc != null) && (doc.getApplicationDataCount() > 0)) {
      Vector apdrefs = new Vector();
      ApplicationData[] appdatas = doc.getApplicationData();
      for (int q = 0; q < appdatas.length; q++) {
        String refstring = appdatas[q].getDataReference();
        if (refstring != null)
          apdrefs.add(refstring);
        User users[] = appdatas[q].getUser();

        if (users != null)
          for (int u = 0; u < users.length; u++) {
            refstring = users[u].getDataReference();
            if (refstring != null)
              apdrefs.add(new String(refstring)); // avoid referencing.
          }
      }
      if (apdrefs.size() > 0)
        return apdrefs;
    }
    return null;
  }

  /**
   * General search through the set of AppData objects for a particular profile
   * of Client and User handle.
   * 
   * @param doc
   * @param test
   *          interface implemented by the filter selecting particular AppDatas.
   * @param cascade
   *          if true only User objects for ApplicationData objects that
   *          test.process returned true will be tested.
   * @return set of uk.ac.vamsas.objects.core.AppData objects for which
   *         test.process returned true
   */
  static public Vector searchAppDatas(VamsasDocument doc, IAppDSearch test,
      boolean cascade) {
    if ((doc != null) && (doc.getApplicationDataCount() > 0)) {
      Vector apdrefs = new Vector();
      ApplicationData[] appdatas = doc.getApplicationData();
      for (int q = 0; q < appdatas.length; q++) {
        boolean t;
        if (t = test.process(appdatas[q]))
          apdrefs.add(appdatas[q]);
        if (t || cascade) {
          User users[] = appdatas[q].getUser();
          if (users != null)
            for (int u = 0; u < users.length; u++)
              if (test.process(users[u]))
                apdrefs.add(users[u]);
        }
      }
      if (apdrefs.size() > 0)
        return apdrefs;
    }
    return null;
  }

  static public boolean equals(User p, UserHandle u) {
    if (p.getFullname().equals(u.getFullName())
        && p.getOrganization().equals(u.getOrganization()))
      return true;
    return false;
  }

  /**
   * returns true if Name matches in c and p, and Urn's match (or
   * c.getUrn()==null) and Version's match (or c.getVersion()==null)
   * 
   * @param p
   * @param c
   * @return match of p on template c.
   */
  static public boolean equals(ApplicationData p, ClientHandle c) {
    if (
    // ((c.getClientUrn()==null) || p.getUrn().equals(c.getClientUrn()))
    // &&
    (p.getName().equals(c.getClientName()))
        && ((c.getVersion() == null) || (p.getVersion().equals(c.getVersion()))))
      return true;
    return false;
  }

  /**
   * Searches document appData structure for particular combinations of client
   * and user data
   * 
   * @param doc
   *          the data
   * @param user
   *          template user data to match against
   * @see AppDataReference.equals(uk.ac.vamsas.objects.core.User,
   *      uk.ac.vamsas.client.UserHandle)
   * @param app
   * @see AppDataReference.equals(uk.ac.vamsas.objects.core.ApplicationData,
   *      uk.ac.vamsas.client.ClientHandle)
   * @return set of matching client app datas for this client and user
   *         combination
   */
  static public Vector getUserandApplicationsData(VamsasDocument doc,
      UserHandle user, ClientHandle app) {
    if (doc == null) {
      return null;
    }
    final UserHandle u = user;
    final ClientHandle c = app;

    IAppDSearch match = new IAppDSearch() {
      public boolean process(AppData p) {
        if (p instanceof User) {
          if (AppDataReference.equals((User) p, u))
            return true;
        } else if (p instanceof ApplicationData) {
          if (AppDataReference.equals((ApplicationData) p, c))
            return true;
        }
        return false;
      }
    };

    return searchAppDatas(doc, match, true); // only return AppDatas belonging
                                             // to appdata app.
  }

  /**
   * safely creates a new appData reference
   * 
   * @param dest
   *          destination document Vobject
   * @param entry
   *          base application reference to make unique
   */
  public static String uniqueAppDataReference(VamsasDocument dest, String base) {
    String urn = base.replace('/', '_').replace('\\', '_').replace(':', '_')
        .replace('.', '_');
    int v = 1;
    for (int i = 0, j = dest.getApplicationDataCount(); i < j; i++) {
      ApplicationData o = dest.getApplicationData()[i];
      // ensure new urn is really unique
      while (o.getDataReference() != null && o.getDataReference().equals(urn)) {
        urn = base + "_" + v++;
      }
    }
    return urn;
  }
}
