/////////////////////////////////////////////////////////////////////////////
// File:        wiimotes_types.h
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Copyright:   (C) 2011 Cesar Mauri Loba - CREA Software Systems
// 
//  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 WIIMOTES_TYPES
#define WIIMOTES_TYPES

#include "spcore/basetypeimpl.h"

namespace mod_wiimotes {

/**
 Class to store and notify wiimotes status
*/
class CTypeWiimotesStatusContents : public spcore::CTypeAny {
public:
	enum Status { IDLE= 0, CONNECTING, CONNECTED };
	
	static inline const char* getTypeName() { return "wiimotes_status"; }

	virtual Status GetGeneralStatus() const { return m_generalStatus; }

	// Query the number of max devices
	virtual unsigned int GetMaxCount() const { return MAXWIIMOTES; }

	// Query the number of connected wiimotes
	virtual unsigned int GetConnectedCount() const { return m_connectedCount; }
	
	// Asks wheter a particular wiimote is connected
	virtual bool IsConnected(unsigned int wiimote_n) const;

	// Accessors to query which extensions are avalable
	virtual bool HasNunchuk(unsigned int wiimote_n) const;
	virtual bool HasClassicPad(unsigned int wiimote_n) const;
	virtual bool HasGuitarHero(unsigned int wiimote_n) const;
	virtual bool HasBalanceBoard(unsigned int wiimote_n) const;
	virtual bool HasMotionPlus(unsigned int wiimote_n) const;

	// Accessors to query which features are enabled
	virtual bool IsAccelerometersEnabled(unsigned int wiimote_n) const;
	virtual bool IsMotionPlusEnabled(unsigned int wiimote_n) const;
	virtual bool IsNunchuckEnabled(unsigned int wiimote_n) const;

protected:
	CTypeWiimotesStatusContents(int id); 

	virtual bool CopyTo ( CTypeAny& dst, bool ) const {
		assert (dst.GetTypeID()== this->GetTypeID());
		*(static_cast<CTypeWiimotesStatusContents*>(&dst))= *this;
		return true;
	}
	
private:
	//
	// Constants
	// 
	enum { MAXWIIMOTES= 4 };
	enum Extension { 
		NONE				= 0, 
		NUNCHUK				= 0x1, 
		CLASSIC				= 0x2, 
		GUITAR_HERO			= 0x4, 
		BALANCE_BOARD		= 0x10, 
		MOTION_PLUS			= 0x20, 
		WIIMOTE_CONNECTED	= 0x8000 
	};

	/**
		Flags to tell whether accelerometers and/or gyros are enabled. We disable them when not used to save battery.
	*/
	enum EnabledFeatures { ENABLED_NONE= 0, ENABLED_ACC= 0x1, ENABLED_MP= 0x2, ENABLED_NUNCHUCK= 0x4 };

	//
	// Attributes
	//
	Status m_generalStatus;
	unsigned int m_connectedCount;
	Extension m_wiimoteStatus[MAXWIIMOTES];
	unsigned int m_enabledFeatures[MAXWIIMOTES];

	//
	// Private opetarions
	//
	void SetGeneralStatus(Status e) { m_generalStatus= e; }

	void SetConnectedCount(int c) { assert (c<=MAXWIIMOTES); m_connectedCount= c; }

	void SetIsConnected(unsigned int wiimote_n, bool v);

	// Sets one extension and disables the others
	void SetExtension (unsigned int wiimote_n, Extension e);

	// Sets/unsets enabled features
	void SetEnabledFeature (unsigned int wiimote_n, EnabledFeatures f) {
		assert (wiimote_n< MAXWIIMOTES);
		assert (!(f & 0xFFF8));

		unsigned int flags= m_enabledFeatures[wiimote_n];
		flags|= f;		
		m_enabledFeatures[wiimote_n]= flags;
	}

	void UnsetEnabledFeature (unsigned int wiimote_n, EnabledFeatures f) {
		assert (wiimote_n< MAXWIIMOTES);
		assert (!(f & 0xFFF8));
		
		unsigned int flags= m_enabledFeatures[wiimote_n];
		flags&= ~f;
		m_enabledFeatures[wiimote_n]= flags;
	}

	void Reset();

	friend class WiiuseThread;
	friend class WiiuseThreadController;
	friend class WiimotesConfig;
	friend class WiimotesInput;
};

typedef spcore::SimpleType<CTypeWiimotesStatusContents> CTypeWiimotesStatus;


/**
 Class to store and notify accelerometer events
*/
class CTypeWiimotesAccelerometerContents : public spcore::CTypeAny {
public:
	static inline const char* getTypeName() { return "wiimotes_accelerometers"; }

