// $Id: InvCatalogRef.java,v 1.13 2004/09/24 03:26:27 caron Exp $
/*
 * Copyright 2002 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 *
 * This library 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 2.1 of the License, or (at
 * your option) any later version.
 *
 * This library 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 this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package thredds.catalog;

import java.net.URI;

/**
 * A reference to a InvCatalog. The referenced catalog is not read until getDatasets() is called.
 * A client will see the referenced catalog as a nested dataset.
 *
 * <p>
 * The client can also do asynchronous reading, if the InvCatalogFactory supports it,
 *  and if readAsynch() is used.
 *
 * <pre>
 * Parent relationship:
 *   ds -> catRef -- catalog
 *                      ^ top  -> ds ...
 *
 *  ParentView relationship:
 *   ds -> catRef -> top  -> ds ...
 *           (or) -> ds                 if UseProxy
 *
 * </pre>
 * @see InvDatasetImpl for API
 * @see thredds.catalog.ui.CatalogTreeView as example to read asynchronously
 *
 * @author john caron
 * @version $Revision: 1.13 $ $Date: 2004/09/24 03:26:27 $
 */

public class InvCatalogRef extends InvDatasetImpl {
  private String href;
  private InvDatasetImpl proxy = null; // top dataset of referenced catalog
  private URI uri = null;
  private String errMessage = null;

  private boolean init = false, useProxy = false;
  private boolean debug = false, debugProxy = false, debugAsynch = false;

  /**
   * Constructor.
   * @param parent : parent dataset
   * @param title : display name of collection
   * @param href : URL to another catalog
   */
  public InvCatalogRef( InvDatasetImpl parent, String title, String href) {
    super( parent, title);
    this.href = href.trim();
  }

    /** get Xlink Href, as a String, unresolved */
  public String getXlinkHref() { return href; }

    /** get Xlink reference as a URI, resolved */
  public URI getURI() {
    if (uri != null) return uri;

    // may be reletive
    try {
      return getParentCatalog().resolveUri(href);
    }
    catch (java.net.URISyntaxException e) {
      errMessage = "URISyntaxException on url  " + href + " = " + e.getMessage();
      return null;
    }
  }

    /** Get Datasets. This triggers a read of the referenced catalog the first time its called.*/
  public java.util.List getDatasets() {
    read();
    return useProxy ? proxy.getDatasets() : super.getDatasets();
  }

  /** If the referenced catalog has been read */
  public boolean isRead() { return init; }

  /** Return top dataset of referenced catalog.
   *  This triggers a read of the referenced catalog the first time its called.
   **/
  public InvDatasetImpl getProxyDataset() {
    read();
    return proxy;
  }

  // LOOK !!!!
  public String getName() { return useProxy ? proxy.getName() : super.getName(); }
  public String getFullName() { return useProxy ? proxy.getName() : super.getFullName(); }
  public String getID() { return useProxy ? proxy.getID() : super.getID(); }
  public String getAuthority() { return useProxy ? proxy.getAuthority() : super.getAuthority(); }
  public DataType getDataType() { return useProxy ? proxy.getDataType() : super.getDataType(); }
  public boolean hasAccess() { return useProxy ? proxy.hasAccess() : super.hasAccess(); }
  public boolean hasNestedDatasets() { return useProxy ? proxy.hasNestedDatasets() : true; }
  public java.util.List getDocumentation() { return useProxy ? proxy.getDocumentation() : super.getDocumentation(); }
  public java.util.List getProperties() { return useProxy ? proxy.getProperties() : super.getProperties(); }
  public java.util.List getMetadata() { return useProxy ? proxy.getMetadata() : super.getMetadata(); }

  public java.util.List getAccessLocal() { return useProxy ? proxy.getAccessLocal() : super.getAccessLocal(); }
  // public java.util.List getPropertiesLocal() { return useProxy ? proxy.getPropertiesLocal() : super.getPropertiesLocal(); }

  //////////////////////////////////////////////
  public boolean finish() {
    return super.finish( );
  }

  private synchronized void read() {
    if (init)
      return;

    URI uriResolved = getURI();
    if (uriResolved == null) {
      // this is to display an error message
      proxy = new InvDatasetImpl(null, "HREF ERROR");
      if (debug)
        System.out.println(errMessage);
      proxy.addProperty(new InvProperty("HREF ERROR", errMessage));
      datasets.add(proxy);
      init = true;
    }

    // open and read the referenced catalog XML
    try {
      if (debug)
        System.out.println(" InvCatalogRef read " + getFullName()+"  hrefResolved = " + uriResolved);

      InvCatalogFactory factory = ((InvCatalogImpl)getParentCatalog()).getCatalogFactory();
      InvCatalogImpl cat = (InvCatalogImpl) factory.readXML(uriResolved.toString());
      finishCatalog( cat);
    } catch (Exception e) {
      // this is to display an error message
      proxy = new InvDatasetImpl(null, "HREF ERROR");
      if (debug)
        System.out.println("HREF ERROR =\n  " + href + " err= " + e.getMessage());
      proxy.addProperty(new InvProperty("HREF ERROR", href));
      datasets.add(proxy);
      init = true;
      return;
    }
  }

