#include "wiimote.h"

#include <iostream>
#include <sigc++/bind.h>

#include <pbd/xml++.h>
#include <ardour/session.h>

#include "i18n.h"


using namespace ARDOUR;
using namespace PBD;

void wiimote_control_protocol_cwiid_callback(cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], struct timespec *t);

uint16_t WiimoteControlProtocol::button_state = 0;

WiimoteControlProtocol::WiimoteControlProtocol ( Session & session) 
	: ControlProtocol ( session, "Wiimote"),
	  main_thread_quit (false),
	  restart_discovery (false),
	  callback_thread_registered_for_ardour (false),
	  wiimote_handle (0)
{
	main_thread = Glib::Thread::create( sigc::mem_fun(*this, &WiimoteControlProtocol::wiimote_main), true);
}

WiimoteControlProtocol::~WiimoteControlProtocol()
{
	main_thread_quit = true;
	slot_cond.signal();
	main_thread->join();

	if (wiimote_handle) {
		cwiid_close(wiimote_handle);
	}
	std::cerr << "Wiimote: closed" << std::endl;
}


bool 
WiimoteControlProtocol::probe()
{
	return true;
}

void
WiimoteControlProtocol::wiimote_callback(cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], struct timespec *t)
{
	int i;
	uint16_t b;

	if (!callback_thread_registered_for_ardour) {
		register_thread("Wiimote Control Protocol");
		callback_thread_registered_for_ardour = true;
	}

        for (i=0; i < mesg_count; i++)
	{
		if (mesg[i].type == CWIID_MESG_ERROR) {	
			std::cerr << "Wiimote: disconnect" << std::endl;
			restart_discovery = true;
			slot_cond.signal();
			return;
		}

		if (mesg[i].type != CWIID_MESG_BTN) continue;

		// what buttons are pressed down which weren't pressed down last time
		b = (mesg[i].btn_mesg.buttons ^ button_state) & mesg[i].btn_mesg.buttons;

		button_state = mesg[i].btn_mesg.buttons;
	
		// if B is pressed down
		if (button_state & CWIID_BTN_B) {
			if (b & CWIID_BTN_A) { // B is down and A is pressed
				access_action("Transport/ToggleRollForgetCapture");
			}

			if (b & CWIID_BTN_LEFT) {
				access_action("Editor/playhead-to-previous-region-boundary");
			}
			if (b & CWIID_BTN_RIGHT) {
				access_action("Editor/playhead-to-next-region-boundary");
			}
			if (b & CWIID_BTN_UP) {
				next_marker();
			}
			if (b & CWIID_BTN_DOWN) {
				prev_marker();
			}

			if (b & CWIID_BTN_HOME) {
				access_action("Editor/add-location-from-playhead");
			}

			if (b & CWIID_BTN_MINUS) {	
				access_action("Transport/GotoStart");
			}
			
			if (b & CWIID_BTN_PLUS) {
				access_action("Transport/GotoEnd");
			}

			continue;
		}


		if (b & CWIID_BTN_A) {
			access_action("Transport/ToggleRoll");
		}

		if (b & CWIID_BTN_1) { // 1
			access_action("Editor/track-record-enable-toggle");
		}
		if (b & CWIID_BTN_2) { // 2
			rec_enable_toggle();
		}

		// d-pad
		if (b & CWIID_BTN_LEFT) { // left
			access_action("Editor/nudge-playhead-backward");
		}
		if (b & CWIID_BTN_RIGHT) { // right
			access_action("Editor/nudge-playhead-forward");
		}
		if (b & CWIID_BTN_DOWN) { // down
			access_action("Editor/select-next-route");
		}
		if (b & CWIID_BTN_UP) { // up
			access_action("Editor/select-prev-route");
		}


		if (b & CWIID_BTN_PLUS) { // +
			access_action("Editor/temporal-zoom-in");
		}
		if (b & CWIID_BTN_MINUS) { // -
			access_action("Editor/temporal-zoom-out");
		}
		if (b & CWIID_BTN_HOME) { // "home"
			// no op, yet. any suggestions?
			access_action("Editor/playhead-to-edit");
		}

	}
}

