/***********************************************************************************************************************
*                                                                                                                      *
* libscopehal v0.1                                                                                                     *
*                                                                                                                      *
* Copyright (c) 2012-2021 Andrew D. Zonenberg and contributors                                                         *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

/**
	@file
	@author Andrew D. Zonenberg
	@brief Declaration of Filter
 */

#ifndef Filter_h
#define Filter_h

#include "OscilloscopeChannel.h"
#include "FlowGraphNode.h"

/**
	@brief Abstract base class for all filters and protocol decoders
 */
class Filter	: public OscilloscopeChannel
				, public FlowGraphNode
{
public:

	//Add new categories to the end of this list to maintain ABI compatibility with existing plugins
	enum Category
	{
		CAT_ANALYSIS,		//Signal integrity analysis
		CAT_BUS,			//Buses
		CAT_CLOCK,			//Clock stuff
		CAT_MATH,			//Basic math functions
		CAT_MEASUREMENT,	//Measurement functions
		CAT_MEMORY,			//Memory buses
		CAT_SERIAL,			//Serial communications
		CAT_MISC,			//anything not otherwise categorized
		CAT_POWER,			//Power analysis
		CAT_RF				//Frequency domain analysis (FFT etc) and other RF stuff
	};

	Filter(
		OscilloscopeChannel::ChannelType type,
		const std::string& color,
		Category cat,
		const std::string& kernelPath = "",
		const std::string& kernelName = "");
	virtual ~Filter();

	/**
		@brief Specifies whether we're using an auto-generated name or not
	 */
	void UseDefaultName(bool use)
	{
		m_usingDefault = use;
		if(use)
			SetDefaultName();
	}

	bool IsUsingDefaultName()
	{ return m_usingDefault; }

	virtual bool IsScalarOutput();

	virtual void Refresh() =0;

	virtual void AddRef();
	virtual void Release();

	size_t GetRefCount()
	{ return m_refcount; }

	//Get all currently existing filters
	static std::set<Filter*> GetAllInstances()
	{ return m_filters; }

	//Set all currently existing filters to the dirty state
	static void SetAllFiltersDirty()
	{
		for(auto f : m_filters)
			f->SetDirty();
	}

	/**
		@brief Clears any integrated data from past triggers (e.g. eye patterns).

		Most decoders shouldn't have to do anything for this.
	 */
	virtual void ClearSweeps();

	virtual void SetDefaultName() =0;

	Category GetCategory()
	{ return m_category; }

	/**
		@brief Return true (default) if this decoder should be overlaid on top of the original waveform.

		Return false (override) if it should be rendered as its own line.
	 */
	virtual bool IsOverlay();

	virtual bool NeedsConfig() =0;	//false if we can automatically do the decode from the signal w/ no configuration

	void RefreshIfDirty();
	void RefreshInputsIfDirty();

	void SetDirty()
	{ m_dirty = true; }

	/**
		@brief Gets the display name of this protocol (for use in menus, save files, etc). Must be unique.
	 */
	virtual std::string GetProtocolDisplayName() =0;

	/**
		@brief Serialize this decoder's configuration to a string
	 */
	virtual std::string SerializeConfiguration(IDTable& table, size_t indent = 8);

	virtual void LoadParameters(const YAML::Node& node, IDTable& table);

	/**
		@brief Standard colors for protocol decode overlays.

		Do not change ordering, add new items to the end only.
	 */
	enum FilterColor
	{
		COLOR_DATA,			//protocol data
		COLOR_CONTROL,		//generic control sequences
		COLOR_ADDRESS,		//addresses or device IDs
		COLOR_PREAMBLE,		//preambles, start bits, and other constant framing
		COLOR_CHECKSUM_OK,	//valid CRC/checksum
		COLOR_CHECKSUM_BAD,	//invalid CRC/checksum
		COLOR_ERROR,		//malformed traffic
		COLOR_IDLE,			//downtime between frames

		STANDARD_COLOR_COUNT
	};

	static Gdk::Color m_standardColors[STANDARD_COLOR_COUNT];

	/**
		@brief Returns true if this filter uses the clFFT library.

		Needed to enable some workarounds for broken OpenCL stacks.
	 */
	virtual bool UsesCLFFT();

protected:

	///Group used for the display menu
	Category m_category;

	///Indicates if our output is out-of-sync with our input
	bool m_dirty;

	///Indicates we're using an auto-generated name
	bool m_usingDefault;

	bool VerifyAllInputsOK(bool allowEmpty = false);
	bool VerifyInputOK(size_t i, bool allowEmpty = false);
	bool VerifyAllInputsOKAndAnalog();

	///Gets the timestamp of the next event (if any) on a waveform
	int64_t GetNextEventTimestamp(WaveformBase* wfm, size_t i, size_t len, int64_t timestamp)
	{
		if(i+1 < len)
			return wfm->m_offsets[i+1];
		else
			return timestamp;
	}

	///Advance the waveform to a given timestamp
	void AdvanceToTimestamp(WaveformBase* wfm, size_t& i, size_t len, int64_t timestamp)
	{
		while( ((i+1) < len) && (wfm->m_offsets[i+1] <= timestamp) )
			i ++;
	}

	AnalogWaveform* SetupOutputWaveform(WaveformBase* din, size_t stream, size_t skipstart, size_t skipend);
	DigitalWaveform* SetupDigitalOutputWaveform(WaveformBase* din, size_t stream, size_t skipstart, size_t skipend);

public:
	//Text formatting for CHANNEL_TYPE_COMPLEX decodes
	virtual Gdk::Color GetColor(int i);
	virtual std::string GetText(int i);

	//Helpers for sub-sample interoplation
	static float InterpolateTime(AnalogWaveform* cap, size_t a, float voltage);
	static float InterpolateTime(AnalogWaveform* p, AnalogWaveform* n, size_t a, float voltage);
	static float InterpolateValue(AnalogWaveform* cap, size_t index, float frac_ticks);

	//Helpers for more complex measurements
	//TODO: create some process for caching this so we don't waste CPU time
	static float GetMinVoltage(AnalogWaveform* cap);
	static float GetMaxVoltage(AnalogWaveform* cap);
	static float GetBaseVoltage(AnalogWaveform* cap);
	static float GetTopVoltage(AnalogWaveform* cap);
	static float GetAvgVoltage(AnalogWaveform* cap);
	static std::vector<size_t> MakeHistogram(AnalogWaveform* cap, float low, float high, size_t bins);

	//Samples a digital channel on the edges of another channel.
	//The two channels need not be the same sample rate.
	static void SampleOnAnyEdges(DigitalWaveform* data, DigitalWaveform* clock, DigitalWaveform& samples);
	static void SampleOnAnyEdges(DigitalBusWaveform* data, DigitalWaveform* clock, DigitalBusWaveform& samples);
	static void SampleOnRisingEdges(DigitalWaveform* data, DigitalWaveform* clock, DigitalWaveform& samples);
	static void SampleOnRisingEdges(DigitalBusWaveform* data, DigitalWaveform* clock, DigitalBusWaveform& samples);
	static void SampleOnFallingEdges(DigitalWaveform* data, DigitalWaveform* clock, DigitalWaveform& samples);

	//Find interpolated zero crossings of a signal
	static void FindZeroCrossings(AnalogWaveform* data, float threshold, std::vector<int64_t>& edges);

	//Find edges in a signal (discarding repeated samples)
	static void FindZeroCrossings(DigitalWaveform* data, std::vector<int64_t>& edges);
	static void FindRisingEdges(DigitalWaveform* data, std::vector<int64_t>& edges);
	static void FindFallingEdges(DigitalWaveform* data, std::vector<int64_t>& edges);

	static void ClearAnalysisCache();

	//Checksum helpers
	static uint32_t CRC32(std::vector<uint8_t>& bytes, size_t start, size_t end);

protected:
	//Common text formatting
	virtual std::string GetTextForAsciiChannel(int i, size_t stream);

#ifdef HAVE_OPENCL

	//OpenCL state
	cl::Program* m_program;
	cl::Kernel* m_kernel;

#endif

public:
	typedef Filter* (*CreateProcType)(const std::string&);
	static void DoAddDecoderClass(const std::string& name, CreateProcType proc);

	static void EnumProtocols(std::vector<std::string>& names);
	static Filter* CreateFilter(const std::string& protocol, const std::string& color = "#ffffff");

protected:
	//Class enumeration
	typedef std::map< std::string, CreateProcType > CreateMapType;
	static CreateMapType m_createprocs;

	//Object enumeration
	static std::set<Filter*> m_filters;

	//Caching
	static std::mutex m_cacheMutex;
	static std::map<std::pair<WaveformBase*, float>, std::vector<int64_t> > m_zeroCrossingCache;
};

#define PROTOCOL_DECODER_INITPROC(T) \
	static Filter* CreateInstance(const std::string& color) \
	{ \
		return new T(color); \
	} \
	virtual std::string GetProtocolDisplayName() \
	{ return GetProtocolName(); }

#define AddDecoderClass(T) Filter::DoAddDecoderClass(T::GetProtocolName(), T::CreateInstance)

#endif
