/**
* // todo: standards
*/
#ifndef MAPLE_H
#define MAPLE_H

#include <string>
#include <kos.h>

using namespace std;

#define MAPLE_CONTROLLER_MAX 4
#define MAPLE_MEMCARD_PER_CONTROLLER_MAX 2
#define MAPLE_MEMCARD_MAX 8
#define MAPLE_CONTROLLER_PORT 0
#define MAPLE_CONTROLLER_PORT_1 1
#define MAPLE_CONTROLLER_PORT_2 2
#define MAPLE_SAVE_FILE_MAX 5
#define MAPLE_UNIT_MAX 6
#define MAPLE_MEMCARD_1 0
#define MAPLE_MEMCARD_2 1
#define MAPLE_VSTICK_LEFT 0
#define MAPLE_VSTICK_RIGHT 1
#define MAPLE_VSTICK_BUTTON_PAD 2

namespace MAPLE
{

// Forward declarations.
class cMapleController;
class CMapleVmu;

class cMapleVirtualStick
{
	private:

		cMapleController *m_pController;
		int16_t m_sCurrAxisX = 0;
		int16_t m_sCurrAxisY = 0;
		int16_t m_sTargetAxisX = 0;
		int16_t m_sTargetAxisY = 0;		
		int8_t m_sPrevDirection = 0;
		int8_t m_sCurrDirection = 0;								
		int16_t m_sButtonUp = 0;
		int16_t m_sButtonDown = 0;
		int16_t m_sButtonLeft = 0;
		int16_t m_sButtonRight = 0;
		int32_t m_lLastTime = 0;
		float m_fRate = 0.f;
		bool m_bInitialButtonPress = true;

	public:
	
	cMapleVirtualStick(cMapleController *pController, float fRate)
	{
		m_pController = pController;
		m_fRate = fRate;
	};

	void SetButtons(int16_t sButtonUp, int16_t sButtonDown, int16_t sButtonLeft, int16_t sButtonRight);	
	void GetAxis(int16_t* x, int16_t* y);
};

///
//
///
class cMapleModule
{
	protected:

	bool m_bConnected = false;		
	bool m_bError = false;
	bool m_bInitialised = false;	
	int8_t m_cControllerUnit = -1;
	int8_t m_cControllerId;

	maple_device_t* m_pDevice = nullptr;	
	cMapleController *m_pController = nullptr;

	public:
		
	uint32_t m_ulDisconnectedCycles = 0;

	enum
	{
		Error = -1,
		Uninitialised = 0,
		Connected = 1,
		Disconnected = 2,
	} States;

	virtual
	void Init(void) = 0;

	virtual
	int8_t Status(bool bClearError = false);

	inline __attribute__((always_inline)) int8_t GetUnit(void)
	{
		return m_cControllerUnit;
	}	
};

// todo:
class cMapleJump : public cMapleModule
{
	friend class cMapleController;

	private: 		
	int8_t m_cActiveEffect = 0;	
	int32_t m_lEffectStart = 0;
	int32_t m_lEffectEnd = 0;
	purupuru_effect_t effect;
	
	public:		
			
	enum
	{
		ShortWeakPulse = 1,
		LongWeakPulse,
		ShortStrongJolt,
		BulletHit,
		LowHealth
	} JumpEffects;

	static bool m_bRumbleOff;

	/// <summary>
	///	todo
	/// </summary>
	cMapleJump(cMapleController *pController)
	{		
		m_pController = pController;
		Init();
	}

	~cMapleJump()
	{

	}
		
	void Init(void);
	void RumbleOn(int8_t effect);	
	void RumbleOff(void);
	void RumbleUpdate(void);
};


/// <summary>
///	todo
/// </summary>
class cMapleVmu : public cMapleModule
{
	friend class cMapleController;

private:

	bool m_bHasLcd = false;
	bool m_bHasImg = false;
	uint8_t *m_pImgBuffer = nullptr;
	uint8_t *m_pIconImgBuffer = nullptr;
	uint16_t *m_pIconPalBuffer = nullptr;	
	
public:		
	
	static cMapleVmu* m_pActiveVmu;
	static char *m_pszSaveDescL;
	static char *m_pszSaveDescS;
	static char *m_pszSaveAppName;
	
	/// <summary>
	///	todo
	/// </summary>
	cMapleVmu(cMapleController *pController, int8_t cUnitId)
	{		
		m_pController = pController;		
		m_cControllerUnit = cUnitId;		
		Init();		
	}

