//-------------------------------------------------------------------------------------------------
// <copyright file="LWSerialPortLua.cpp" company="LiquidRobotics">
//  Copyright (c) Liquid Robotics Corporation, confidential and proprietary.  All rights reserved. 
// </copyright>
//
// <summary>
//	Implementation of LWSerialPortLua, an implementation of metatable methods exposed
//	to lua as the return value from SMC:NewSerialPort()  It represents the methods of a
//	(simplified) serial port.
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------------------------

#include "LWConcreteCmd.h"
#include "LWSerialPortLua.h"

using namespace std;

namespace LiquidRobotics {
namespace LuaWrapper {

const LWSerialPortLua::RegType LWSerialPortLua::registrations[] =
{
	{ "GetID",				&LWSerialPortLua::L_GetID },
	{ "SetBaudRate", 		&LWSerialPortLua::L_SetBaudRate },
	{ "SetBreak",			&LWSerialPortLua::L_SetBreak },
	{ "SetBreakTime",		&LWSerialPortLua::L_SetBreakTime },
	{ "Transmit", 			&LWSerialPortLua::L_Transmit },
};

const size_t LWSerialPortLua::registrationCount = sizeof(registrations)/sizeof(registrations[0]);

LWSerialPortLua::LWSerialPortLua(LuaInstance& _L)
: pController(NULL)
{
}

LWSerialPortLua::~LWSerialPortLua()
{
	serListenerConnection.reset();

	if (serListener)
	{
		try { serListener->Shutdown(); } catch(...) { }
		serListener.reset();
	}

	if (sp && pController)
		pController->CloseSerialPortConfig(configName);

	sp.reset();
}

void LWSerialPortLua::Initialize(LWControllerBase0* _ctrl, const string& _configName, const string& _luaInstanceName, const string& _portInstanceName)
{
	pController = _ctrl;
	luaInstanceName = _luaInstanceName;
	portInstanceName = _portInstanceName;

	bool closeConfig = false;

	try
	{
		pController->OpenSerialPortConfig(_configName, sp, serListener);
		closeConfig = true;
		serListenerConnection = serListener->Subscribe(boost::bind(&LWSerialPortLua::OnSerialDataReceived, this, _1, _2));
		//serListener->Initialize();
		configName = _configName;
	}
	catch(...)
	{
		serListenerConnection.reset();
		serListener.reset();
		sp.reset();
		if (closeConfig) pController->CloseSerialPortConfig(_configName);
		throw;
	}
}

string LWSerialPortLua::GetClassName()
{
	return "SMCSerialPort";
}

int LWSerialPortLua::GetRegistrations(const RegType*& regBase)
{
	regBase = registrations;
	return registrationCount;
}

void LWSerialPortLua::PreRegistration(LuaInstance& L)
{
}

int LWSerialPortLua::L_GetID(LuaInstance& L)
{
	L.pushlstring(GetID());
	return 1;
}

int LWSerialPortLua::L_SetBaudRate(LuaInstance& L)
{
	if (L.isnumber(-1))
	{
		const LuaInstance::Number num = L.tonumber(-1);
		const ISerialPort::BaudRate newBaudRate = ISerialPort::NumToBaudRate((unsigned long) floor(num));

		SetBaudRate(newBaudRate);
	}

	return 0;
}

int LWSerialPortLua::L_SetBreak(LuaInstance& L)
{
	SetBreak();
	return 0;
}

int LWSerialPortLua::L_SetBreakTime(LuaInstance& L)
{
	if (L.isnumber(-1))
	{
		const LuaInstance::Number num = L.tonumber(-1);

		if (num <= 0)
			SetBreak();
		else
			SetBreak((int) floor(num));
	}

	return 0;
}

int LWSerialPortLua::L_Transmit(LuaInstance& L)
{
	LuaInstance::StringPtr s;

	if (L.isstring(-1))
		s = L.tolstring(-1);

	else if (L.isnumber(-1))
	{
		L.pushnumber(L.tonumber(-1));
		s = L.tolstring(-1);
		L.pop(1);
	}

	if (s)
	{
		ByteArray ba((const ByteArray::byte*) s->data(), s->length());
		Transmit(ba);
	}

	return 0;
}

string LWSerialPortLua::GetID() const
{
	return portInstanceName;
}

void LWSerialPortLua::SetBaudRate(ISerialPort::BaudRate baudRate)
{
	if (sp)
	{
		sp->SetBaudRate(baudRate);
		sp->FlushAll();
	}
}

void LWSerialPortLua::SetBreak()
{
	if (sp)
		sp->SetBreak();
}

void LWSerialPortLua::SetBreak(int ms)
{
	if (sp)
	{
		if (ms <= 0)
			sp->SetBreak();
		else
		{
			using namespace boost::posix_time;
			sp->SetBreak(milliseconds(ms));
		}
	}
}

void LWSerialPortLua::Transmit(const ByteArray& data)
{
	if (sp)
		sp->Write(data);
}

void LWSerialPortLua::OnSerialDataReceived(SerialListener& sender, const ByteArray& data)
{
	if (pController)
		pController->ForwardSerialData(luaInstanceName, portInstanceName, data);
}

}
}