	/**
		Get force on the X axis
		@return force on X axis in G's
	*/
	virtual float GetForceX() const { return m_forceX; }

	/**
		Set force on the X axis in G's
	*/
	virtual void SetForceX(float fx) { m_forceX= fx; }

	/**
		Get force on the Y axis
		@return force on Y axis in G's
	*/
	virtual float GetForceY() const { return m_forceY; }

	/**
		Set force on the Y axis in G's
	*/
	virtual void SetForceY(float fy) { m_forceY= fy; }

	/**
		Get force on the Z axis
		@return force on Z axis in G's
	*/
	virtual float GetForceZ() const { return m_forceZ; }

	/**
		Set force on the Z axis in G's
	*/
	virtual void SetForceZ(float fz) { m_forceZ= fz; }

	/**
		GetRoll		
		@return roll in degrees (-180..180)
	*/
	virtual float GetRoll() const { return m_roll; }

	/**
		SetRoll		
		Set roll in degrees (-180..180)
	*/
	virtual void SetRoll(float roll) { 
		assert (-180.0f<= roll && roll<= 180.0f);
		m_roll= roll;
	}

	/**
		GetPitch		
		Return pitch in degrees (-180..180)
	*/
	virtual float GetPitch() const { return m_pitch; }

	/**
		SetPitch	
		Set pitch in degrees (-180..180)
	*/
	virtual void SetPitch(float pitch) { 
		assert (-180.0f<= pitch && pitch<= 180.0f);
		m_pitch= pitch;
	}

	/**
		IsOrientationAccurate
		@return whether roll and pitch are accurate enough to use it.
	*/
	virtual bool IsOrientationAccurate() const { 
		return (m_forceX>= -1.1f && m_forceX<= 1.1f &&
			m_forceY>= -1.1f && m_forceY<= 1.1f &&
			m_forceZ>= -1.1f && m_forceZ<= 1.1f);
	}

protected:
	CTypeWiimotesAccelerometerContents(int id) : spcore::CTypeAny(id) {
		m_forceX= m_forceY= m_forceZ= m_roll= m_pitch= 0.0f;
	}

	virtual bool CopyTo ( CTypeAny& dst, bool ) const {
		assert (dst.GetTypeID()== this->GetTypeID());
		*(static_cast<CTypeWiimotesAccelerometerContents*>(&dst))= *this;

		return true;
	}

private:
	float m_forceX, m_forceY, m_forceZ, m_roll, m_pitch;
};

typedef spcore::SimpleType<CTypeWiimotesAccelerometerContents> CTypeWiimotesAccelerometer;

/**
 Class to store and notify button press events
*/
class CTypeWiimotesButtonsContents : public spcore::CTypeAny {
public:
	static inline const char* getTypeName() { return "wiimotes_buttons"; }

	virtual bool IsPressedA() const;
	virtual bool IsPressedB() const;
	virtual bool IsPressedOne() const;
	virtual bool IsPressedTwo() const;
	virtual bool IsPressedMinus() const;
	virtual bool IsPressedPlus() const;
	virtual bool IsPressedHome() const;
	virtual bool IsPressedUp() const;
	virtual bool IsPressedDown() const;
	virtual bool IsPressedLeft() const;
	virtual bool IsPressedRight() const;
	
	// nunchuck
	virtual bool IsPressedC() const;
	virtual bool IsPressedZ() const;

	// any button
	virtual bool IsPressedAny() const;

protected:
	CTypeWiimotesButtonsContents(int id) : spcore::CTypeAny(id) {
		m_wiimoteButtons= 0;
		m_nunchuckButtons= 0;
	}

	virtual bool CopyTo ( CTypeAny& dst, bool ) const {
		assert (dst.GetTypeID()== this->GetTypeID());
		*(static_cast<CTypeWiimotesButtonsContents*>(&dst))= *this;
		return true;
	}

private:
	void SetWiimoteButtons (unsigned short mask) { m_wiimoteButtons= mask; }
	unsigned short GetWiimoteButtons () const { return m_wiimoteButtons; }

	void SetNunchuckButtons (unsigned char mask) { m_nunchuckButtons= mask; }
	unsigned char GetNunchuckButtons () const { return m_nunchuckButtons; }

	unsigned short m_wiimoteButtons;
	unsigned char  m_nunchuckButtons;

