/*
 * DataRecorder.h
 *
 */

#ifndef DATARECORDER_H_
#define DATARECORDER_H_

#include "DataRecorderStdFormat.h"

#include <stdint.h>

class RecorderManager;

class DataRecorderRaw
{
public:
	enum  type_t { type_unknown=1, type_struct=1, type_numeric=2, type_mixed=3};

	typedef uint64_t marker_t;

	DataRecorderRaw(const char* name=0, DataRecorderRaw::marker_t marker=0);

	DataRecorderRaw(int enabled, const char* name=0, DataRecorderRaw::marker_t marker=0);

    /*virtual*/ ~DataRecorderRaw();

	void setName(const char* name);
	void setMarker(marker_t marker);


	bool saveRaw(const void* data, unsigned int size, unsigned int t=type_unknown);
    bool saveRawNoSataFlush(const void* data, unsigned int size, unsigned int t=type_unknown);
    bool saveRawPreFlush(const void* data, unsigned int size, unsigned int t=type_unknown);

	bool saveRaw2(const void* data1, unsigned int size1, const void* data2, unsigned int size2, unsigned int t=type_unknown);

	void setCacheSynchRequired(bool required=true);
	bool cacheSynchRequired() const;

	void setEnable(bool ena);

	static unsigned int blockSizeOverhead();

protected:
	void saveTerminated();

	void* copyRaw(volatile void* dst, const void* src, unsigned int len);

	void* copyPrepare(DataRecorderRaw& d, unsigned int size, unsigned int type, unsigned int meta_size=0);

    void registerMe();

    //WANING: noCopyPrepare() - use with care, assuming dst already hold the data and has room to hold the block descriptor at dst[-sizeof(block)]
    void* noCopyPrepare(DataRecorderRaw& d, void* dst, unsigned int size, unsigned int type, unsigned int meta_size=0);

public:
	const char* name;
	marker_t mark;

	int enabled;

	unsigned int rec_counter;
	unsigned int rec_saved;

	unsigned int s_cursor;

	mutable int rec_reserved_id_;

	unsigned int cache_synch_required;

	friend class RecorderManager;

private:
	DataRecorderRaw(const DataRecorderRaw&);//= delete;
	void operator=(const DataRecorderRaw&);//= delete;
};


template<typename T_> class DataRecorder: public DataRecorderRaw
{
public:
	typedef T_ type_t;
	typedef DataRecorder<type_t> DataRecorderType;

	DataRecorder(const char* name, marker_t marker): DataRecorderRaw(name, marker) {}
	DataRecorder(int enabled, const char* name, marker_t marker): DataRecorderRaw(enabled, name, marker) {}

	bool save(const T_& d)
	{
		return saveRaw(&d, sizeof d, type_struct);
	}

    bool savePreFlush(const T_& d)
    {
        return saveRawPreFlush(&d, sizeof d, type_struct);
    }

	void saveWithoutSataFlush(const T_& d)
	{
        saveRawNoSataFlush(&d, sizeof d, type_struct);
	}
};

template<typename T1_, typename T2_> class DataRecorder2: public DataRecorderRaw
{
public:
	typedef T1_ type1_t;
	typedef T2_ type2_t;
	typedef DataRecorder<type_t> DataRecorderType;

	DataRecorder2(const char* name, marker_t marker): DataRecorderRaw(name, marker) {}
	DataRecorder2(int enabled, const char* name, marker_t marker): DataRecorderRaw(enabled, name, marker) {}

	bool save(const T1_& d1, const T2_& d2)
	{
		return saveRaw2(&d1, sizeof d1, &d2, sizeof d2, type_struct);
	}
};


class DataRecorderSignal: public DataRecorderRaw
{
public:
    enum { k_sign_marker=0x4E474953};

	class Geometry
	{
	public:
		DataRecorderNumericFormat::geometry_t geometry;

		Geometry(unsigned int rbins, unsigned int pri)
		{
			geometry.dx=geometry.dx_stride=rbins;
			geometry.dy=geometry.dy_stride=pri;
			geometry.dz=geometry.dz_stride=1;
		}
	};

    typedef DataRecorderNumericFormat::info_t info_t;

    DataRecorderSignal(bool enabled, unsigned int fmt, const char* name, unsigned int marker=0):
        DataRecorderRaw((int)enabled, name, marker)
    {
    	DataRecorderSignalInit(fmt);
    }

    DataRecorderSignal(unsigned int fmt, const char* name, unsigned int marker=0):
        DataRecorderRaw(name, marker)
    {
    	DataRecorderSignalInit(fmt);
    }

