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

import uk.ac.vamsas.client.IClientDocument;
import uk.ac.vamsas.objects.core.*;

/**
 * Implements a depth first traversal over the document tree calling update
 * handlers based on the Vobject.isUpdated() and Vobject.isNewInDocument() state
 * at each backtrack.
 * 
 * @author JimP
 * 
 */
public class DocumentUpdaterEngine {
  private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
      .getLog(DocumentUpdaterEngine.class);

  private IDocumentUpdater handler;

  /**
   * initialise the engine with an implementation of the interface.
   * 
   * @param hander
   */
  public DocumentUpdaterEngine(IDocumentUpdater handler) {
    super();
    this.handler = handler;
  }

  /**
   * call the necessary update handlers at each point on the VamsasDocument OM
   * TODO: later: Make this more elegant (use reflection and factor to single
   * update(Object) method) ? - we take the plodding, explicit approach rather
   * than a funky generalised one here
   */
  public void callHandlers(IClientDocument cdoc) {
    if (cdoc == null) {
      log.debug("Null IClientDocument instance.");
      return;
    }
    VAMSAS[] roots = cdoc.getVamsasRoots();
    if (roots != null) {
      for (int r = 0; r < roots.length; r++) {
        if (roots[r].isNewInDocument() || roots[r].isUpdated()) {
          if (!updateRoot(roots[r])) {
            log.debug("Calling handler(VAMSAS)");
            handler.update(roots[r]);
          }
        }
      }
    } else {
      log.debug("No Document Roots.");
    }
    // TODO: determine if the User, private or public appData has changed
    log.debug("Finished.");
  }

  private boolean updateRoot(VAMSAS vamsas) {
    boolean called = false;
    DataSet[] dset = vamsas.getDataSet();
    if (dset != null) {
      for (int ds = 0; ds < dset.length; ds++) {
        if (dset[ds].isNewInDocument() || dset[ds].isUpdated()) {
          if (!updateDataset(dset[ds])) {
            log.debug("Calling handler(Dataset)");
            handler.update(dset[ds]);
            called = true;
          }
        }
      }
    }
    return called;
  }

  private boolean updateDataset(DataSet set) {
    boolean called = false;
    // Sequences
    Sequence[] dseq = set.getSequence();
    if (dseq != null) {
      for (int s = 0; s < dseq.length; s++) {
        if (dseq[s].isNewInDocument() || dseq[s].isUpdated()) {
          if (!updateSequence(dseq[s])) {
            log.debug("Calling update(Sequence)");
            handler.update(dseq[s]);
            called = true;
          }
        }
      }
    }
    // Annotations
    DataSetAnnotations[] dann = set.getDataSetAnnotations();
    if (dann != null) {
      for (int a = 0; a < dann.length; a++) {
        if (dann[a].isNewInDocument() || dann[a].isUpdated()) {
          if (!updateDataSetAnnotation(dann[a])) {
            log.debug("Calling update(DataSetAnnotation)");
            handler.update(dann[a]);
            called = true;
          }
        }
      }
    }
    // Alignments
    Alignment[] al = set.getAlignment();
    if (al != null) {
      for (int a = 0; a < al.length; a++) {
        if (al[a].isNewInDocument() || al[a].isUpdated()) {
          if (!updateAlignment(al[a])) {
            log.debug("Calling update(Alignment)");
            handler.update(al[a]);
            called = true;
          }
        }
      }
    }
    // Trees associated with dataset sequences
    if (updateTrees(set.getTree())) {
      called = true;
    }
    return called;
  }

  private boolean updateTrees(Tree[] trees) {
    boolean called = false;
    if (trees != null) {
      for (int t = 0; t < trees.length; t++) {
        if (trees[t].isNewInDocument() || trees[t].isUpdated()) {
          if (!updateTree(trees[t])) {
            log.debug("Calling update(tree)");
            handler.update(trees[t]);
            called = true;
          }
        }
      }
    }
    return called;
  }

  private boolean updateDataSetAnnotation(DataSetAnnotations annotations) {
    boolean called = false;
    return called;
  }

  private boolean updateTree(Tree tree) {
    // TODO: if (
    return false;
  }

  private boolean updateAlignment(Alignment alignment) {
    // TODO Auto-generated method stub
    return false;
  }

  private boolean updateSequence(Sequence sequence) {
    // TODO Auto-generated method stub
    return false;
  }
}
