/*
 * 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.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;

import uk.ac.vamsas.client.ClientDocument;
import uk.ac.vamsas.client.Vobject;
import uk.ac.vamsas.client.simpleclient.VamsasArchiveReader;
import uk.ac.vamsas.objects.core.Alignment;
import uk.ac.vamsas.objects.core.AppData;
import uk.ac.vamsas.objects.core.ApplicationData;
import uk.ac.vamsas.objects.core.DataSet;
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.Tree;
import uk.ac.vamsas.objects.core.User;
import uk.ac.vamsas.objects.core.VAMSAS;
import uk.ac.vamsas.objects.core.VamsasDocument;

/**
 * this class contains static methods for writing info to stdout about a vamsas
 * document Methods have a 'cascade' switch to indicate if sub-objects should
 * have info printed on them. Methods return true or false - indicating if the
 * Vobject was valid or not TODO: LATER: propagate true/false return so that an
 * invalid vamsas Vobject invalidates the whole document
 * 
 * @author jimp
 * 
 */
public class ArchiveReports {
  /**
   * print an informative summary on a VamsasDocument
   * 
   * @param outstr
   *          TODO
   * @param document
   *          - the document itself
   * @param archive
   *          - document source archive for resolving any appData refs
   * @return
   */
  public static boolean reportProvenance(Provenance p, PrintStream outstr) {
    if (p == null) {
      outstr.println("No Provenance");
      return false;
    }
    Entry[] pe = p.getEntry();
    for (int i = 0; i < pe.length; i++) {
      outstr.print(pe[i].getDate() + "\t'" + pe[i].getUser() + "'\t"
          + pe[i].getApp() + "\t'" + pe[i].getAction() + "' ");
      outputVobjectState(pe[i], outstr);
    }
    return true;
  }

  public static boolean appDataEntryReport(AppData appD,
      VamsasArchiveReader archive, boolean cascade, PrintStream outstr) {
    if (appD != null) {
      boolean nulldata = false;
      if (appD.getDataReference() != null) {
        String appData = appD.getDataReference();
        if (appData == null) {
          outstr.println("Empty DataReference - not valid ?");
        } else if (appData.length() > 1) {
          outstr.print("a reference (" + appData + ")");
          InputStream jstrm;
          if ((jstrm = archive.getAppdataStream(appData)) != null)
            outstr.println(" which resolves to a JarEntry.");
          else {
            outstr.println(" which does not resolve to a JarEntry.");
            outstr.println("Unresolved appdata reference '" + appData + "'");
          }
        } else {
          nulldata = true;
        }
      } else {
        if (appD.getData() == null)
          nulldata &= true;
        else
          outstr.println("an embedded chunk of " + appD.getData().length
              + " bytes.");
      }
      if (nulldata)
        outstr.println("Null AppData reference/data chunk.");
    }
    return true;
  }

  public static boolean appDataReport(ApplicationData appD,
      VamsasArchiveReader archive, boolean cascade, PrintStream outstr) {
    if (appD != null) {
      // Report on root appData
      appDataEntryReport(appD, archive, cascade, outstr);
      if (appD.getInstanceCount() > 0) {
        Instance inst[] = appD.getInstance();
        for (int i = 0, j = inst.hashCode(); i < j; i++) {
          outstr.println("Data for App Instance URN: '" + inst[i].getUrn());
          appDataEntryReport(inst[i], archive, cascade, outstr);
        }
      }
      if (appD.getUserCount() > 0) {
        User users[] = appD.getUser();
        for (int i = 0, j = users.length; i < j; i++) {
          outstr.println("Data for User '" + users[i].getFullname() + "' of '"
              + users[i].getOrganization() + "'");
          appDataEntryReport(users[i], archive, cascade, outstr);
        }
      }
    }

    return true;
  }

