/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <http://www.opensourcephysics.org/>
 */

package org.opensourcephysics.numerics;
import java.text.DecimalFormat;
import org.opensourcephysics.controls.XML;
import org.opensourcephysics.controls.XMLControl;

/**
 * DoubleArray stores an array of doubles as a string and as an array.
*/
public class DoubleArray {
  public static int NumberFormatError = 1;
  public static int ArrayIndexOutOfBoundsError = 2;
  protected DecimalFormat format = new DecimalFormat("0.00");       // display format //$NON-NLS-1$
  protected DecimalFormat formatExp = new DecimalFormat("0.00#E0"); // display format //$NON-NLS-1$
  protected double[] array;
  protected String defaultString;
  protected double[] defaultArray;
  protected int errorcode = 0;

  /**
   * Creates a DoubleArray of the given length with all elements set to zero.
   *
   * The length of the arry cannot be changed.
   *
   * @param n
   */
  public DoubleArray(int n) {
    array = new double[n];
    defaultArray = array;
  }

  /**
   * Creates a DoubleArray of the given length with all elements set to zero.
   *
   * The length of the arry cannot be changed.
   *
   * @param array
   */
  public DoubleArray(double[] array) {
    defaultArray = array.clone();
    this.array = defaultArray;
  }

  /**
   * Creates an arry with the given string being the default string.
   *
   * The given string determines the length of the array.  This lenght cannot be changed.
   * @param str
   * @throws NumberFormatException
   */
  public DoubleArray(String str) throws NumberFormatException {
    this.array = toDouble(str);
    defaultString = str;
    defaultArray = array;
  }

  /**
   * Creates a DecimalFormat for printing array elements using the given pattern and the symbols
   * for the default locale.
   *
   * @see java.text.DecimalFormat
   */
  public void setDecimalFormat(String pattern) {
    format = new DecimalFormat(pattern);
    formatExp = format;
  }

  /**
   * Gets the default array.
   *
   * The default is used if the input string is not valid due to a number format exception or an
   * array length exception.
   *
   * @return string
   */
  public String getDefault() {
    return defaultString;
  }

  /**
   * Converts the array to a comma delimited string enclosed in braces.
   *
   * @return string
   */
  public String toString() {
    if(errorcode>0) {
      return defaultString;
    }
    String str = "{"; //$NON-NLS-1$
    for(int i = 0, n = array.length; i<n; i++) {
      str += ((Math.abs(array[i])<0.1)||(Math.abs(array[i])>1000)) ? formatExp.format(array[i]) : format.format(array[i]);
      if(i<n-1) {
        str += ", "; //$NON-NLS-1$
      }
    }
    str += "}"; //$NON-NLS-1$
    return str;
  }

  /**
   * Gets the error code
   * @return
   */
  public int getError() {
    return errorcode;
  }

  /**
   * Converts the string to an array and returns the array.
   *
   * If the conversion fails, the error code is set and default array is returned.
   *
   * @return double[] the converted array
   */
  public double[] getArray(String str) {
    set(str);
    return array;
  }

  /**
   * Gets the array of doubles.
   * @return
   */
  public double[] getArray() {
    return array;
  }

  /**
   * Sets the array to the given string.
   * @return true if successful; false otherwise
   */
  public boolean set(String str) {
    errorcode = 0;
    try {
      array = toDouble(str);
    } catch(NumberFormatException ex) {
      errorcode = NumberFormatError;
      array = toDouble(defaultString);
      return false;
    } catch(ArrayIndexOutOfBoundsException ex) {
      errorcode = ArrayIndexOutOfBoundsError;
      array = toDouble(defaultString);
      return false;
    }
    return true;
  }

  public void setDefaultArray(double[] array) {
    defaultArray = array.clone();
    this.array = defaultArray;
  }

  /**
   * Converts a comma delimited string enclosed in braces to an array.
   *
   * v={1.5, 2.0, -3.2} would returns an array with componets (1.5,2.0,-3.2).
   * @param str
   * @return
   */
  protected double[] toDouble(String str) throws ArrayIndexOutOfBoundsException {
    if(str==null) {
      str = "{}"; //$NON-NLS-1$
    }
    double[] array = null;
    int start = str.indexOf("{")+1; //$NON-NLS-1$
    int end = str.indexOf("}");     //$NON-NLS-1$
    if(end-start<=0) {
      errorcode = ArrayIndexOutOfBoundsError;
      return defaultArray;
    }
    String[] s = str.substring(start, end).split(","); //$NON-NLS-1$
    if((this.array!=null)&&(this.array.length!=s.length)) {
      throw new ArrayIndexOutOfBoundsException("Array length cannot be changed in DoubleArray. "+str); //$NON-NLS-1$
    }
    array = new double[s.length];
    for(int i = 0, n = s.length; i<n; i++) {
      try {
        array[i] = Double.parseDouble(s[i]);
      } catch(NumberFormatException ex) {
        errorcode = NumberFormatError;
      }
    }
    return array;
  }

  /*
   * Tests this class.
   * @param args  command line parameters
   */
  /*
  public static void main(String[] args) {
    DoubleArray a = new DoubleArray("{1.5, 2.0, -3.2}");
    System.out.println(a);
    a.set("{3.0, -500, 0}");    // set new value
    System.out.println(a);
    a.set("{xx.5, 2.0, -3.2}"); // array set to default due to error in input
    System.out.println(a);
    a.set("{3.0, -500, 0, 0}"); // array set to default due to error in input
  }*/

  /**
 * Returns an XML.ObjectLoader to save and load object data.
 *
 * @return the XML.ObjectLoader
 */
  public static XML.ObjectLoader getLoader() {
    return new Loader();
  }

  /**
   * A class to save and load DoubleArray data.
   */
  static class Loader implements XML.ObjectLoader {
    /**
     * Saves DoubleArray data in an XMLControl.
     *
     * @param control the control
     * @param obj the DrawingPanel to save
     */
    public void saveObject(XMLControl control, Object obj) {
      DoubleArray array = (DoubleArray) obj;
      control.setValue("data", array.getArray()); //$NON-NLS-1$
    }

    /**
     * Creates a DoubleArray.
     *
     * @param control the control
     * @return the newly created panel
     */
    public Object createObject(XMLControl control) {
      return new DoubleArray((double[]) control.getObject("data")); //$NON-NLS-1$
    }

    /**
     * Loads an object with data from an XMLControl.
     *
     * @param control the control
     * @param obj the object
     * @return the loaded object
     */
    public Object loadObject(XMLControl control, Object obj) {
      DoubleArray dataArray = (DoubleArray) obj;
      double[] data = (double[]) control.getObject("data"); //$NON-NLS-1$
      dataArray.array = data;
      dataArray.defaultArray = data;
      return obj;
    }

  }

}

/*
 * Open Source Physics software is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License (GPL) as
 * published by the Free Software Foundation; either version 2 of the License,
 * or(at your option) any later version.

 * Code that uses any portion of the code in the org.opensourcephysics package
 * or any subpackage (subdirectory) of this package must must also be be released
 * under the GNU GPL license.
 *
 * This software 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2007  The Open Source Physics project
 *                     http://www.opensourcephysics.org
 */
