// $Id: SimpleUnit.java,v 1.1 2004/09/11 00:04:22 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, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package ucar.nc2.units;

import ucar.units.*;

/**
 * Convenience routines on top of ucar.units package.
 *
 * The ucar.units package handles <ol>
 *  <li> scientific units, which are factors of the fundamental
 *  dimensions such as length, time, mass, etc
 *  <li> dates, represented as "n units of time since reference date" eg
 *  "1203 days since 1970-01-01 00:00:00"
 * </ol>
 *
 *
 *
 * @author caron
 * @version $Revision: 1.1 $ $Date: 2004/09/11 00:04:22 $
 */

public class SimpleUnit {
  static protected UnitFormat format;
  static protected Unit timeUnit, dateUnit;
  static protected boolean debugParse = false;

  static {
    try {
      format = UnitFormatManager.instance();
      timeUnit = format.parse("1 sec");
      dateUnit = format.parse("secs since 1970-01-01 00:00:00");

      // aliasing
      UnitDB unitDB = UnitDBManager.instance();
      Unit u = format.parse("millibar");
      Unit alias = u.clone( UnitName.newUnitName("mb"));
      unitDB.addUnit( alias);

    } catch (Exception e) {
      System.out.println("SimpleUnit initialization failed " +e);
      throw new RuntimeException("SimpleUnit initialization failed " +e);
    }
  }

  /**
   * Create a SimpleUnit from the given name, catch Exceptions.
   *
   * @param name parse this name to create a unit.
   * @return SimpleUnit, DateUnit, TimeUnit, or null if failed
   * @see ucar.units.UnitFormat#parse
   */
   static public SimpleUnit factory( String name) {
    try {
      return factoryWithExceptions(name);
    } catch (Exception e) {
      if (debugParse) System.out.println("Parse " +name +" got Exception " +e);
      return null;
    }
  }

  /**
   * Create a SimpleUnit from the given name, allow Exceptions.
   *
   * @param name parse this name to create a unit.
   * @return SimpleUnit, DateUnit, or TimeUnit
   * @throws Exception
   * @see ucar.units.UnitFormat#parse
   */
  static public SimpleUnit factoryWithExceptions( String name) throws Exception {
      Unit uu = format.parse(name);
      if (isDateUnit( uu)) return new DateUnit( uu);
      if (isTimeUnit( uu)) return new TimeUnit( name, uu);
      return new SimpleUnit(uu);
  }

  /* dorky normalizarion
  static public SimpleUnit factoryNWithExceptions( String name) throws Exception {
    return factoryWithExceptions( normalize(name));
  }

  static public String normalize( String units) {
    units = StringUtil.substitute( units, "**", "^");
    if (units.equals("fraction")) units="";
    return units;
  } */

  /** Return true if unitString1 is compatible to unitString2,
   *  meaning one can be converted to the other.
   * If either unit string is illegal, return false.
   **/
  public static boolean isCompatible(String unitString1, String unitString2) {
    Unit uu1, uu2;
    try {
      uu1 = format.parse(unitString1);
    } catch (Exception e) {
      if (true || debugParse) System.out.println("Parse " +unitString1 +" got Exception1 " +e);
      return false;
    }

    try {
      uu2 = format.parse(unitString2);
    } catch (Exception e) {
      if (true || debugParse) System.out.println("Parse " +unitString2 +" got Exception2 " +e);
      return false;
    }

    //System.out.println("udunits isCompatible "+ uu1+ " "+ uu2);
    return uu1.isCompatible( uu2);
  }

  /** Return true if  unitString1 is convertible to unitString2 */
  static public boolean isCompatibleWithExceptions(String unitString1, String unitString2) throws Exception {
    Unit uu1 = format.parse(unitString1);
    Unit uu2 = format.parse(unitString2);
    return uu1.isCompatible( uu2);
  }

  /** Return true if this ucar.units.Unit is a Date. */
  static public boolean isDateUnit(ucar.units.Unit uu) {
    boolean ok = uu.isCompatible(dateUnit);
    if (!ok) return false;
    try {
      uu.getConverterTo(dateUnit);
      return true;
    } catch(ConversionException e) {
      return false;
    }
  }

  /** Return true if this ucar.units.Unit is convertible to secs. */
  static public boolean isTimeUnit(ucar.units.Unit uu) {
    return uu.isCompatible(timeUnit);
  }

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