void
WiimoteControlProtocol::update_led_state()
{
	ENSURE_WIIMOTE_THREAD(sigc::mem_fun(*this, &WiimoteControlProtocol::update_led_state));

	uint8_t state = 0;

	if (session->transport_rolling()) {
		state |= CWIID_LED1_ON;
	}

	if (session->actively_recording()) {
		state |= CWIID_LED4_ON;
	}

	cwiid_set_led(wiimote_handle, state);
}

void
WiimoteControlProtocol::wiimote_main()
{
	bdaddr_t bdaddr;
	unsigned char rpt_mode = 0;
	register_thread("Wiimote Discovery and Callback Thread");

wiimote_discovery:

	std::cerr << "Wiimote: discovering, press 1+2" << std::endl;

 	while (!wiimote_handle && !main_thread_quit) {
		bdaddr = (bdaddr_t) {{0, 0, 0, 0, 0, 0}};
		callback_thread_registered_for_ardour = false;
		wiimote_handle = cwiid_open(&bdaddr, 0);

		if (!wiimote_handle && !main_thread_quit) {
			sleep(1); 
			// We don't know whether the issue was a timeout or a configuration 
			// issue
		}
	}

	if (main_thread_quit) {
		// The corner case where the wiimote is bound at the same time as
		// the control protocol is destroyed
		if (wiimote_handle) {
			cwiid_close(wiimote_handle);
		}
		wiimote_handle = 0;

		std::cerr << "Wiimote Control Protocol stopped before connected to a wiimote" << std::endl;
		return;
	}

	std::cerr << "Wiimote: connected" << std::endl;
	WiimoteControlProtocol::button_state = 0;

	if (cwiid_enable(wiimote_handle, CWIID_FLAG_REPEAT_BTN)) {
		std::cerr << "cwiid_enable(), error" << std::endl;
		cwiid_close(wiimote_handle);
		wiimote_handle = 0;
		return;
	}
	if (cwiid_set_mesg_callback(wiimote_handle, wiimote_control_protocol_cwiid_callback)) {
		std::cerr << "cwiid_set_mesg_callback(), couldn't connect callback" << std::endl;
		cwiid_close(wiimote_handle);
		wiimote_handle = 0;
		return;
	} 
	if (cwiid_command(wiimote_handle, CWIID_CMD_RPT_MODE, CWIID_RPT_BTN)) {
		std::cerr << "cwiid_command(), RPT_MODE error" << std::endl;
		cwiid_close(wiimote_handle);
		wiimote_handle = 0;
		return;
	}

	rpt_mode |= CWIID_RPT_BTN;
	cwiid_enable(wiimote_handle, CWIID_FLAG_MESG_IFC);
	cwiid_set_rpt_mode(wiimote_handle, rpt_mode);

	transport_state_conn = session->TransportStateChange.connect(sigc::mem_fun(*this, &WiimoteControlProtocol::update_led_state));
	record_state_conn = session->RecordStateChanged.connect(sigc::mem_fun(*this, &WiimoteControlProtocol::update_led_state));

	std::cerr << "Wiimote: initialization done, waiting for callbacks / quit" << std::endl;

	while (!main_thread_quit) {
		slot_mutex.lock();
		while (slot_list.empty() && !main_thread_quit && !restart_discovery)
			slot_cond.wait(slot_mutex);

		if (main_thread_quit) {
			slot_mutex.unlock();
			break;
		}

		if (restart_discovery) {
			std::cerr << "Wiimote: closing wiimote and restarting discovery" << std::endl;
			if (wiimote_handle) {
				cwiid_close(wiimote_handle);
				wiimote_handle = 0;
			}
			slot_mutex.unlock();
			restart_discovery = false;
			goto wiimote_discovery;
		}

		sigc::slot<void> call_me = *slot_list.begin();
		slot_list.pop_front();
		slot_mutex.unlock();

		call_me();
	}


	std::cerr << "Wiimote: main thread stopped" << std::endl;
}


int
WiimoteControlProtocol::set_active (bool yn)
{
	// Let's not care about this just yet
	return 0;

}

XMLNode&
WiimoteControlProtocol::get_state()
{
	XMLNode *node = new XMLNode ("Protocol");
        node->add_property (X_("name"), _name);
        node->add_property (X_("feedback"), "0");

	return *node;
}

int
WiimoteControlProtocol::set_state(const XMLNode& node)
{
	return 0;
}
