/***************************************************************************
 *   Copyright (C) 2005 by David Saxton                                    *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/

#ifndef OSCILLOSCOPEDATA_H
#define OSCILLOSCOPEDATA_H

#include <QColor>
#include <QObject>
#include <stdint.h>
#include <vector>

#define DATA_CHUNK_SIZE (8192/sizeof(T))

/*
#define DATA_CHUNK_ARRAY_SIZE ((8192-sizeof(uint32_t))/sizeof(DataChunk<T>*))

// Allow a minimum of 64 megabytes of stored data (67108864 bytes)
/// \todo The maximum allowed amount of stored data should be configurable or
/// more intelligent (e.g. taking into account the number of probes or the
/// amount of physical memory on the system).
#define DCARRAY_ARRAY_SIZE ((67108864/(8192*DATA_CHUNK_ARRAY_SIZE))+1)
*/

/** max number of samples that a probe can hold */
#define MAX_PROBE_DATA_SIZE     ( 1 * 1024 * 1024 )
// TODO ^ should be configurable

/**
For use in LogicProbe: Every time the input changes state, the new input state
is recorded in value, along with the simulator time that it occurs at.
 */
class LogicDataPoint
{
	public:
		LogicDataPoint() : value(0), time(0) { }
		LogicDataPoint( bool v, uint64_t t) : value(v), time(t) {}

		bool value	:  1;
		uint64_t time	: 63;
};

/**
@author David Saxton
 */
class ProbeData : public QObject
{
	Q_OBJECT
	public:
		ProbeData( int id);
		~ProbeData() override;

		/**
		 * @returns unique id for oscilloscope, set on construction
		 */
		int id() const { return m_id; }
		/**
		 * Set the proportion (0 = top, 1 = bottom) of the way down the
		 * oscilloscope view that the probe output is drawn. If the proportion
		 * is out of range ( <0, or >1), then the drawPosition is set to 0/1
		 */
		void setDrawPosition( float drawPosition) { m_drawPosition = drawPosition; }
		/**
		 * Returns the draw position. Default is 0.5.
		 * @see setDrawPosition
		 */
		float drawPosition() const { return m_drawPosition; }
		/**
		 * Set the colour that is used to display the probe in the oscilloscope.
		 * Default is black.
		 */
		void setColor( QColor color);
		/**
		 * @returns the colour that is used to display the probe in the oscilloscope
		 */
		QColor color() const { return m_color; }
// 		/**
// 		 * Will not record any data when paused
// 		 */
// 		void setPaused( bool isPaused) { b_isPaused = isPaused; }
		/**
		 * Returns the time (in Simulator time) that this probe was created at,
		 * or last reset.
		 */
		uint64_t resetTime() const { return m_resetTime; }
		/**
		 * Erases all recorded data, and sets m_resetTime to the current
		 * simulator time.
		 */
		virtual void eraseData() = 0;
		/**
		 * Searches for and returns the position of the last DataPoint that was
		 * added before or at the given Simulator time. If no DataPoints were
		 * were recorded before the given time, then will return the one closest
		 * to the given time. Will return 0 if no DataPoints have been recorded
		 * yet.
		 */
		virtual uint64_t findPos( uint64_t time) const = 0;

	signals:
		/**
		 * Emitted when an attribute that affects how the probe is drawn in the
		 * oscilloscope is changed.
		 */
		void displayAttributeChanged();

	protected:
		const int m_id;
		float m_drawPosition;
		uint64_t m_resetTime;
		QColor m_color;
};


/**
@author David Saxton
*/
class LogicProbeData : public ProbeData
{
	public:
		LogicProbeData( int id);
		~LogicProbeData() override { delete m_data; }

		/**
		 * Appends the data point to the set of data.
		 */
		void addDataPoint( LogicDataPoint data); /* {
			m_data->push_back(data);
		} */ // 2016.05.06 - moved to cpp

		void eraseData() override;
		uint64_t findPos( uint64_t time) const override;

		bool isEmpty() const { return m_data->size() == 0; }

	protected:
		std::vector<LogicDataPoint> *m_data;
		friend class OscilloscopeView;
};

/**
@author David Saxton
*/
class FloatingProbeData : public ProbeData
{
	public:
		enum Scaling { Linear, Logarithmic };

		FloatingProbeData( int id);

		/**
		 * Appends the data point to the set of data.
		 */
		void addDataPoint( float data) ; // { m_data->push_back(data); } // 2016.05.06 - moved to cpp
		/**
		 * Converts the insert position to a Simulator time.
		 */
		uint64_t toTime( uint64_t at) const;
		/**
		 * Sets the scaling to use in the oscilloscope display.
		 */
		void setScaling( Scaling scaling);
		/**
		 * @return the scaling used for the oscilloscope display.
		 */
		Scaling scaling() const { return m_scaling; }
		/**
		 * Sets the value to use as the upper absolute value in the display.
		 */
		void setUpperAbsValue( double upperAbsValue);
		/**
		 * @return the upper absolute value to use in the display.
		 */
		double upperAbsValue() const { return m_upperAbsValue; }
		/**
		 * Sets the value to use as the lower absolute value in the display
		 * (this is only used with logarithmic scaling).
		 */
		void setLowerAbsValue( double lowerAbsValue);
		/**
		 * @return the lower absolute value to use in the display (this is
		 * only used with logarithmic scaling).
		 */
		double lowerAbsValue() const { return m_lowerAbsValue; }

		void eraseData() override;
		uint64_t findPos( uint64_t time) const override;

		bool isEmpty() const { return m_data->size() == 0; }

	protected:
		Scaling m_scaling;
		double m_upperAbsValue;
		double m_lowerAbsValue;
		std::vector<float> *m_data;
		friend class OscilloscopeView;
};

#endif
