/* ==============================================
 * Simtools : The tools library used in JSynoptic
 * ==============================================
 *
 * Project Info:  http://jsynoptic.sourceforge.net/index.html
 * 
 * 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.
 *
 * (C) Copyright 2001-2003, by :
 *     Corporate: 
 *         Astrium SAS 
 *         EADS CRC
 *     Individual: 
 *         Nicolas Brodu
 *
 * $Id: DynamicDataSource.java,v 1.5 2008/04/10 16:42:45 ogor Exp $
 *
 * Changes
 * -------
 * 21-Jan-2004: made initial version from  animated collection (NB)
 *
 */
package simtools.data;
import simtools.data.buffer.DelayedBuffer;
import simtools.util.NumberStringComparator;
public class DynamicDataSource extends DataSource {
  /**
   * Builds a new dynamic data source with the given data information.
   * Only the last value will be available, no buffer is set up.
   * @param di the data information for this source (name, comment, unit...)
   * @param kind the type of the data source. @see ValueProvider
   */
  public DynamicDataSource(DataInfo di, int kind) {
    this(di,kind,0);
  }
  /**
   * Builds a new dynamic data source with the given data information, and backed 
   * by a data buffer of the given size
   * @param di the data information for this source (name, comment, unit...)
   * @param kind the type of the data source. @see ValueProvider
   * @param delay the size of the buffer for past values
   */
  public DynamicDataSource(DataInfo di, int kind, int delay) {
    info = di;
    if (delay>0) buffer = new DelayedBuffer(kind, this, delay);
    else buffer = null;
    this.kind = kind;
    lastIndex = -1;
    hasValue = false; // Uninitialized
    autoRegister = true;
  }
  /** This class contains info necessary to handle the data sources.
   *  The vector has the same size as this object (reminder: Vector inheritance).
   */
  public DataInfo info;
  protected long lastIndex;
  /** Set this to true to register the values as soon as they are set
   * True by default, but this behaviour is not the same as the source collection so it can be set off 
   */
  protected boolean autoRegister = true; 
  /** See valueProvider 
   */
  protected int kind; 
  /** Whether or not values were set, or if they are unititialized. Itself false when uninitialized, so OK
   */
  protected boolean hasValue;
  /** current / cached values
   *  current values are positioned by setvalue but not registered yet
   *  cached values are registered and returned by the collection until the next register.
   *  current values correspond to currentIndex, cached values to the next.
   */
//  /o  protected type currentType, cachedType;
//  /  protected type minType, maxType;
//  ------ START of perl-generated corresponding code --------
  protected byte currentByte, cachedByte;
  protected byte minByte, maxByte;
  protected short currentShort, cachedShort;
  protected short minShort, maxShort;
  protected int currentInteger, cachedInteger;
  protected int minInteger, maxInteger;
  protected long currentLong, cachedLong;
  protected long minLong, maxLong;
  protected float currentFloat, cachedFloat;
  protected float minFloat, maxFloat;
  protected double currentDouble, cachedDouble;
  protected double minDouble, maxDouble;
  protected Object currentObject, cachedObject;
  protected Object minObject, maxObject;
//  -------- END of perl-generated corresponding code --------
  DelayedBuffer buffer;
  /**
   * Adds a buffer to this source so it can keep previous values in memory
   */
  public void bufferize(int delay) throws UnsupportedOperation {
    buffer = new DelayedBuffer(kind, this,delay);
  }
  /**
   * @return Returns the autoRegister.
   */
  public boolean isAutoRegister() {
    return autoRegister;
  }
  /**
   * @param autoRegister The autoRegister to set.
   */
  public void setAutoRegister(boolean autoRegister) {
    this.autoRegister = autoRegister;
  }
  /** Other functions have a reasonable default implementation
   */
  public Object getMin()  throws UnsupportedOperation {
    if (hasValue==false) throw new UnsupportedOperation();
    switch (kind) {
//    /      case ValueProvider.TypeProvider: return new Type(minType);
//    ------ START of perl-generated corresponding code --------
    case ValueProvider.ByteProvider: return new Byte(minByte);
    case ValueProvider.ShortProvider: return new Short(minShort);
    case ValueProvider.IntegerProvider: return new Integer(minInteger);
    case ValueProvider.LongProvider: return new Long(minLong);
    case ValueProvider.FloatProvider: return new Float(minFloat);
    case ValueProvider.DoubleProvider: return new Double(minDouble);
//    -------- END of perl-generated corresponding code --------
    case ValueProvider.ObjectProvider: return minObject;
    }
    return null;
  }
  public Object getMax() throws UnsupportedOperation {
    if (hasValue==false) throw new UnsupportedOperation();
    switch (kind) {
//    /      case ValueProvider.TypeProvider: return new Type(maxType);
//    ------ START of perl-generated corresponding code --------
    case ValueProvider.ByteProvider: return new Byte(maxByte);
    case ValueProvider.ShortProvider: return new Short(maxShort);
    case ValueProvider.IntegerProvider: return new Integer(maxInteger);
    case ValueProvider.LongProvider: return new Long(maxLong);
    case ValueProvider.FloatProvider: return new Float(maxFloat);
    case ValueProvider.DoubleProvider: return new Double(maxDouble);
//    -------- END of perl-generated corresponding code --------
    case ValueProvider.ObjectProvider: return maxObject;
    }
    return null;
  }
//  /  public type getTypeMin() throws DataException {
//  /    if (kind == ValueProvider.TypeProvider) return minType;
//  /    return super.getTypeMin();
//  /  }
//  /
//  /  public type getTypeMax() throws DataException {
//  /    if (kind == ValueProvider.TypeProvider) return maxType;
//  /    return super.getTypeMax();
//  /  }
//  /
//  ------ START of perl-generated corresponding code --------
  public byte getByteMin() throws DataException {
    if (kind == ValueProvider.ByteProvider) return minByte;
    return super.getByteMin();
  }
  public byte getByteMax() throws DataException {
    if (kind == ValueProvider.ByteProvider) return maxByte;
    return super.getByteMax();
  }
  public short getShortMin() throws DataException {
    if (kind == ValueProvider.ShortProvider) return minShort;
    return super.getShortMin();
  }
  public short getShortMax() throws DataException {
    if (kind == ValueProvider.ShortProvider) return maxShort;
    return super.getShortMax();
  }
  public int getIntegerMin() throws DataException {
    if (kind == ValueProvider.IntegerProvider) return minInteger;
    return super.getIntegerMin();
  }
  public int getIntegerMax() throws DataException {
    if (kind == ValueProvider.IntegerProvider) return maxInteger;
    return super.getIntegerMax();
  }
  public long getLongMin() throws DataException {
    if (kind == ValueProvider.LongProvider) return minLong;
    return super.getLongMin();
  }
  public long getLongMax() throws DataException {
    if (kind == ValueProvider.LongProvider) return maxLong;
    return super.getLongMax();
  }
  public float getFloatMin() throws DataException {
    if (kind == ValueProvider.FloatProvider) return minFloat;
    return super.getFloatMin();
  }
  public float getFloatMax() throws DataException {
    if (kind == ValueProvider.FloatProvider) return maxFloat;
    return super.getFloatMax();
  }
  public double getDoubleMin() throws DataException {
    if (kind == ValueProvider.DoubleProvider) return minDouble;
    return super.getDoubleMin();
  }
  public double getDoubleMax() throws DataException {
    if (kind == ValueProvider.DoubleProvider) return maxDouble;
    return super.getDoubleMax();
  }
  /**
   * Compute the min and the max of this data source
   * @return -  true if min or max values have changed
   * @throws UnsupportedOperation
   */
  public boolean computeMinMax() throws UnsupportedOperation {
      boolean hasChanged = false;
      
    if (hasValue == false) {
        throw new UnsupportedOperation();
    }
    
    if (buffer==null) {
      switch (kind) {
      case ValueProvider.ByteProvider: 
        minByte = maxByte =  cachedByte;
        break;
        
      case ValueProvider.ShortProvider: 
        minShort = maxShort = cachedShort;
        break;
        
      case ValueProvider.IntegerProvider: 
        minInteger = maxInteger =  cachedInteger;
        break;
      case ValueProvider.LongProvider: 
        minLong = maxLong = cachedLong;
        break;
        
      case ValueProvider.FloatProvider: 
        minFloat = maxFloat = cachedFloat;
        break;
        
      case ValueProvider.DoubleProvider: 
        minDouble = maxDouble = cachedDouble;
        break;
      case ValueProvider.ObjectProvider: 
        minObject = maxObject = cachedObject;
        break;
      }
      hasChanged = true;  // If there is no buffer, the range changes on each new value
  
    } else {
          int order = sortedOrder();
          hasChanged = (order != 0);
          
          switch (kind) {
          case ValueProvider.ByteProvider: 
              if (cachedByte < minByte){
                  minByte = cachedByte;
                  hasChanged = true;
              }
              if (cachedByte > maxByte){
                    maxByte = cachedByte;
                    hasChanged = true;
                }
             
          case ValueProvider.ShortProvider: 
              if (cachedShort < minShort){
                  minShort = cachedShort;
                    hasChanged = true;
                }
                if (cachedShort > maxShort){
                    maxShort = cachedShort;
                    hasChanged = true;
                }
              
          case ValueProvider.IntegerProvider: 
              if (cachedInteger < minInteger){
                  minInteger = cachedInteger;
                    hasChanged = true;
                }
                if (cachedInteger > maxInteger){
                    maxInteger = cachedInteger;
                    hasChanged = true;
                }
          case ValueProvider.LongProvider: 
              if (cachedLong < minLong){
                  minLong = cachedLong;
                    hasChanged = true;
                }
                if (cachedLong > maxLong){
                    maxLong = cachedLong;
                    hasChanged = true;
                }
          case ValueProvider.FloatProvider: 
              if (cachedFloat < minFloat){
                  minFloat = cachedFloat;
                    hasChanged = true;
                }
                if (cachedFloat > maxFloat){
                    maxFloat = cachedFloat;
                    hasChanged = true;
                }
                
          case ValueProvider.DoubleProvider: 
              if (cachedDouble < minDouble){
                  minDouble = cachedDouble;
                    hasChanged = true;
                }
                if (cachedDouble > maxDouble){
                    maxDouble = cachedDouble;
                    hasChanged = true;
                }
                
          case ValueProvider.ObjectProvider: 
              if (cachedObject instanceof Comparable){
                  Comparable cco = (Comparable)cachedObject;
                  
                  if (cco.compareTo(minObject) < 0){
                      minObject = cco;
                      hasChanged = true;
                  }
                  if (cco.compareTo(maxObject) > 0){
                        maxObject = cco;
                        hasChanged = true;
                    }
              }
          }
    }
    return hasChanged;
  }
  