  public static boolean reportDocument(VamsasDocument document,
      VamsasArchiveReader archive, boolean cascade, PrintStream outstr) {
    if (document != null) {
      outstr.println("Vamsas Document version '" + document.getVersion() + "'");
      reportProvenance(document.getProvenance(), outstr);
      outstr.print("Document contains " + document.getVAMSASCount()
          + " VAMSAS Elements and " + document.getApplicationDataCount()
          + " Application data elements.\n");
      if (document.getVAMSASCount() > 0 && cascade)
        rootReport(document.getVAMSAS(), true, outstr);
      if (document.getApplicationDataCount() > 0) {
        outstr.print("There are " + document.getApplicationDataCount()
            + " ApplicationData references.\n");
        ApplicationData appd[] = document.getApplicationData();
        for (int i = 0, j = appd.length; i < j; i++) {
          outstr.print("Application " + i + ": '" + appd[i].getName()
              + "'\nVersion '" + appd[i].getVersion() + "'\n");
          outstr.print("AppData is :");
          appDataReport(appd[i], archive, cascade, outstr);
        }

      }
      return true;
    } else {
      outstr.println("Document Object is null");
    }
    return false;
  }

  /**
   * summarises all the datasets in a vamsas document.
   * 
   * @param roots
   * @param cascade
   *          TODO
   * @param outstr
   *          TODO
   * @return
   */
  public static boolean rootReport(VAMSAS[] roots, boolean cascade,
      PrintStream outstr) {
    if (roots != null) {
      for (int i = 0; i < roots.length; i++) {
        VAMSAS r = roots[i];
        int ds, tr;
        outstr.print("Vamsas Root " + i + " (id="
            + ((r.getId() != null) ? r.getId() : "<none>") + ") contains "
            + (ds = r.getDataSetCount()) + " DataSets, "
            + (tr = r.getTreeCount()) + " Global trees\n");
        outputVobjectState(r, outstr);
        if (cascade) {
          for (int j = 0; j < ds; j++) {
            outstr.println("Dataset " + j);
            cascade = datasetReport(r.getDataSet(j), true, outstr) && cascade;
          }
          for (int j = 0; j < tr; j++) {
            outstr.println("Global tree " + j);
            cascade = treeReport(r.getTree(j), true, outstr) && cascade;
          }
        }
      }
      return true;
    }
    return false;
  }

  public static void outputVobjectState(Vobject v, PrintStream outstr) {
    outstr.print(" (Object is: ");
    boolean comma = false;
    if (v.is__stored_in_document()) {
      outstr.print(" stored");
      comma = true;
    }
    if (v.isNewInDocument()) {
      if (comma)
        outstr.print(",");
      comma = true;
      outstr.print(" new in document");
    }
    if (v.isUpdated()) {
      if (comma)
        outstr.print(",");
      comma = true;
      outstr.print(" updated since last read");
    }
    outstr.println(")");
  }

  public static boolean datasetReport(DataSet ds, boolean cascade,
      PrintStream outstr) {
    if (cascade)
      reportProvenance(ds.getProvenance(), outstr);
    outputVobjectState(ds, outstr);
    outstr.println("Dataset contains : " + ds.getSequenceCount()
        + " sequences, " + ds.getAlignmentCount() + " alignments and "
        + ds.getTreeCount() + " trees.");
    if (cascade)
      alignmentReport(ds.getAlignment(), true, outstr);
    return true;
  }

  public static boolean alignmentReport(Alignment[] al, boolean cascade,
      PrintStream outstr) {
    boolean val = true;
    if (al != null && al.length > 0) {
      for (int i = 0; i < al.length; i++) {
        outstr.println("Alignment "
            + i
            + (al[i].isRegistered() ? " (" + al[i].getVorbaId() + ")"
                : " (unregistered)"));
        outputVobjectState(al[i], outstr);
        if (cascade)
          reportProvenance(al[i].getProvenance(), outstr);
        outstr.println("Involves " + al[i].getAlignmentSequenceCount()
            + " sequences, has " + al[i].getAlignmentAnnotationCount()
            + " annotations and " + al[i].getTreeCount() + " trees.");
        if (cascade) {
          for (int t = 0; t < al[i].getTreeCount(); t++)
            treeReport(al[i].getTree(t), true, outstr);
        }
      }
    }
    return val;
  }

  public static boolean treeReport(Tree t, boolean cascade, PrintStream outstr) {
    outstr.print("Tree: '" + t.getTitle() + "' ");
    outputVobjectState(t, outstr);
    return !cascade || reportProvenance(t.getProvenance(), outstr);
  }

}
