// $Id: Structure.java,v 1.12 2004/12/10 17:04:16 caron Exp $
/*
 * Copyright 1997-2004 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, strlenwrite to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package ucar.nc2;

import ucar.ma2.*;

import java.util.*;
import java.io.IOException; 

/**
 * A Structure is a type of Variable that contains other Variables, analogous to a struct in C.
 *
 * A call to structure.read() will read all of the data in a Structure,
 *   including nested structures, and returns an Array of StructureData, with all of the data in memory.
 *
 * Generally, the programmer can assume that the data in a Structure are stored together,
 * so that it is efficient to read a Structure all at once, then access the variable data through the
 * Arrays in the StructureData.
 *
 * @author caron
 * @version $Revision: 1.12 $ $Date: 2004/12/10 17:04:16 $
 */

public class Structure extends Variable {
  protected ArrayList members = new ArrayList(); // Variable
  protected ArrayList memberNames = new ArrayList(); // String

  /** Constructor */
  public Structure( NetcdfFile ncfile, Group group, Structure parent, String shortName) {
    super (ncfile, group, parent, shortName);
    this.dataType = DataType.STRUCTURE;
    this.elementSize = -1; // gotta wait before calculating
  }

  /** copy constructor;
   * @param from  copy from this
   * @param reparent : if true, reparent the members. if so, cant use 'from' anymore
   */
  protected Structure( Structure from, boolean reparent) {
    super( from);

    members = new ArrayList(from.members);
    memberNames = new ArrayList(from.memberNames);

    if (reparent) {
      for (int i=0; i<members.size(); i++) {
        Variable v = (Variable) members.get(i);
        v.setParentStructure( this);
      }
    }
  }

  /** Override so it returns a Structure */
  public Variable section(List section) throws InvalidRangeException  {
    Variable vs = new Structure( this, false);  // LOOK ??
    makeSection( vs, section);
    return vs;
  }

  /** Add a member variable */
  public void addMemberVariable( Variable v) {
    members.add( v);
    memberNames.add( v.getShortName());
    v.setParentStructure( this);
  }

  /** Set the list of member variables. */
  public void setMemberVariables( ArrayList vars) {
    members = vars;
  }

  /** Remove a Variable : uses the variable hashCode to find it.
   * @return true if was found and removed */
  public boolean removeMemberVariable( Variable v) {
    if (v == null) return false;
    memberNames.remove( v.getShortName());
    return members.remove( v);
  }

  /** Set the parent group of this Structure, and all member variables. */
  public void setParentGroup(Group group) {
    super.setParentGroup(group);
    for (int i=0; i<members.size(); i++) {
      Variable v = (Variable) members.get(i);
      v.setParentGroup( group);
    }
 }

  /** Get the variables contained directly in this Structure.
   * @return List of type Variable.
   */
  public java.util.List getVariables() { return members; }

  /** Get the (short) names of the variables contained directly in this Structure.
   * @return List of type String.
   */
  public java.util.List getVariableNames() { return memberNames; }

  /**
   * Find the Variable member with the specified (short) name, or null if not found.
   */
  public Variable findVariable(String shortName) {
    if (shortName == null) return null;
    for (int i=0; i<members.size(); i++) {
      Variable v = (Variable) members.get(i);
      if (shortName.equals(v.getShortName()))
        return v;
    }
    return null;
  }

  /* public Variable findNestedVariable(String[] names) {
    return findNestedVariable( Arrays.asList( names).iterator());
  }

  public Variable findNestedVariable(java.util.Iterator names) {
    if (!names.hasNext()) return null;
    String name = (String) names.next();
    Variable nested = findVariable( name);
    if (nested == null) return null;
    if (!names.hasNext()) return nested;
    if (nested.getDataType() != DataType.STRUCTURE) return null;
    Structure s = (Structure) nested;
    return s.findNestedVariable( names);
  } */

  public int getElementSize() {
    if (elementSize == -1) calcElementSize();
    return elementSize;
  }

  protected void calcElementSize() {
    int total = 0;
    for (int i=0; i<members.size(); i++) {
      Variable v = (Variable) members.get(i);
      total += v.getElementSize() * v.getSize();
    }
    elementSize = total;
  }

  /**
   * Use this when this is a scalar Structure. Its the same as read(), but it extracts the single
   * StructureData out of the Array.
   */
  public StructureData readStructure() throws IOException {
    if (getRank() != 0) throw new java.lang.UnsupportedOperationException("not a scalar structure");
    Array data = (cache.data != null) ? cache.data : read();
    return (StructureData) data.getObject(scalarIndex);
  }

