/*
	Copyright 2008-2015 DeSmuME team

	This file 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 file 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 the this software.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __MOVIE_H_
#define __MOVIE_H_

#include <vector>
#include <map>
#include <string>
#include <stdlib.h>
#include <time.h>

#include "utils/datetime.h"
#include "utils/guid.h"
#include "utils/md5.h"

struct UserInput;
class EMUFILE;

typedef struct
{
	int movie_version;					// version of the movie format in the file
	u32 num_frames;
	u32 rerecord_count;
	bool poweron;
	u32 emu_version_used;
	MD5DATA md5_of_rom_used;
	std::string name_of_rom_used;

	std::vector<std::wstring> comments;
	std::vector<std::string> subtitles;
} MOVIE_INFO;

enum EMOVIEMODE
{
	MOVIEMODE_INACTIVE = 0,
	MOVIEMODE_RECORD = 1,
	MOVIEMODE_PLAY = 2,
	MOVIEMODE_FINISHED = 3,
};

enum EMOVIECMD
{
	MOVIECMD_MIC = 1,
	MOVIECMD_RESET = 2,
	MOVIECMD_LID = 4,
};

//RLDUTSBAYXWEG

class MovieData;
class MovieRecord
{

public:
	u16 pad;
	
	union {
		struct {
			u8 x, y;
			u8 touch;
		};

		u32 padding;
	} touch;
	
	//misc commands like reset, etc.
	//small now to save space; we might need to support more commands later.
	//the disk format will support up to 64bit if necessary
	u8 commands;
	bool command_reset() const { return (commands&MOVIECMD_RESET)!=0; }
	bool command_microphone() const { return (commands&MOVIECMD_MIC)!=0; }
	bool command_lid() const { return (commands&MOVIECMD_LID)!=0; }

	void toggleBit(int bit)
	{
		pad ^= mask(bit);
	}

	void setBit(int bit)
	{
		pad |= mask(bit);
	}

	void clearBit(int bit)
	{
		pad &= ~mask(bit);
	}

	void setBitValue(int bit, bool val)
	{
		if(val) setBit(bit);
		else clearBit(bit);
	}

	bool checkBit(int bit)
	{
		return (pad & mask(bit))!=0;
	}

	bool Compare(MovieRecord& compareRec);
	void clear();
	
	void parse(EMUFILE* fp);
	bool parseBinary(EMUFILE* fp);
	void dump(EMUFILE* fp);
	void dumpBinary(EMUFILE* fp);
	void parsePad(EMUFILE* fp, u16& pad);
	void dumpPad(EMUFILE* fp, u16 pad);
	
	static const char mnemonics[13];

private:
	int mask(int bit) { return 1<<bit; }
};


class MovieData
{
public:
	MovieData();
	

	int version;
	int emuVersion;
	//todo - somehow force mutual exclusion for poweron and reset (with an error in the parser)
	
	u32 romChecksum;
	std::string romSerial;
	std::string romFilename;
	std::vector<u8> savestate;
	std::vector<u8> sram;
	std::vector<MovieRecord> records;
	std::vector<std::wstring> comments;
	
	int rerecordCount;
	Desmume_Guid guid;

	DateTime rtcStart;

	//was the frame data stored in binary?
	bool binaryFlag;

	int getNumRecords() { return records.size(); }

	class TDictionary : public std::map<std::string,std::string>
	{
	public:
		bool containsKey(std::string key)
		{
			return find(key) != end();
		}

		void tryInstallBool(std::string key, bool& val)
		{
			if(containsKey(key))
				val = atoi(operator [](key).c_str())!=0;
		}

		void tryInstallString(std::string key, std::string& val)
		{
			if(containsKey(key))
				val = operator [](key);
		}

		void tryInstallInt(std::string key, int& val)
		{
			if(containsKey(key))
				val = atoi(operator [](key).c_str());
		}

	};

	void truncateAt(int frame);
	void installValue(std::string& key, std::string& val);
	int dump(EMUFILE* fp, bool binary);
	void clearRecordRange(int start, int len);
	void insertEmpty(int at, int frames);
	
	static bool loadSavestateFrom(std::vector<u8>* buf);
	static void dumpSavestateTo(std::vector<u8>* buf, int compressionLevel);

	static bool loadSramFrom(std::vector<u8>* buf);
	//void TryDumpIncremental();

private:
	void installInt(std::string& val, int& var)
	{
		var = atoi(val.c_str());
	}

	void installBool(std::string& val, bool& var)
	{
		var = atoi(val.c_str())!=0;
	}
};

extern int currFrameCounter;
extern EMOVIEMODE movieMode;		//adelikat: main needs this for frame counter display
extern MovieData currMovieData;		//adelikat: main needs this for frame counter display

extern bool movie_reset_command;

bool FCEUI_MovieGetInfo(EMUFILE* fp, MOVIE_INFO& info, bool skipFrameCount);
void FCEUI_SaveMovie(const char *fname, std::wstring author, int flag, std::string sramfname, const DateTime &rtcstart);
const char* _CDECL_ FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, int _pauseframe); // returns NULL on success, errmsg on failure
void FCEUI_StopMovie();
void FCEUMOV_AddInputState();
void FCEUMOV_HandlePlayback();
void FCEUMOV_HandleRecording();
void mov_savestate(EMUFILE* fp);
bool mov_loadstate(EMUFILE* fp, int size);
void LoadFM2_binarychunk(MovieData& movieData, EMUFILE* fp, int size);
bool LoadFM2(MovieData& movieData, EMUFILE* fp, int size, bool stopAfterHeader);
extern bool movie_readonly;
extern bool ShowInputDisplay;
void FCEUI_MakeBackupMovie(bool dispMessage);
DateTime FCEUI_MovieGetRTCDefault();
void BinaryDataFromString(std::string &inStringData, std::vector<u8> *outBinaryData);
void ReplayRecToDesmumeInput(const MovieRecord &theRecord, UserInput *theInput);
void DesmumeInputToReplayRec(const UserInput &theInput, MovieRecord *theRecord);

#endif