  private void finishCatalog( InvCatalogImpl cat) {
    InvCatalogImpl parentCatalog = null;
    if (cat.hasFatalError()) {
      // this is to display an error message
      proxy = new InvDatasetImpl( null, "ERROR OPENING");
      StringBuffer out = new StringBuffer();
      cat.check( out);
      if (debug) System.out.println( "PARSE ERROR =\n  "+out.toString());
      proxy.addProperty( new InvProperty( "ERROR OPENING", out.toString()));
      proxy.finish();

    } else {
      // check for filter
      parentCatalog = (InvCatalogImpl) getParentCatalog();
      DatasetFilter filter = parentCatalog.getDatasetFilter();
      if (filter != null)
        cat.filter( filter);

      proxy = (InvDatasetImpl) cat.getDataset();
      if (proxy.getMark()) {
        proxy.setName(proxy.getName() + " (EMPTY)");
        proxy.addProperty( new InvProperty("isEmpty", "true"));
        proxy.finish();
      }

      String name = getName().trim();
      String proxyName = proxy.getName().trim();

      useProxy = proxyName.equals( name) && !(proxy instanceof InvCatalogRef);
      if (debugProxy) System.out.println("catRefname="+name+"=topName="+proxyName+"="+useProxy);
      if (useProxy) {
        for (int i=0; i<docs.size(); i++)
          proxy.addDocumentation( (InvDocumentation) docs.get(i));
        proxy.finish();

        /*List proxyData = proxy.getDatasets();
        for (int i=0; i<proxyData.size(); i++) {
          InvDatasetImpl ds = (InvDatasetImpl) proxyData.get(i);
          ds.setViewParent( this); // for treeview
        } */
      }
    }

    // make proxy the top dataset
    datasets.add( proxy);
    //proxy.setViewParent( this); // for treeview

    /* all this rigamorole to send an event when reference is read.
    if (parentCatalog != null) {
      // get topmost catalog
      InvCatalogImpl top = parentCatalog.getTopCatalog();
      cat.setTopCatalog(top);

      // may not be on the awt event thread, so need to do invokeLater
      javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          InvCatalogImpl top = ((InvCatalogImpl)getParentCatalog()).getTopCatalog();
          top.firePropertyChangeEvent(
              new java.beans.PropertyChangeEvent(top, "InvCatalogRefInit", null, this));
        }
      });
    } */

    init = true;
  }

  /**
   * Read the referenced catalog asynchronously, if the catalog factory supports it.
   * If it doesnt, this method will work equivilently to read(), which is called the first time
   *  getDatasets() is called. If the catalog is already read in, the callback will be called
   *  immediately, before this method exits.
   * @param factory : use this catalog factory
   * @param caller when catalog is read
   * @see CatalogSetCallback
   */
  public synchronized void readAsynch( InvCatalogFactory factory, CatalogSetCallback caller) {
    if (init) {
      caller.setCatalog( (InvCatalogImpl) getParentCatalog());
      return;
    }

    // may be reletive
    String hrefResolved = null;
    try {
      java.net.URI uri = getParentCatalog().resolveUri(href);
      hrefResolved = uri.toString();
    }
    catch (java.net.URISyntaxException e) {
      // this is to display an error message
      proxy = new InvDatasetImpl(null, "HREF ERROR");
      if (debug)
        System.out.println("HREF ERROR =\n  " + href + " err= " + e.getMessage());
      proxy.addProperty(new InvProperty("HREF ERROR", href));
      datasets.add(proxy);
      return;
    }

    // open and read the referenced catalog XML asynchronously
    // setCatalog will be called when ready
    InvCatalogImpl cat = null;
    try {
      if (debug) System.out.println(" InvCatalogRef readXMLasynch " + getFullName() +
        "  hrefResolved = " + hrefResolved);
      factory.readXMLasynch(hrefResolved, new Callback( caller));
    } catch (Exception e) {
      // this is to display an error message
      proxy = new InvDatasetImpl(null, "HREF ERROR");
      if (debug)
        System.out.println("HREF ERROR =\n  " + href + " err= " + e.getMessage());
      proxy.addProperty(new InvProperty("HREF ERROR", href));
      datasets.add(proxy);
      return;
    }
  }

  private class Callback implements CatalogSetCallback {
    CatalogSetCallback caller;
    Callback( CatalogSetCallback caller) {
      this.caller = caller;
    }
    public void setCatalog(InvCatalogImpl cat) {
      if (debugAsynch)
        System.out.println(" setCatalog was called");
      finishCatalog(cat);
      caller.setCatalog(cat);
    }

    public void failed() {
      if (debugAsynch)
        System.out.println(" setCatalog failed");
      caller.failed();
    }
  }

  boolean check(StringBuffer out, boolean show) {
    return isRead() ? proxy.check(out, show) : true;
  }
}

/**
 * $Log: InvCatalogRef.java,v $
 * Revision 1.13  2004/09/24 03:26:27  caron
 * merge nj22
 *
 * Revision 1.12  2004/06/04 00:51:54  caron
 * release 2.0b
 *
 * Revision 1.11  2004/05/11 23:30:27  caron
 * release 2.0a
 *
 * Revision 1.10  2004/03/19 20:12:52  caron
 * trim URLs
 *
 * Revision 1.9  2004/03/05 23:35:47  caron
 * rel 1.3.1 javadoc
 *
 * Revision 1.7  2004/02/21 02:18:49  caron
 * add java.net.Authenticator.setDefault()
 *
 * Revision 1.6  2004/02/20 02:34:47  caron
 * remove debug message
 *
 * Revision 1.5  2004/02/20 00:49:50  caron
 * 1.3 changes
 *
 * Revision 1.4  2003/12/04 22:27:44  caron
 * *** empty log message ***
 *
 * Revision 1.3  2003/05/29 21:30:47  john
 * resolve reletive URLS differently
 *
 */