  public boolean isComparable(int i) {
    return false; // not comparable by default
  }
  // In this source, we use delay buffer. Those buffers privilege new values over the old ones.
  // Thus, if a buffer is present for a datasource, use it.
  // Otherwise, only the last data is available
  public long getStartIndex() {
    if (buffer==null) return lastIndex;
    return buffer.getStartIndex();
  }
  public long getLastIndex() {
    if (buffer==null) return lastIndex;
    return buffer.getEndIndex();
  }
  public long computeStartIndex() {
    return getStartIndex();
  }
  public long computeLastIndex() {
    return getLastIndex();
  }
  /**
   * Change our own information. May be called any time, and will notify
   * the our listeners.
   */
  protected void changeInfo(DataInfo di) {
    info = di;
    notifyListenersForInfoChange(di);
  }
//  /o  /**
//  /   * Sets the value of this Datasource to the type v
//  /   * @param v the type value to set
//  /   */
//  /  protected void setTypeValue(type v) {
//  /    currentType = v;
//  /    if (autoRegister) registerNewValue();
//  /  }
//  /
//  ------ START of perl-generated corresponding code --------
  /**
   * Sets the value of this Datasource to the byte v
   * @param v the byte value to set
   */
  protected void setByteValue(byte v) {
    currentByte = v;
    if (autoRegister) registerNewValue();
  }
  /**
   * Sets the value of this Datasource to the short v
   * @param v the short value to set
   */
  protected void setShortValue(short v) {
    currentShort = v;
    if (autoRegister) registerNewValue();
  }
  /**
   * Sets the value of this Datasource to the int v
   * @param v the int value to set
   */
  protected void setIntegerValue(int v) {
    currentInteger = v;
    if (autoRegister) registerNewValue();
  }
  /**
   * Sets the value of this Datasource to the long v
   * @param v the long value to set
   */
  protected void setLongValue(long v) {
    currentLong = v;
    if (autoRegister) registerNewValue();
  }
  /**
   * Sets the value of this Datasource to the float v
   * @param v the float value to set
   */
  protected void setFloatValue(float v) {
    currentFloat = v;
    if (autoRegister) registerNewValue();
  }
  /**
   * Sets the value of this Datasource to the double v
   * @param v the double value to set
   */
  protected void setDoubleValue(double v) {
    currentDouble = v;
    if (autoRegister) registerNewValue();
  }
  /**
   * Sets the value of this Datasource to the Object v
   * @param v the Object value to set
   */
  protected void setObjectValue(Object v) {
    currentObject = v;
    if (autoRegister) registerNewValue();
  }
//  -------- END of perl-generated corresponding code --------
  /**
   * Call this function once you have set up all the values with setValue.
   * The last value is reused if no new value is set.
   * Note that by default, auto registering is true, so just calling setXXXValue will do the job
   * @param dsnum
   * @param v
   */
  protected void registerNewValue() {
    lastIndex++;
    switch (kind) {
    case ValueProvider.ByteProvider:
        cachedByte = currentByte;
        if (buffer!=null) {
            try {
                buffer.setByteValue(lastIndex,cachedByte);
            } catch (DataException e) {}
        }
        if (hasValue==false) {
            hasValue = true;
            minByte = maxByte = cachedByte;
        } 
      break;
    case ValueProvider.ShortProvider:
        cachedShort = currentShort;
        if (buffer!=null) {
            try {
                buffer.setShortValue(lastIndex,cachedShort);
            } catch (DataException e) {}
        }
        if (hasValue==false) {
            hasValue = true;
            minShort = maxShort = cachedShort;  
        } 
        break;
    case ValueProvider.IntegerProvider:
        cachedInteger = currentInteger;
        if (buffer!=null) {
            try {buffer.setIntegerValue(lastIndex,cachedInteger);
            } catch (DataException e) {}
        }
        if (hasValue==false) {
            hasValue = true;
            minInteger = maxInteger = cachedInteger;
        } 
      break;
      
    case ValueProvider.LongProvider:
      cachedLong = currentLong;
      if (buffer!=null){
          try {buffer.setLongValue(lastIndex,cachedLong);
          } catch (DataException e) {}
      }
      if (hasValue==false) {
        hasValue = true;
        minLong = maxLong = cachedLong;
      } 
      break;
      
    case ValueProvider.FloatProvider:
      cachedFloat = currentFloat;
      if (buffer!=null) {
          try {buffer.setFloatValue(lastIndex,cachedFloat);
          } catch (DataException e) {}
      }
      if (hasValue==false) {
        hasValue = true;
        minFloat = maxFloat = cachedFloat;
      } 
      break;
      
    case ValueProvider.DoubleProvider:
      cachedDouble = currentDouble;
      if (buffer!=null){
          try {buffer.setDoubleValue(lastIndex,cachedDouble);
          } catch (DataException e) {}
      }
      if (hasValue==false) {
          hasValue = true;
          minDouble = maxDouble = cachedDouble;
      } 
      break;
    case ValueProvider.ObjectProvider:
        cachedObject = currentObject;
        if (buffer!=null) {
            try {buffer.setValue(lastIndex,cachedObject);
            } catch (DataException e) {}
        }
        if (hasValue==false) {
            hasValue = true;
            minObject = maxObject = cachedObject;
        } 
        break;
    }
    // Update sorted order value
    updateSortedOrder();
    
    boolean vRangeChanged;
    try {
        vRangeChanged = computeMinMax();
        
    } catch (UnsupportedOperation e) {
        vRangeChanged = true;
    }
    if (vRangeChanged) {
        notifyListenersForValueRangeChange();
    }
    
    notifyListenersForIndexRangeChange(getStartIndex(), getLastIndex());
    notifyEndNotificationListeners();
  }
  /* (non-Javadoc)
   * @see simtools.data.DataSourceCollection#getInformation()
   */
  public DataInfo getInformation() {
    return info;
  }
  /* (non-Javadoc)
   * @see simtools.data.DataSourceCollection#getValue(int, long)
   */
  public Object getValue(long index) throws DataException {
    if (index==lastIndex) switch (kind) {
//    /      case ValueProvider.TypeProvider: return new Type(cachedType);
//    ------ START of perl-generated corresponding code --------
    case ValueProvider.ByteProvider: return new Byte(cachedByte);
    case ValueProvider.ShortProvider: return new Short(cachedShort);
    case ValueProvider.IntegerProvider: return new Integer(cachedInteger);
    case ValueProvider.LongProvider: return new Long(cachedLong);
    case ValueProvider.FloatProvider: return new Float(cachedFloat);
    case ValueProvider.DoubleProvider: return new Double(cachedDouble);
//    -------- END of perl-generated corresponding code --------
    default: return cachedObject;
    }
    if (buffer==null) throw new NoSuchIndex(index);
    long start = buffer.getStartIndex(); // ask buffer
    long last = buffer.getEndIndex(); // ask buffer
    if ((index<start) || (index>last)) throw new NoSuchIndex(index);
    return buffer.getValue(index);
  }
  public int getKind() {
    return kind;
  }
  /* (non-Javadoc)
   * @see simtools.data.DataSourceCollection#valueClass(int)
   */
  public Class valueClass() {
    switch (kind) {
//    /      case ValueProvider.TypeProvider: return Type.class;
//    ------ START of perl-generated corresponding code --------
    case ValueProvider.ByteProvider: return Byte.class;
    case ValueProvider.ShortProvider: return Short.class;
    case ValueProvider.IntegerProvider: return Integer.class;
    case ValueProvider.LongProvider: return Long.class;
    case ValueProvider.FloatProvider: return Float.class;
    case ValueProvider.DoubleProvider: return Double.class;
//    -------- END of perl-generated corresponding code --------
    }
    return Object.class;
  }
}