/*
 * File:     ContinuousRateTSTrack.java
 * Project:  MPI Linguistic Application
 * Date:     03 April 2006
 *
 * Copyright (C) 2001-2006  Max Planck Institute for Psycholinguistics
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package mpi.eudico.client.annotator.timeseries;


/**
 * Implementation of a time series data track. Data are stored in a flat list
 * or array of values; in combination with the  (fixed) sample rate it is
 * possible to find a time-value pair.
 */
public class ContinuousRateTSTrack extends AbstractTSTrack {
    private float sampleRate;
    private float msPerSample;
    private float[] data;

    /**
     * Constructor.
     */
    public ContinuousRateTSTrack() {
        super();
    }

    /**
     * @see mpi.eudico.client.annotator.timeseries.TimeSeriesTrack#setSampleRate(int)
     */
    public void setSampleRate(float rate) {
        sampleRate = rate;
        msPerSample = 1000 / sampleRate;
    }

    /**
     * @see mpi.eudico.client.annotator.timeseries.TimeSeriesTrack#getSampleRate()
     */
    public float getSampleRate() {
        return sampleRate;
    }

    /**
     * @see mpi.eudico.client.annotator.timeseries.TimeSeriesTrack#getSampleCount()
     */
    public int getSampleCount() {
        if (data == null) {
            return 0;
        }

        return data.length;
    }

    /**
     * Returns an array of floats.
     *
     * @see mpi.eudico.client.annotator.timeseries.TimeSeriesTrack#getData()
     */
    public Object getData() {
        return data;
    }

    /**
     * Sets the data of this tracks. Currently this method only accepts an
     * array of floats; in any other case  an IllegalArgumentException will be
     * thrown.
     *
     * @param data DOCUMENT ME!
     *
     * @throws IllegalArgumentException when the data is provided in anything
     *         else but an array of floats
     *
     * @see mpi.eudico.client.annotator.timeseries.TimeSeriesTrack#setData(java.lang.Object)
     */
    public void setData(Object data) {
        if (!(data instanceof float[])) {
            throw new IllegalArgumentException(
                "This track only accepts an array of floats");
        }

        this.data = (float[]) data;
    }

    /**
     * Returns a calculated index based on the time, time offset and the sample
     * rate. There is no guarantee that the index is within the data array's
     * bounds.
     *
     * @see mpi.eudico.client.annotator.timeseries.AbstractTSTrack#getIndexForTime(long)
     */
    public int getIndexForTime(long time) {
        int index = -1;

        if ((time + timeOffset) >= 0) {
            //index = (int) ((time + timeOffset) / msPerSample);	

            /* this might be more accurate and more consistent */
            index = (msPerSample >= 1)
                ? (int) ((time + timeOffset) / msPerSample)
                : (int) Math.ceil((time + timeOffset) / msPerSample);
        }

        return index;
    }

    /**
     * @see mpi.eudico.client.annotator.timeseries.AbstractTSTrack#getTimeForIndex(int)
     */
    public long getTimeForIndex(int index) {
        if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(
                "Index should be greater than or equal to 0");
        }

        long time = 0L;

        if (data != null) {
            if (index >= data.length) {
                throw new ArrayIndexOutOfBoundsException("Index (" + index +
                    ") is greater than " + (data.length - 1));
            }

            time = (long) (index * msPerSample);
        }

        return time + timeOffset;
    }

    /**
     * @see mpi.eudico.client.annotator.timeseries.TimeSeriesTrack#getAverage(long,
     *      long)
     */
    public float getAverage(long begin, long end) {
        if (data == null) {
            return 0; // throw an exception?
        }

        if (begin >= end) {
            throw new IllegalArgumentException(
                "Begin time should not be greater than or equal to the end time");
        }

        if (begin < 0) {
            throw new ArrayIndexOutOfBoundsException(begin + " < 0");
        }

        if (end > (msPerSample * data.length)) {
            // in fact: end + timeOffset > msPerSample * data.length - timeOffset
            throw new ArrayIndexOutOfBoundsException(end + " > " +
                (msPerSample * data.length));
        }

        int bi = getIndexForTime(begin);
        int ei = getIndexForTime(end);
        int count = 0;
        float total = 0f;

        for (int i = bi; (i <= ei) && (i < data.length); i++) {
            total += data[i];
            count++;
        }

        if (count == 0) {
            return 0;
        } else {
            return total / count;
        }
    }

    /**
     * @see mpi.eudico.client.annotator.timeseries.TimeSeriesTrack#getMaximum(long,
     *      long)
     */
    public float getMaximum(long begin, long end) {
        if (data == null) {
            return 0; // throw an exception?
        }

        if (begin >= end) {
            throw new IllegalArgumentException(
                "Begin time should not be greater than or equal to the end time");
        }

        if (begin < 0) {
            throw new ArrayIndexOutOfBoundsException(begin + " < 0");
        }

        if (end > (msPerSample * data.length)) {
            // in fact: end + timeOffset > msPerSample * data.length - timeOffset
            throw new ArrayIndexOutOfBoundsException(end + " > " +
                (msPerSample * data.length));
        }

        int bi = getIndexForTime(begin);
        int ei = getIndexForTime(end);
        float max = Integer.MIN_VALUE; //problem with Float.MIN_VALUE

        for (int i = bi; (i <= ei) && (i < data.length); i++) {
            if (data[i] > max) {
                max = data[i];
            }
        }

        return max;
    }

    /**
     * @see mpi.eudico.client.annotator.timeseries.TimeSeriesTrack#getMinimum(long,
     *      long)
     */
    public float getMinimum(long begin, long end) {
        if (data == null) {
            return 0; // throw an exception?
        }

        if (begin >= end) {
            throw new IllegalArgumentException(
                "Begin time should not be greater than or equal to the end time");
        }

        if (begin < 0) {
            throw new ArrayIndexOutOfBoundsException(begin + " < 0");
        }

        if (end > (msPerSample * data.length)) {
            // in fact: end + timeOffset > msPerSample * data.length - timeOffset
            throw new ArrayIndexOutOfBoundsException(end + " > " +
                (msPerSample * data.length));
        }

        int bi = getIndexForTime(begin);
        int ei = getIndexForTime(end);
        float min = Integer.MAX_VALUE;

        for (int i = bi; (i <= ei) && (i < data.length); i++) {
            if (data[i] < min) {
                min = data[i];
            }
        }

        return min;
    }
}