	void Init(void);		
	int8_t GetSaveFileCount(void);
	int16_t GetFreeBlocks(void);
	void SetLCDImage(uint8_t *pImgData);	
	void SetSaveIcon(uint8_t *pSaveIconImgData, uint16_t* pSaveIconPalData);
	bool WriteData(const char *szFileName, uint8_t *pData, int16_t sSize);
	bool ReadData(const char *szFileName, uint8_t *pData, int16_t sSize);
	bool FileExists(const char *szFileName);
	inline __attribute__((always_inline)) int8_t GetUnit(void)
	{
		return m_cControllerUnit;
	}	
	
	/// <summary>
	///	todo
	/// </summary>
	static int8_t GetCount()
	{	
		int8_t count = 0;
		for (int8_t index = 0; index < MAPLE_MEMCARD_MAX; index++)
		{
			if (maple_enum_type(index, MAPLE_FUNC_MEMCARD))
			{
				count++;
			}
		}
		return count;
	}
};

//
// todo
//
class cMapleController 
{
	friend class cMapleModule;
	friend class cMapleVmu;
	friend class cMapleJump;

private :	
	
	char m_cControllerPort;
	int8_t m_cControllerId = -1;
	bool m_bExtended = false;
	bool m_bConnected = false;	
	cont_state_t* m_pState = NULL;	
	maple_device_t* m_pDeviceController = nullptr;	
	cMapleVirtualStick *m_pVirtualSticks[3];

public :
	
	enum
	{
		Controller = 0,
		TwinStick = 1,
	} ControllerTypes; // todo: needed?
	
	enum
	{
		ControllerConnected = 0,
		ControllerDisconnected = 1,
		VmuDisconnected = 2,		
	} ControllerStates;
		
	cMapleVmu *m_pMemCards[MAPLE_MEMCARD_PER_CONTROLLER_MAX];
	cMapleJump *m_pJumpPack;		
	int8_t m_cControlSchemeId = 0;
	uint32_t m_ulDisconnectedCycles = 0;

	void CanReboot(void);
	bool ButtonDown(uint16_t button);
	void GetAxis(int16_t* x, int16_t* y, int8_t cAxisNumber = 0);
	bool TriggerPulled(bool trigger);	
	bool GetStickAngle(float *fAngle, int8_t stick = 0);
	
	inline __attribute__((always_inline)) char Port(void)
	{
		return m_cControllerPort;
	}

	inline __attribute__((always_inline)) int8_t Id(void)
	{
		return m_cControllerId;
	}

	inline __attribute__((always_inline)) bool IsExtended(void)
	{
		return m_bExtended;
	}

	void Init();
	void InitVirtualStick(int8_t cStick, int8_t cRate, int16_t sButtonUp, int16_t sButtonDown, int16_t sButtonLeft, int16_t sButtonRight);
	int8_t Status();
		
	/// <summary>
	///	todo
	/// </summary>
	cMapleController(int8_t cControllerId)
	{		
		m_cControllerId = cControllerId;

		switch (cControllerId)
		{
			case 0:
			m_cControllerPort = 'a';
			break;
			case 1:
			m_cControllerPort = 'b';
			break;
			case 2:
			m_cControllerPort = 'c';
			break;
			case 3:
			m_cControllerPort = 'd';
			break;
		}

		Init();
		m_pMemCards[0] = new cMapleVmu(this, MAPLE_CONTROLLER_PORT_1);		
		m_pMemCards[1] = new cMapleVmu(this, MAPLE_CONTROLLER_PORT_2);
		m_pJumpPack = new cMapleJump(this);
		
		InitVirtualStick(MAPLE_VSTICK_LEFT, 5, CONT_DPAD_UP, CONT_DPAD_DOWN, CONT_DPAD_LEFT, CONT_DPAD_RIGHT);
		InitVirtualStick(MAPLE_VSTICK_RIGHT, 5, CONT_DPAD2_UP, CONT_DPAD2_DOWN, CONT_DPAD2_LEFT, CONT_DPAD2_RIGHT);
		InitVirtualStick(MAPLE_VSTICK_BUTTON_PAD, 5, CONT_Y, CONT_A, CONT_X, CONT_B);
	}

	//
	// todo: needed?
	//
	static int8_t GetCount()
	{
		int8_t count = 0;
		for (int8_t index = 0; index < MAPLE_CONTROLLER_MAX; index++)
		{
			if (maple_enum_type(index, MAPLE_FUNC_CONTROLLER))
			{
				count++;
			}
		}

		return count;
	}
};



}

#endif //MAPLE_H
