/*
 *  psychlops_io_keyboard.cpp
 *  Psychlops Standard Library (Universal)
 *
 *  Last Modified 2006/01/05 by Kenchi HOSOKAWA
 *  (C) 2006 Kenchi HOSOKAWA, Kazushi MARUYA and Takao SATO
 */


#include "psychlops_io_hid.h"
#include "../ApplicationInterfaces/psychlops_code_exception.h"
#include "../graphic/psychlops_g_shape.h"
#include "../graphic/psychlops_g_canvas.h"
#include "../math/psychlops_math.h"

#define PSYCHLOPS_HID_API_PLATFORM
#include "../../platform/psychlops_platform_selector.h"

namespace Psychlops {


////////	Class HumanInterfaceDevice	////////

	int HumanInterfaceDevice::pushRepeatFrames = 20, HumanInterfaceDevice::pushRepeatInterval = 3;

	void HumanInterfaceDevice::initialize() {
		APIHIDProperties::initialize();
		//Keyboard::initialize();
		Mouse::initialize();
		startListener();
	}
	void HumanInterfaceDevice::finalize() {
		stopListener();
		APIHIDProperties::finalize();
	}
	bool HumanInterfaceDevice::get(Keyboard::Key code, Keyboard::KeyState state) {
		return Keyboard::get(code,state);
	}
	bool HumanInterfaceDevice::get(Mouse::Button code, Mouse::ButtonState state) {
		return Mouse::get(code,state);
	}

	void HumanInterfaceDevice::wait(Keyboard::Key code, Keyboard::KeyState state) {
		Keyboard::wait(code,state);
	}

	void HumanInterfaceDevice::refresh() {
		Keyboard::refresh();
		Mouse::refresh();
	}

	void HumanInterfaceDevice::startListener() {
		if(!runningHIDListner) APIHIDProperties::startListener();
		runningHIDListner = true;
	}
	void HumanInterfaceDevice::stopListener() {
		if(runningHIDListner) APIHIDProperties::stopListener();
		runningHIDListner = false;
	}

	bool HumanInterfaceDevice::runningHIDListner = false;
	HumanInterfaceDevice::States HumanInterfaceDevice::state[3];

	typedef HumanInterfaceDevice HID;


	HID::ButtonStateHolder::ButtonStateHolder(const Mouse::Button &t)
	{
		target = (Mouse::Button*)&t;
		pressed_ = pushed_ = released_ = hadpressed_ = false;
	}
	void HID::ButtonStateHolder::ask()
	{
		hadpressed_ = pressed_;
		pressed_ = target->pressed();
		if(hadpressed_==false && pressed_==true) pushed_ = true;
		if(hadpressed_==true && pressed_==false) released_ = true;
	}
	bool HID::ButtonStateHolder::pressed()
	{
		ask();
		return pressed_;
	}
	bool HID::ButtonStateHolder::pushed()
	{
		ask();
		bool tmp = pushed_;
		pushed_ = false;
		return tmp;
	}
	bool HID::ButtonStateHolder::released()
	{
		ask();
		bool tmp = released_;
		released_ = false;
		return tmp;
	}


////////	Class Keyboard	////////

	const Keyboard::Key
		Keyboard::one(0),Keyboard::two(1),Keyboard::three(2),Keyboard::four(3),Keyboard::five(4),Keyboard::six(5),Keyboard::seven(6),Keyboard::eight(7),Keyboard::nine(8),Keyboard::zero(9),
		Keyboard::a(10),Keyboard::b(11),Keyboard::c(12),Keyboard::d(13),Keyboard::e(14),Keyboard::f(15),Keyboard::g(16),Keyboard::h(17),Keyboard::i(18),Keyboard::j(19),Keyboard::k(20),Keyboard::l(21),Keyboard::m(22),Keyboard::n(23),Keyboard::o(24),Keyboard::p(25),Keyboard::q(26),Keyboard::r(27),Keyboard::s(28),Keyboard::t(29),Keyboard::u(30),Keyboard::v(31),Keyboard::w(32),Keyboard::x(33),Keyboard::y(34),Keyboard::z(35),
		Keyboard::comma(36),Keyboard::period(37),Keyboard::slash(38),
		Keyboard::pad0(39),Keyboard::pad1(40),Keyboard::pad2(41),Keyboard::pad3(42),Keyboard::pad4(43),Keyboard::pad5(44),Keyboard::pad6(45),Keyboard::pad7(46),Keyboard::pad8(47),Keyboard::pad9(48),
		Keyboard::rtn(49),Keyboard::spc(50),Keyboard::esc(51),Keyboard::tab(52),Keyboard::up(53),Keyboard::down(54),Keyboard::left(55),Keyboard::right(56),
		//	key modulator
		Keyboard::shift(100),Keyboard::shift_r(101),Keyboard::ctrl(102),Keyboard::alt(103),
		//  not recommended
		Keyboard::hyphen(59),Keyboard::equal(60),Keyboard::backslash(61),Keyboard::caret(62),
		Keyboard::underscore(63),Keyboard::semicolon(64),Keyboard::colon(65),Keyboard::leftbracket(66),Keyboard::rightbracket(67),Keyboard::at(68),   // because these keys laid on different location in differnt keyboard-lacalization.
		Keyboard::padcomma(69),Keyboard::padperiod(70),Keyboard::padenter(71),Keyboard::padplus(72),Keyboard::padminus(73),Keyboard::padasterisk(74),Keyboard::padslash(75),Keyboard::padequal(76),Keyboard::padclear(77),	//  because these keys are OS-specific.
		Keyboard::nfer(78),Keyboard::xfer(79)   // because these keys exist only on Japanese keyboard
	;