  /** Return true if the given unit is convertible to a date Unit.
   * allowed format is something like:
   * <pre>[-]Y[Y[Y[Y]]]-MM-DD[(T| )hh[:mm[:ss[.sss*]]][ [+|-]hh[[:]mm]]]</pre>
   **/
  static public boolean isDateUnit(String unitString) {
    SimpleUnit su = factory(unitString);
    if (su == null) return false;
    return isDateUnit( su.getUnit());
  }

  /** Return true if the given unit is a time Unit, eg "seconds".
   **/
  static public boolean isTimeUnit(String unitString) {
    SimpleUnit su = factory(unitString);
    if (su == null) return false;
    return isTimeUnit( su.getUnit());
  }

  ////////////////////////////////////////////////
  protected ucar.units.Unit uu;

  /** for subclasses. */
  protected SimpleUnit() {
  }

  /** Wrap a ucar.units.Unit in a SimpleUnit. Use factory(). */
  SimpleUnit(ucar.units.Unit uu) {
    this.uu = uu;
  }

  /** Unit string representation. */
  public String getName() { return uu.toString(); }

  /** Unit string representation. */
  public String toString() { return uu.toString(); }

  /** Get underlying ucar.unit.Unit. */
  public Unit getUnit() { return uu; }

    /** Convert given value of this unit to the new unit. */
  public double convertTo(double value, SimpleUnit outputUnit) throws ConversionException {
    return uu.convertTo( value, outputUnit.getUnit());
  }

    /** Divide this unit by given unit, creat a new unit to hold result. */
  public SimpleUnit divideBy(SimpleUnit denom) throws OperationException {
    return new SimpleUnit( uu.divideBy( denom.uu));
  }

  /** Return true if this unit is an instance of an UnknownUnit. */
  public boolean isUnknownUnit() { return (uu instanceof UnknownUnit); }

  ///////////////////////////////////////////////////////////
  // testing

  private static void tryDivide() throws Exception {
    SimpleUnit t1 = factory("3 days");
    SimpleUnit t2 = factory("1 minute");
    SimpleUnit t3 = t1.divideBy(t2);
    System.out.println(t1+" divideBy "+t2+" = " +t3);
  }

  private static void tryConvert() throws Exception {
    SimpleUnit t1 = factory("1 days");
    SimpleUnit t2 = factory("1 hour");
    double v = t1.convertTo(1.0, t2);
    System.out.println(t1+" convertTo "+t2+" = " +v);
  }

  /** testing */
  public static void main(String[] args) throws Exception {
    SimpleUnit tu = factory("3 days");
    System.out.println("time.isTimeUnit = "+isTimeUnit(tu.getUnit()));
    System.out.println("time.isDateUnit = "+isDateUnit(tu.getUnit()));

    String text = "3 days since 1930-07-27 12:00:00-05:00";
    SimpleUnit du = factory( text);
    System.out.println(text+" == standard format "+du);
    System.out.println("date.isTimeUnit = "+isTimeUnit(du.getUnit()));
    System.out.println("date.isDateUnit = "+isDateUnit(du.getUnit()));

    text = "0 hours since 1930-07-29T01:00:00-08:00";
    du = factory( text);
    System.out.println(text+" == standard format "+du);
    System.out.println("date.isTimeUnit = "+isTimeUnit(du.getUnit()));
    System.out.println("date.isDateUnit = "+isDateUnit(du.getUnit()));
    System.out.println(text+" == standard format "+du);

    tryDivide();
    tryConvert();
  }

}


/**
 * $Log: SimpleUnit.java,v $
 * Revision 1.1  2004/09/11 00:04:22  caron
 * *** empty log message ***
 *
 * Revision 1.3  2004/08/16 20:53:52  caron
 * 2.2 alpha (2)
 *
 * Revision 1.2  2004/07/12 23:40:20  caron
 * 2.2 alpha 1.0 checkin
 *
 * Revision 1.1  2004/07/06 19:28:13  caron
 * pre-alpha checkin
 *
 * Revision 1.1  2004/06/07 20:24:55  caron
 * move SimpleUnit to ucar.unit.units, add aliases in UnitDB
 *
 * Revision 1.2  2004/05/11 23:30:37  caron
 * release 2.0a
 *
 * Revision 1.4  2004/03/18 23:55:15  caron
 * better detection of Date unit
 *
 * Revision 1.3  2004/02/06 21:29:19  caron
 * add  getDateOrigin()
 *
 * Revision 1.2  2003/10/28 23:57:20  caron
 * minor
 *
 * Revision 1.1  2003/10/02 20:33:48  caron
 * move SimpleUnit to dataset; add <units> tag; add projections to CF
 *
 */