#pragma once

#include "Adept.hpp"
#include "Controls.hpp"
	
namespace Adept {


	//#########################################################################
	//############################ UnipolarFilter #############################
	//#########################################################################

	class UnipolarFilter
		#if defined(_ARMOR)
			: public Stuff::Signature
		#endif
	{
	public:
		enum Direction {
			IncreasingPositive,
			DecreasingPositive
		};

		enum LimitType {
			FixedLimits,
			PanningLimits,
			FloatingLimits
		};

		UnipolarFilter(
			Stuff::Scalar minimum,
			Stuff::Scalar maximum,
			Stuff::Scalar dead_band,
			Direction direction,
			LimitType limit
		);
		~UnipolarFilter();

		virtual Stuff::Scalar
			Update(Stuff::Scalar value);

		void
			Calibrate(
				Stuff::Scalar minimum,
				Stuff::Scalar maximum,
				Stuff::Scalar dead_band
			);
		virtual void
			BeginAlignment();
		virtual void
			EndAlignment();
		virtual void
			SetDeadBand(Stuff::Scalar percent);

		void
			TestInstance()
				{}

		Stuff::Scalar
			inputMin,
			inputMax;
		Stuff::AverageOf<Stuff::Scalar>
			inputAverage;

	protected:
		Stuff::Scalar
			lowerRange,
			lowerDeadband;
		Direction
			positiveDirection;
		LimitType
			limitType;
		Stuff::Scalar
			deadBand;
	};

	//#########################################################################
	//############################ ThrottleFilter #############################
	//#########################################################################

	class ThrottleFilter : public UnipolarFilter
	{
	public:
		ThrottleFilter(Stuff::Scalar minimum,Stuff::Scalar maximum,Stuff::Scalar dead_band) :
					UnipolarFilter (minimum,maximum,dead_band,UnipolarFilter::DecreasingPositive,UnipolarFilter::FixedLimits)
		{
			inputMin = minimum;
			inputMax = maximum;
			dead = dead_band;
		}

		~ThrottleFilter();

		virtual Stuff::Scalar	Update(Stuff::Scalar value)
		{
			return 1 - ((value - inputMin) / (inputMax - inputMin));
		}

		void Calibrate(Stuff::Scalar minimum,Stuff::Scalar maximum,Stuff::Scalar dead_band)
		{}
		virtual void BeginAlignment()
		{}
		virtual void EndAlignment()
		{}
		virtual void SetDeadBand(Stuff::Scalar value)
		{}

		void TestInstance()
				{}


	protected:
		Stuff::Scalar center,dead;
	};

	//#########################################################################
	//############################# BipolarFilter #############################
	//#########################################################################

	class BipolarFilter:
		public UnipolarFilter
	{
	public:
		BipolarFilter(
			Stuff::Scalar minimum,
			Stuff::Scalar center,
			Stuff::Scalar maximum,
			Stuff::Scalar dead_band,
			Direction direction,
			LimitType limit
		);
		~BipolarFilter();

		Stuff::Scalar
			Update(Stuff::Scalar value);

		void
			Calibrate(
				Stuff::Scalar minimum,
				Stuff::Scalar center,
				Stuff::Scalar maximum,
				Stuff::Scalar dead_band
			);
		void
			BeginAlignment();
		void
			EndAlignment();
		void
			SetDeadBand(Stuff::Scalar percent);

		Stuff::Scalar
			inputCenter;

	protected:
		Stuff::Scalar
			upperRange,
			upperDeadband;
	};

	//#########################################################################
	//############################## Joystick #################################
	//#########################################################################

	class Joystick
		#if defined(_ARMOR)
			: public Stuff::Signature
		#endif
	{
	public:
		enum HatStatus {
			NoHat,
			HasAHat
		};

		enum {
			XAxis,
			YAxis,
			ThrottleAxis,
			RudderAxis,
			HatAxis,
			AxisCount
		};

		Joystick(
			DWORD stick_id,
			Stuff::Scalar x_dead_band,
			Stuff::Scalar y_dead_band,
			Stuff::Scalar throttle_dead_band,
			Stuff::Scalar rudder_dead_band
		);
		~Joystick();

		void
			FilterInput(
				int axis,
				Stuff::Scalar value
			);
		void
			SetDeadBand(Stuff::Scalar percent);
		void
			SetXDeadBand(Stuff::Scalar percent);
		void
			SetYDeadBand(Stuff::Scalar percent);
		void
			SetThrottleDeadBand(Stuff::Scalar percent);
		void
			SetRudderDeadBand(Stuff::Scalar percent);

		int
			buttonCount;
		Stuff::Scalar
			axisValues[AxisCount];
		Stuff::Scalar	hatValue;
		Stuff::Scalar	lastHatValue;

		bool
			HasHat()
				{Check_Object(this); return hatStatus == HasAHat;}
		bool
			HasAxis(int axis)
				{Check_Object(this); return axis<AxisCount && axisFilters[axis]!=NULL;}

		bool IsForceFeedback (void)
		{ Check_Object(this); return m_ForceFeedback; }

		void
			AdoptFilter(
				int axis,
				UnipolarFilter *filter
			);

		void
			Update(
				int control_mask,
				Adept::ControlsUpdateManagerOf<Stuff::Scalar> *joystick_axis_group,
				Adept::ControlsUpdateManagerOf<int> *joystick_button_group,
				Adept::ControlsUpdateManagerOf<int> *joystick_hat
			);

		void
			TestInstance() const
				{}
		bool ShiftButton (void);
		void ShiftButton (int id)
		{ m_ShiftButton = id; }
		int StickID (void) const
		{ return joystickID; }

	protected:
		HatStatus hatStatus;
		UnipolarFilter
			*axisFilters[AxisCount];
		DWORD
			joystickID;
		GOSJoystickAxis
			joystickXInput,
			joystickYInput,
			throttleInput,
			rudderInput;
		bool *buttonValues;
		bool m_ForceFeedback;
		int m_ShiftButton;

		static int
			CalculateButtonCount(DWORD stick_id);
	};

}