	Keyboard::Key::Key(const int code_) : code(code_) {}
	bool Keyboard::Key::pressed() const {
		return Keyboard::get(*this, Keyboard::pressed);
	}
	bool Keyboard::Key::pushed() const {
		return Keyboard::get(*this, Keyboard::pushed);
	}
	bool Keyboard::Key::released() const {
		return Keyboard::get(*this, Keyboard::released);
	}
	bool Keyboard::Key::get(KeyState state) const {
		switch(state) {
		case Keyboard::pressed:
			return pressed();
		case Keyboard::released:
			return released();
		case Keyboard::pushed:
			return pushed();
		default:
			throw new Exception("Keyboard.get: unknown key state.");
		}
	}




	void Keyboard::update() {
		APIHIDProperties::update();
	}
	void Keyboard::refresh() {
		APIHIDProperties::update();
		APIHIDProperties::refresh_needed = true;
		refresh_internal();
	}
	void Keyboard::refresh_internal() {
		for(int i=0;i<128;i++) {
			HID::state[HID::pushed].key[i] = 0;
			HID::state[HID::released].key[i] = 0;
		}
	}

	bool Keyboard::get(Key key, KeyState state) {
		APIHIDProperties::update();
		return APIHIDProperties::get(key, state);
	}

    //  obsolete
        bool Keyboard::getKey(Key key, KeyState state) {
            return APIHIDProperties::get(key, state);
        }

        void Keyboard::wait(Key key, bool needrelease) {
        	exit(50);
        	/*
            for(;;) {
                if(!Input::runningInputListner) { updateKeyState(); }
                if(get(code,pressed)) { break; }
            }
            if(needrelease) {
                if(!Input::runningInputListner) { updateKeyState(); }
                for(;;) {
                    if(!get(code,pressed)) { break; }
                }
            }
            */
        }

        void Keyboard::allReleased() {
        	exit(50);
        	/*
            bool loop = true;
            while(loop) {
                if(!Input::runningInputListner) { updateKeyState(); }
                loop = false;
                for(int i=0; i<4; i++ ) {
                    if(key[pressed][i]!=0) { loop=true; }
                }
            }
            */
        }


////////	Class Mouse	////////

	::Psychlops::Point Mouse::wheelDelta, Mouse::lastPoint;

	const Mouse::Button Mouse::left(0), Mouse::right(1), Mouse::middle(2), Mouse::any(-1);
	Mouse::Button::Button(const int code_) : code(code_) {}
	bool Mouse::Button::pressed() const {
		return Mouse::get(*this, Mouse::pressed);
	}
	bool Mouse::Button::pushed() const {
		return Mouse::get(*this, Mouse::pushed);
	}
	bool Mouse::Button::released() const {
		return Mouse::get(*this, Mouse::released);
	}
	bool Mouse::Button::get(ButtonState state) const {
		switch(state) {
		case Mouse::pressed:
			return pressed();
		case Mouse::released:
			return released();
		case Mouse::pushed:
			return pushed();
		default:
			throw new Exception("Mouse.get: unknown key state.");
		}
	}