  /**
   * Use this when this is a one dimensional array of Structures. This will read only the ith structure,
   * and return the data as a StructureData object.
   * @return ith StructureData
   */
  public StructureData readStructure(int index) throws IOException, ucar.ma2.InvalidRangeException {
    if (getRank() != 1) throw new java.lang.UnsupportedOperationException("not a vector structure");
    int[] origin = new int[] {index};
    int[] shape = new int[] {1};
    Array data = read(origin, shape);
    return (StructureData) data.getObject(scalarIndex);
  }

  /** Iterator over all the Structure; use for array of Structures.
   *  @return Iterator over type StructureData
   */
  public Structure.Iterator getStructureIterator() {
    return new Structure.Iterator();
  }

  public class Iterator {
    private int count = 0;
    private int recnum = (int) getSize();

    public boolean hasNext() { return count < recnum; }
    public StructureData next() throws IOException { // LOOK NOT CORRECT only for 1D
      try {
        return readStructure( count++);
      } catch (InvalidRangeException e) { return null; } // can happen if hasNext is ignored
    }
  }

  /*
   * Get the value as a double for a scalar member Variable.
   * @param name name of scalar member Variable.
   * @throws IllegalArgumentException if name is not a member variable.
   * @throws IOException if theres an IO Error
   * @throws UnsupportedOperationException if not a scalar Variable
   * @throws ClassCastExeption if data type not convertible to double
   *
  public double readScalarDouble(String name) throws IOException  {
    Variable v = findVariable(name);
    if (v == null) throw new IllegalArgumentException("no member named "+name);
    return v.readScalarDouble();
  } */

  ////////////////////////////////////////////

  /** Get String with name and attributes. Used in short descriptions like tooltips. */
  public String getNameAndAttributes() {
    StringBuffer sbuff = new StringBuffer();
    sbuff.append("Structure ");
    getNameAndDimensions(sbuff, false, false);
    sbuff.append("\n");
    for (int i=0; i<attributes.size(); i++) {
      Attribute att = (Attribute) attributes.get(i);
      sbuff.append("  "+getShortName()+":");
      sbuff.append(att.toString());
      sbuff.append(";");
      sbuff.append("\n");
    }
    return sbuff.toString();
  }

  /** String representation of Structure and nested variables. */
  public String toString() {
    return writeCDL("   ", false, false);
  }

  public String writeCDL(String space, boolean useFullName, boolean strict) {
    StringBuffer buf = new StringBuffer();
    buf.setLength(0);
    //buf.append("\n");
    buf.append(space);
    buf.append(dataType.toString());
    buf.append(" {\n");

    String nestedSpace = "  "+space;
    for (int i=0; i<members.size(); i++) {
      Variable v = (Variable) members.get(i);
      buf.append(v.writeCDL(nestedSpace, useFullName, strict));
    }

    buf.append(space);
    buf.append("} ");
    getNameAndDimensions(buf, useFullName, strict);
    buf.append(";");
    buf.append(extraInfo());
    buf.append("\n");

    java.util.Iterator iter = getAttributes().iterator();
    while (iter.hasNext()) {
      buf.append(nestedSpace);
      if (strict) buf.append(getShortName());
      buf.append("  :");
      Attribute att = (Attribute) iter.next();
      buf.append(att.toString());
      buf.append(";");
      if (!strict && (att.getDataType() != DataType.STRING)) buf.append(" // "+att.getDataType());
      buf.append("\n");
    }

    return buf.toString();
  }

}

/* Change History:
   $Log: Structure.java,v $
   Revision 1.12  2004/12/10 17:04:16  caron
   *** empty log message ***

   Revision 1.11  2004/12/01 05:53:44  caron
   ncml pass 2, new convention parsing

   Revision 1.10  2004/10/29 00:14:13  caron
   no message

   Revision 1.9  2004/10/13 19:45:13  caron
   add strict NCDump

   Revision 1.8  2004/10/06 19:03:44  caron
   clean up javadoc
   change useV3 -> useRecordsAsStructure
   remove id, title, from NetcdfFile constructors
   add "in memory" NetcdfFile

   Revision 1.7  2004/08/19 21:38:13  caron
   no message

   Revision 1.6  2004/08/19 19:27:28  edavis
   Make methods for building a Structure public.

   Revision 1.5  2004/08/17 19:20:05  caron
   2.2 alpha (2)

   Revision 1.4  2004/08/16 20:53:46  caron
   2.2 alpha (2)

   Revision 1.3  2004/07/12 23:40:17  caron
   2.2 alpha 1.0 checkin

   Revision 1.2  2004/07/06 19:28:11  caron
   pre-alpha checkin

   Revision 1.1.1.1  2003/12/04 21:05:27  caron
   checkin 2.2

 */