    void DataRecorderSignalInit(unsigned int fmt)
    {
        info.fmt=fmt;
        info.sign_marker[0]=k_sign_marker;//0xDA7ADA7A;
        info.sign_marker[1]=k_sign_marker;//0xDA7ADA7A;
        if ((fmt==DataRecorderNumericFormat::fmt_complex) || (fmt==DataRecorderNumericFormat::fmt_iq32))
            info.sample_size=sizeof(float)*2;
        else
            info.sample_size=sizeof(uint32_t);
        info.geometry.dx=0;
        info.geometry.dy=0;
        info.geometry.dz=0;
        info.geometry.dx_stride=0;
        info.geometry.dy_stride=0;
        info.geometry.dz_stride=0;
    }

    static unsigned int signalSizeOverhead()
    {
        return DataRecorderRaw::blockSizeOverhead()+sizeof info;
    }

    unsigned int noCopySave(void* data, const Geometry& g)
    {
        unsigned int size=g.geometry.dx_stride*g.geometry.dy_stride*g.geometry.dz_stride*info.sample_size;
        size+=sizeof info;

        void* p=noCopyPrepare(*this, data, size, type_numeric, sizeof info);
        if (!p)
            return 0;

        info.geometry=g.geometry;

        ++rec_saved;
        return size;
    }

    bool save(const void* data, const Geometry& g)
    {
        unsigned int size=g.geometry.dx_stride*g.geometry.dy_stride*g.geometry.dz_stride*info.sample_size;
        void* p=copyPrepare(*this, size+sizeof info, type_numeric, sizeof info);
        if (!p)
            return false;

        info.geometry=g.geometry;

        p=copyRaw(p, &info, sizeof info);
        copyRaw(p, data, size);

        ++rec_saved;
        return true;
    }

    bool save(const void* data, DataRecorderNumericFormat::format_t fmt, unsigned int dx, unsigned int dy=1, unsigned int dz=1)
    {
        unsigned int size=dx*dy*dz*info.sample_size;
        void* p=copyPrepare(*this, size+sizeof info, type_numeric, sizeof info);
        if (!p)
            return false;

        info.fmt=fmt;
        info.geometry.dx=dx;
        info.geometry.dy=dy;
        info.geometry.dz=dz;
        info.geometry.dx_stride=dx;
        info.geometry.dy_stride=dy;
        info.geometry.dz_stride=dz;

        p=copyRaw(p, &info, sizeof info);
        copyRaw(p, data, size);

        ++rec_saved;
        return true;
    }

    info_t info;

private:
    DataRecorderSignal(const DataRecorderSignal&);//= delete;
	void operator=(const DataRecorderSignal&);//= delete;
};

template<typename T_> class DataRecorderResultAndSignal: public DataRecorderSignal
{
public:
    DataRecorderResultAndSignal(unsigned int fmt, const char* name, unsigned int marker=0):
        DataRecorderSignal(fmt, name, marker)
    {
    }
    bool save(const T_& str, const void* data, const Geometry& g)
    {
        unsigned int size=sizeof(str)+(g.geometry.dx_stride*g.geometry.dy_stride*g.geometry.dz_stride*info.sample_size);
        void* p=copyPrepare(*this, size+sizeof info, type_numeric, sizeof info);
        if (!p)
            return false;

        info.geometry=g.geometry;

        p=copyRaw(p, &info, sizeof info);
        p=copyRaw(p, data, size);
        p=copyRaw(p, &str, sizeof str);
        ++rec_saved;
        return true;
    }
};

//
class RecorderMemory
{
public:
    static void* getMemory(void* buffer, unsigned int& size);
};

class Recorder
{
public:
    static void initialize();

    static void configure(void* buffer, unsigned int size);

    static unsigned int start();
    static unsigned int stop();

    static unsigned int sataStart();

    static bool recording();
    static bool sataRecording();

    struct FileInfo
    {
    	unsigned int status;
    	const char* msg;
    	unsigned int size;
    	const void* data;
    };

    static void setStreamMode(bool enable);

    static void setBatchCounter(unsigned int batch, unsigned int time_us);
    static void setSignalCounter(unsigned int signal, unsigned int time_us);

    static FileInfo getAllData();

    static FileInfo getData();

    static FileInfo getStream();

    static void sataFlush();

    struct EntityInfo
    {
    	unsigned int items;
    	DataRecorderRaw** instances;
    };

    static EntityInfo getEntities();

    //static void registerRecordingPoint(DataRecorderRaw& recPoint);

    //static bool validateRecorderPoint(const DataRecorderRaw& recPoint);

};

#endif /* DATARECORDER_H_ */
