/*! \file pibinarylog.h
 * \brief Binary log
*/
/*
	PIP - Platform Independent Primitives
	Class for write binary data to logfile, and read or playback this data
	Copyright (C) 2014  Andrey Bychkov work.a.b@yandex.ru

	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 3 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, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIBINARYLOG_H
#define PIBINARYLOG_H

#include "pifile.h"

#define PIBINARYLOG_VERSION 0x31
#define PIBINARYLOG_SIGNATURE_SIZE 6


class PIBinaryLog: public PIIODevice
{
	PIIODEVICE(PIBinaryLog)
public:
	PIBinaryLog();
    ~PIBinaryLog() {closeDevice();}

	//! \brief Play modes for \a PIBinaryLog
	enum PlayMode {
		PlayRealTime /*! Play in system real time */ ,
		PlayVariableSpeed /*! Play in software time with speed, set by \a setSpeed. Set by default */
	};
	
	
	//! Current \a PlayMode
	PlayMode playMode() const {return (PlayMode)(property("playMode").toInt());}
	
	//! Current directory where billogs wiil be saved
	PIString logDir() const {return property("logDir").toString();}
	
	//! Returns current file prefix
	PIString filePrefix() const {return property("filePrefix").toString();}
	
	//! Current LogDir, returns directory where billogs wiil be saved
	int defaultID() const {return property("defaultID").toInt();}
	
	//! Returns current play speed
	float playSpeed() const {return property("playSpeed").toFloat();}
	
	//! Returns if rapid start enabled
	bool rapidStart() const {return property("rapidStart").toBool();}
	
	
	//! Set \a PlayMode
	void setPlayMode(PlayMode mode) {setProperty("playMode", (int)mode);}
	
	//! Set path to directory where binlogs will be saved
	void setLogDir(const PIString & path) {setProperty("logDir", path);}
	
	//! Set file prefix, used to
	void setFilePrefix(const PIString & prefix) {setProperty("filePrefix", prefix);}
	
	//! Set defaultID, used in \a write function
	void setDefaultID(int id) {setProperty("defaultID", id);}
	
	//! If enabled BinLog \a ThreadedRead starts without delay for first record, e.g. first record will be readed immediately
	void setRapidStart(bool enabled) {setProperty("rapidStart", enabled);}
	
//	/** \brief Set play speed multiplyer, used only in \a PlayMode = \a PlayRealTime default value 1x.
//	 * If "speedX" > 0 than it use as speed increase by X times, else as speed decrease by X times.
//	 * While is running this function does nothing and returns false. If "speedX" is set, returns true.*/
//	bool setRealSpeedX(int speedX) {if (speedX == 0 || isRunning()) return false; real_speedX = speedX; return true;}
//	//! Returns current play speed multiplyer
//	float realSpeedX() const {return real_speedX;}
	//! Set play speed, used only if \a playMode = \a PlayVariableSpeed, default value 1.0
	void setPlaySpeed(float speed) {setProperty("playSpeed", speed);}
	
	
	//! Write one record to BinLog file, with ID = id, id must be greather than 0
	int writeBinLog(int id, PIByteArray data) {return writeBinLog(id, data.data(), data.size_s());}
	
	int writeBinLog(int id, const void * data, int size);
	
	//! Read one record from BinLog file, with ID = id, if id = 0 than any id will be readed
	PIByteArray readBinLog(int id = 0);
	
	int readBinLog(int id, void * read_to, int max_size);
	
	//! Return true, if position at the end of BinLog file
	bool isEnd() {if (!opened_) return true; return file.isEnd();}
	
	//! Returns if BinLog file is empty
	bool isEmpty() {return (file.size() <= PIBINARYLOG_SIGNATURE_SIZE + 1);}
	
	int lastReadedID() const {return lastrecord.id;}
	
	//! Read one message from binlog file, with ID contains in "filterID" or any ID, if "filterID" is empty
	int read(void *read_to, int max_size);
	
	//! Write one record to BinLog file, with ID = "defaultID"
	int write(const void * data, int size) {return writeBinLog(defaultID(), data, size);}
	
	//! Array of ID, that BinLog can read from binlog file, when use \a read function, or in \a ThreadedRead
	PIVector<int> filterID;
	
	//! Go to begin of BinLog file
	void restart();
	
	PIString constructFullPath() const;
	
	//! \handlers
	//! \{
	
	//! \fn PIString createNewFile()
	//! \brief Open device
	
	//! \}
	//! \events
	//! \{
	
	//! \fn void fileEnd()
	//! \brief Create new binlog file in \a logDir, if successful returns filename, else returns empty string.
	//! Filename is like  \a filePrefix + "yyyy_MM_dd__hh_mm_ss.binlog"
	
	//! \fn void fileError()
	//! \brief Raise on file creation error
	
	//! \}
	
	EVENT_HANDLER(PIString, createNewFile);
	EVENT(fileEnd)
	EVENT(fileError)
	
protected:
	PIString fullPathPrefix() const {return "binlog";}
	void configureFromFullPath(const PIString & full_path);
	bool openDevice();
	bool closeDevice();
	bool threadedRead(uchar *readed, int size);

private:
	struct BinLogRecord {
		int id;
		int size;
		PISystemTime timestamp;
		PIByteArray data;
	};
	
	bool writeFileHeader();
	bool checkFileHeader();
	BinLogRecord readRecord();
	
	PIFile file;
	BinLogRecord lastrecord;
	PISystemTime startlogtime;
	//BinLogRecord nextrecord;
	double play_time; //milliseconds
	//int real_speedX; // in X
	bool is_started, is_thread_ok;
	uchar binlog_sig[PIBINARYLOG_SIGNATURE_SIZE];
	
};

#endif // PIBINARYLOG_H
