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

#pragma once

#include <map>
#include <string>

#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "Core/IPC_HLE/hci.h"

class CWII_IPC_HLE_Device_usb_oh1_57e_305;
class PointerWrap;

CWII_IPC_HLE_Device_usb_oh1_57e_305* GetUsbPointer();
void SetUsbPointer(CWII_IPC_HLE_Device_usb_oh1_57e_305* ptr);

class CBigEndianBuffer
{
public:
	CBigEndianBuffer(u8* pBuffer) : m_pBuffer(pBuffer) {}

	u8   Read8(u32 offset)  const       { return m_pBuffer[offset]; }
	u16  Read16(u32 offset) const       { return Common::swap16(*(u16*)&m_pBuffer[offset]); }
	u32  Read32(u32 offset) const       { return Common::swap32(*(u32*)&m_pBuffer[offset]); }

	void Write8(u32 offset, u8 data)    { m_pBuffer[offset] = data; }
	void Write16(u32 offset, u16 data)  { *(u16*)&m_pBuffer[offset] = Common::swap16(data); }
	void Write32(u32 offset, u32 data)  { *(u32*)&m_pBuffer[offset] = Common::swap32(data); }

	u8*  GetPointer(u32 offset)         { return &m_pBuffer[offset]; }

private:
	u8* m_pBuffer;
};


class CWII_IPC_HLE_WiiMote
{
public:
	CWII_IPC_HLE_WiiMote(CWII_IPC_HLE_Device_usb_oh1_57e_305* _pHost, int _Number, bdaddr_t _BD, bool ready = false);

	virtual ~CWII_IPC_HLE_WiiMote() {}

	void DoState(PointerWrap &p);

	// ugly Host handling....
	// we really have to clean all this code

	bool IsConnected() const { return m_ConnectionState == CONN_COMPLETE; }
	bool IsInactive() const { return m_ConnectionState == CONN_INACTIVE; }
	bool LinkChannel();
	void ResetChannels();
	void Activate(bool ready);
	void ExecuteL2capCmd(u8* _pData, u32 _Size); // From CPU
	void ReceiveL2capData(u16 scid, const void* _pData, u32 _Size); // From Wiimote

	void EventConnectionAccepted();
	void EventDisconnect();
	bool EventPagingChanged(u8 _pageMode);

	const bdaddr_t& GetBD() const { return m_BD; }
	const uint8_t* GetClass() const { return uclass; }
	u16 GetConnectionHandle() const { return m_ConnectionHandle; }
	const u8* GetFeatures() const { return features; }
	const char* GetName() const { return m_Name.c_str(); }
	u8 GetLMPVersion() const { return lmp_version; }
	u16 GetLMPSubVersion() const { return lmp_subversion; }
	u16 GetManufactorID() const { return 0x000F; }  // Broadcom Corporation
	const u8* GetLinkKey() const { return m_LinkKey; }

private:
	enum ConnectionState
	{
		CONN_INACTIVE = -1,
		CONN_READY,
		CONN_LINKING,
		CONN_COMPLETE
	};
	ConnectionState m_ConnectionState;

	bool m_HIDControlChannel_Connected;
	bool m_HIDControlChannel_ConnectedWait;
	bool m_HIDControlChannel_Config;
	bool m_HIDControlChannel_ConfigWait;
	bool m_HIDInterruptChannel_Connected;
	bool m_HIDInterruptChannel_ConnectedWait;
	bool m_HIDInterruptChannel_Config;
	bool m_HIDInterruptChannel_ConfigWait;

	// STATE_TO_SAVE
	bdaddr_t m_BD;
	u16 m_ConnectionHandle;
	uint8_t uclass[HCI_CLASS_SIZE];
	u8 features[HCI_FEATURES_SIZE];
	u8 lmp_version;
	u16 lmp_subversion;
	u8 m_LinkKey[HCI_KEY_SIZE];
	std::string m_Name;
	CWII_IPC_HLE_Device_usb_oh1_57e_305* m_pHost;

	struct SChannel
	{
		u16 SCID;
		u16 DCID;
		u16 PSM;

		u16 MTU;
		u16 FlushTimeOut;
	};

	typedef std::map<u32, SChannel> CChannelMap;
	CChannelMap m_Channel;

	bool DoesChannelExist(u16 _SCID)
	{
		return m_Channel.find(_SCID) != m_Channel.end();
	}

	void SendCommandToACL(u8 _Ident, u8 _Code, u8 _CommandLength, u8* _pCommandData);

	void SignalChannel(u8* _pData, u32 _Size);

	void SendConnectionRequest(u16 _SCID, u16 _PSM);
	void SendConfigurationRequest(u16 _SCID, u16 _pMTU = 0, u16 _pFlushTimeOut = 0);
	void SendDisconnectRequest(u16 _SCID);

	void ReceiveConnectionReq(u8 _Ident, u8* _pData, u32 _Size);
	void ReceiveConnectionResponse(u8 _Ident, u8* _pData, u32 _Size);
	void ReceiveDisconnectionReq(u8 _Ident, u8* _pData, u32 _Size);
	void ReceiveConfigurationReq(u8 _Ident, u8* _pData, u32 _Size);
	void ReceiveConfigurationResponse(u8 _Ident, u8* _pData, u32 _Size);

	// some new ugly stuff
	// should be inside the plugin
	void HandleSDP(u16 _SCID, u8* _pData, u32 _Size);
	void SDPSendServiceSearchResponse(u16 _SCID, u16 _TransactionID, u8* _pServiceSearchPattern, u16 _MaximumServiceRecordCount);

	void SDPSendServiceAttributeResponse(u16 _SCID, u16 TransactionID, u32 _ServiceHandle,
											u16 _StartAttrID, u16 _EndAttrID,
											u16 _MaximumAttributeByteCount, u8* _pContinuationState);
};