	friend class WiimotesInput;
};

typedef spcore::SimpleType<CTypeWiimotesButtonsContents> CTypeWiimotesButtons;

/**
 Class to store and notify button balance board status
*/

#define BB_MIN_WEIGHT 8.0f
class CTypeWiimotesBalanceBoardContents : public spcore::CTypeAny {
public:
	static inline const char* getTypeName() { return "wiimotes_balance_board"; }

	/**
		Get top left corner weight in kg
	*/
	virtual float GetTopLeft() const { return m_topLeft; }

	/**
		Set top left corner weight in kg
	*/
	virtual void SetTopLeft(float w) { m_topLeft= w; }

	/**
		Get top right corner weight in kg
	*/
	virtual float GetTopRight() const { return m_topRight; }

	/**
		Set top right corner weight in kg
	*/
	virtual void SetTopRight(float w) { m_topRight= w; }

	/**
		Get bottom left corner weight in kg
	*/
	virtual float GetBottomLeft() const { return m_bottomLeft; }

	/**
		Set bottom left corner weight in kg
	*/
	virtual void SetBottomLeft(float w) { m_bottomLeft= w; }

	/**
		Get bottom right corner weight in kg
	*/
	virtual float GetBottomRight() const { return m_bottomRight; }

	/**
		Set bottom right corner weight in kg
	*/
	virtual void SetBottomRight(float w) { m_bottomRight= w; }

	/**
		Get total weight in kg
	*/
	virtual float GetTotal() const {
		return m_topLeft + m_topRight + m_bottomLeft + m_bottomRight;
	}

	/**
		Get centre of mass for the X axis (ranging from -1 to 1). 
		If there is not enough weight on, returns 0		
	*/
	virtual float GetCenterOfMassX() const {
		float total= GetTotal();
		if (total< BB_MIN_WEIGHT) return 0.0f;
		return ((m_topRight + m_bottomRight) - (m_topLeft+ m_bottomLeft)) / total;
	}

	/**
		Get centre of mass for the Y axis (ranging from -1 to 1). 
		If there is not enough weight on, returns 0		
	*/
	virtual float GetCenterOfMassY() const {
		float total= GetTotal();
		if (total< BB_MIN_WEIGHT) return 0.0f;
		return ((m_topRight + m_topLeft) - (m_bottomRight + m_bottomLeft)) / total;
	}

protected:
	CTypeWiimotesBalanceBoardContents(int id) : spcore::CTypeAny(id) {
		m_topLeft= m_topRight= m_bottomLeft= m_bottomRight= 0;
	}

	virtual bool CopyTo ( CTypeAny& dst, bool ) const {
		assert (dst.GetTypeID()== this->GetTypeID());
		*(static_cast<CTypeWiimotesBalanceBoardContents*>(&dst))= *this;
		return true;
	}

private:
	float m_topLeft, m_topRight, m_bottomLeft, m_bottomRight;
};

typedef spcore::SimpleType<CTypeWiimotesBalanceBoardContents> CTypeWiimotesBalanceBoard;

/**
 Class to store and notify motion plus status
*/
class CTypeWiimotesMotionPlusContents : public spcore::CTypeAny {
public:
	static inline const char* getTypeName() { return "wiimotes_motion_plus"; }

	/**
		Get X speed (pitch) in deg/s
	*/
	virtual float GetXSpeed() const { return m_xSpeed; }

	/**
		Set X speed (pitch) in deg/s
	*/
	virtual void SetXSpeed(float sx) { m_xSpeed= sx; }

	/**
		Get Y speed (roll) in deg/s
	*/
	virtual float GetYSpeed() const { return m_ySpeed; }

	/**
		Set Y speed (roll) in deg/s
	*/
	virtual void SetYSpeed(float sy) { m_ySpeed= sy; }

	/**
		Get Z speed (yaw) in deg/s
	*/
	virtual float GetZSpeed() const { return m_zSpeed; }

	/**
		Set Z speed (yaw) in deg/s
	*/
	virtual void SetZSpeed(float sz) { m_zSpeed= sz; }

protected:
	CTypeWiimotesMotionPlusContents (int id) : spcore::CTypeAny(id)
	{
		m_xSpeed= m_ySpeed= m_zSpeed = 0.0f;
	}

	virtual bool CopyTo ( CTypeAny& dst, bool ) const {
		assert (dst.GetTypeID()== this->GetTypeID());
		*(static_cast<CTypeWiimotesMotionPlusContents*>(&dst))= *this;
		return true;
	}

private:
	float m_xSpeed, m_ySpeed, m_zSpeed;
};

typedef spcore::SimpleType<CTypeWiimotesMotionPlusContents> CTypeWiimotesMotionPlus;

};

#endif