	Mouse::POSITIONVAL::POSITIONVAL() {}
	Mouse::POSITIONVAL::operator int() {
		Mouse::update();
		return val_;
	}
	int Mouse::POSITIONVAL::operator =(int val) {
		val_ = val;
		APIHIDProperties::setPointerPosition(uniX.val_, uniY.val_);
		return val_;
	}
	Mouse::POSITIONVAL Mouse::uniX, Mouse::uniY;

	Mouse::POSITION::operator Psychlops::Point() {
		Mouse::update();
		return Point(uniX.val_, uniY.val_);
	}
	Psychlops::Point Mouse::POSITION::operator =(Psychlops::Point po) {
		uniX.val_ = (int)Math::round(po.x);
		uniY.val_ = (int)Math::round(po.y);
		APIHIDProperties::setPointerPosition(uniX.val_, uniY.val_);
		return Point(uniX.val_, uniY.val_);
	}
	Mouse::POSITION Mouse::uniPosition;

	Mouse::DEFPOSITIONVALX::operator int() { return Display::the_canvas->mouse().x; }
	int Mouse::DEFPOSITIONVALX::operator =(int val) { return (int)Display::the_canvas->mouse(Point(val, y)).x; }
	Mouse::DEFPOSITIONVALX Mouse::x;
	Mouse::DEFPOSITIONVALY::operator int() { return Display::the_canvas->mouse().y; }
	int Mouse::DEFPOSITIONVALY::operator =(int val) { return (int)Display::the_canvas->mouse(Point(x, val)).y; }
	Mouse::DEFPOSITIONVALY Mouse::y;
	Mouse::DEFPOSITION::operator ::Psychlops::Point() { if(Display::the_canvas==0) return Point(0,0); else return Display::the_canvas->mouse(); }
	::Psychlops::Point Mouse::DEFPOSITION::operator =(::Psychlops::Point val) { if(Display::the_canvas==0) return Point(0,0); else return Display::the_canvas->mouse(val); }
	Mouse::DEFPOSITION Mouse::position;

	bool Mouse::show_local_pointer_;
	Figure* Mouse::pointer_figure;
	Group default_pointer_figure;
	Rectangle hb,vb,hf,vf;

	void Mouse::initialize() {
		show_local_pointer_ = false;
		default_pointer_figure.append(hb);
		default_pointer_figure.append(vb);
		default_pointer_figure.append(hf);
		default_pointer_figure.append(vf);
		setPointer(1);
	}

	void Mouse::update() {
		APIHIDProperties::update();
	}
	void Mouse::refresh() {
		APIHIDProperties::update();
		APIHIDProperties::refresh_needed = true;
		refresh_internal();
	}
	void Mouse::refresh_internal() {
		for(int i=0; i<4; i++) for(int j=0; j<8; j++) HID::state[i].button[j] = false;
	}

	bool Mouse::get(Button button, ButtonState state) {
		return APIHIDProperties::get(button, (Mouse::ButtonState)state);
	}
	::Psychlops::Point Mouse::getWheelDelta() {
		Point p = wheelDelta;
		wheelDelta.set(0,0,0);
		return p;
	}

	void Mouse::drawPointer(Point p, Drawable &target) {
		/*
		Rectangle hb(11,3), vb(3,11), hf(9,1), vf(1,9);
		if(show_local_pointer_) {
			hb.centering(p).draw(Color::black);
			vb.centering(p).draw(Color::black);
			hf.centering(p).draw(Color::white);
			vf.centering(p).draw(Color::white);
		}
		*/
		pointer_figure->centering(p).draw(target);
	}
	void Mouse::show() {
		show_local_pointer_ = true;
	}
	void Mouse::hide() {
		show_local_pointer_ = false;
	}
	bool  Mouse::showLocalPointer()
	{
		return show_local_pointer_;
	}

	void setDefaultPointer(int size)
	{
		hf.set(9*size, size).shift(-9*size/2, -size/2);
		hf.fill = Color::white;
		vf.set(size, 9*size).shift(-size/2, -9*size/2);
		vf.fill = Color::white;
		hb.set(2+9*size, 2+size).shift(-(2+9*size)/2, -(2+size)/2);
		hb.fill = Color::black;
		vb.set(2+size, 2+9*size).shift(-(2+size)/2, -(2+9*size)/2);
		vb.fill = Color::black;
	}
	void Mouse::setPointer(int size)
	{
		setDefaultPointer(size);
		pointer_figure = &default_pointer_figure;
	}
	void Mouse::setPointer(Figure &fig)
	{
		pointer_figure = &fig;
	}



}	/*	<- namespace Psycholops 	*/

