// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <string>

#include "Common/CommonTypes.h"

struct GCPadStatus;
class PointerWrap;
struct wiimote_key;

namespace WiimoteEmu
{
struct ReportFeatures;
}

// Per-(video )Movie actions

namespace Movie
{

// Enumerations and structs
enum PlayMode
{
	MODE_NONE = 0,
	MODE_RECORDING,
	MODE_PLAYING
};

// GameCube Controller State
#pragma pack(push,1)
struct ControllerState
{
	bool Start:1, A:1, B:1, X:1, Y:1, Z:1; // Binary buttons, 6 bits
	bool DPadUp:1, DPadDown:1,             // Binary D-Pad buttons, 4 bits
	     DPadLeft:1, DPadRight:1;
	bool L:1, R:1;                         // Binary triggers, 2 bits
	bool disc:1;                           // Checks for disc being changed
	bool reset:1;                          // Console reset button
	bool reserved:2;                       // Reserved bits used for padding, 2 bits

	u8   TriggerL, TriggerR;               // Triggers, 16 bits
	u8   AnalogStickX, AnalogStickY;       // Main Stick, 16 bits
	u8   CStickX, CStickY;                 // Sub-Stick, 16 bits
};
static_assert(sizeof(ControllerState) == 8, "ControllerState should be 8 bytes");
#pragma pack(pop)

// Global declarations
extern bool g_bDiscChange, g_bClearSave, g_bReset;
extern u64 g_titleID;

extern u64 g_currentFrame, g_totalFrames;
extern u64 g_currentLagCount;
extern u64 g_currentInputCount, g_totalInputCount;
extern std::string g_discChange;

#pragma pack(push,1)
struct DTMHeader
{
	u8 filetype[4];         // Unique Identifier (always "DTM"0x1A)

	char gameID[6];         // The Game ID
	bool bWii;              // Wii game

	u8  numControllers;     // The number of connected controllers (1-4)

	bool bFromSaveState;    // false indicates that the recording started from bootup, true for savestate
	u64 frameCount;         // Number of frames in the recording
	u64 inputCount;         // Number of input frames in recording
	u64 lagCount;           // Number of lag frames in the recording
	u64 uniqueID;           // (not implemented) A Unique ID comprised of: md5(time + Game ID)
	u32 numRerecords;       // Number of rerecords/'cuts' of this TAS
	u8  author[32];         // Author's name (encoded in UTF-8)

	u8  videoBackend[16];   // UTF-8 representation of the video backend
	u8  audioEmulator[16];  // UTF-8 representation of the audio emulator
	u8  md5[16];            // MD5 of game iso

	u64 recordingStartTime; // seconds since 1970 that recording started (used for RTC)

	bool bSaveConfig;       // Loads the settings below on startup if true
	bool bSkipIdle;
	bool bDualCore;
	bool bProgressive;
	bool bDSPHLE;
	bool bFastDiscSpeed;
	u8   CPUCore;           // 0 = interpreter, 1 = JIT, 2 = JITIL
	bool bEFBAccessEnable;
	bool bEFBCopyEnable;
	bool bSkipEFBCopyToRam;
	bool bEFBCopyCacheEnable;
	bool bEFBEmulateFormatChanges;
	bool bUseXFB;
	bool bUseRealXFB;
	u8   memcards;
	bool bClearSave;        // Create a new memory card when playing back a movie if true
	u8   bongos;
	bool bSyncGPU;
	bool bNetPlay;
	bool bPAL60;
	u8   language;
	u8   reserved[11];      // Padding for any new config options
	u8   discChange[40];    // Name of iso file to switch to, for two disc games.
	u8   revision[20];      // Git hash
	u32  DSPiromHash;
	u32  DSPcoefHash;
	u64  tickCount;	        // Number of ticks in the recording
	u8   reserved2[11];     // Make heading 256 bytes, just because we can
};
static_assert(sizeof(DTMHeader) == 256, "DTMHeader should be 256 bytes");

#pragma pack(pop)

void FrameUpdate();
void InputUpdate();
void Init();

void SetPolledDevice();

bool IsRecordingInput();
bool IsRecordingInputFromSaveState();
bool IsJustStartingRecordingInputFromSaveState();
bool IsJustStartingPlayingInputFromSaveState();
bool IsPlayingInput();
bool IsMovieActive();
bool IsReadOnly();
u64  GetRecordingStartTime();

bool IsConfigSaved();
bool IsDualCore();
bool IsProgressive();
bool IsPAL60();
bool IsSkipIdle();
bool IsDSPHLE();
bool IsFastDiscSpeed();
int  GetCPUMode();
u8   GetLanguage();
bool IsStartingFromClearSave();
bool IsUsingMemcard(int memcard);
bool IsSyncGPU();
void SetGraphicsConfig();
void GetSettings();
bool IsNetPlayRecording();

bool IsUsingPad(int controller);
bool IsUsingWiimote(int wiimote);
bool IsUsingBongo(int controller);
void ChangePads(bool instantly = false);
void ChangeWiiPads(bool instantly = false);

void DoFrameStep();
void SetReadOnly(bool bEnabled);

void SetFrameSkipping(unsigned int framesToSkip);
void FrameSkipping();

bool BeginRecordingInput(int controllers);
void RecordInput(GCPadStatus* PadStatus, int controllerID);
void RecordWiimote(int wiimote, u8 *data, u8 size);

bool PlayInput(const std::string& filename);
void LoadInput(const std::string& filename);
void ReadHeader();
void PlayController(GCPadStatus* PadStatus, int controllerID);
bool PlayWiimote(int wiimote, u8* data, const struct WiimoteEmu::ReportFeatures& rptf, int ext, const wiimote_key key);
void EndPlayInput(bool cont);
void SaveRecording(const std::string& filename);
void DoState(PointerWrap &p);
void CheckMD5();
void GetMD5();
void Shutdown();
void CheckPadStatus(GCPadStatus* PadStatus, int controllerID);
void CheckWiimoteStatus(int wiimote, u8* data, const struct WiimoteEmu::ReportFeatures& rptf, int ext, const wiimote_key key);

std::string GetInputDisplay();

// Done this way to avoid mixing of core and gui code
typedef void(*GCManipFunction)(GCPadStatus*, int);
typedef void(*WiiManipFunction)(u8*, WiimoteEmu::ReportFeatures, int, int, wiimote_key);

void SetGCInputManip(GCManipFunction);
void SetWiiInputManip(WiiManipFunction);
void CallGCInputManip(GCPadStatus* PadStatus, int controllerID);
void CallWiiInputManip(u8* core, WiimoteEmu::ReportFeatures rptf, int controllerID, int ext, const wiimote_key key);
}
