/*  *************************************************************************
	*   copyright: (C) 2005 Justin Huff <jjhuff@mspin.net>                  *
    *************************************************************************
*/

/*  *************************************************************************
    *                                                                       *
    * 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 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/
#include "config.h"

#include <tqcombobox.h>
#include <tqlayout.h>
#include <tqapplication.h>
#include <tqevent.h>
#include <tqmutex.h>
#include <tqthread.h>
#include <tqcheckbox.h>

#include <tdelocale.h>
#include <kurlrequester.h>
#include <tdemessagebox.h>
#include <tdeprocess.h>
#include <kdebug.h>
#include <tdeconfigbase.h>

#include <unistd.h>
#include <gsmlib/gsm_me_ta.h>
#include <gsmlib/gsm_sms.h>
#include <gsmlib/gsm_util.h>
#include <gsmlib/gsm_error.h>

#include "kopeteaccount.h"
#include "kopeteuiglobal.h"
#include "kopetemetacontact.h"
#include "kopetecontactlist.h"
#include "kopetechatsessionmanager.h"

#include "gsmlib.h"
#include "gsmlibprefs.h"
#include "smsprotocol.h"
#include "smscontact.h"

#include "kopete_unix_serial.h"

/////////////////////////////////////////////////////////////////////
#define GSMLIB_EVENT_ID 245
GSMLibEvent::GSMLibEvent(SubType t) : TQCustomEvent(TQEvent::User+GSMLIB_EVENT_ID)
{
	setSubType(t);
}

GSMLibEvent::SubType GSMLibEvent::subType()
{
	return m_subType;
}

void GSMLibEvent::setSubType(GSMLibEvent::SubType t)
{
	m_subType = t;
}

/////////////////////////////////////////////////////////////////////
GSMLibThread::GSMLibThread(TQString dev, GSMLib* parent)
{
	m_device = dev;
	m_parent = parent;
	m_run = true;
	m_MeTa = NULL;
}

GSMLibThread::~GSMLibThread()
{
	m_run = false;
}

void GSMLibThread::stop()
{
	m_run = false;
	kdDebug( 14160 ) << "Waiting from GSMLibThread to die"<<endl;
	if( wait(4000) == false )
		kdWarning( 14160 ) << "GSMLibThread didn't exit!"<<endl;
}
void GSMLibThread::run()
{
	if( doConnect() )
	{
		while( m_run )
		{
			pollForMessages();
			sendMessageQueue();	
		}
	}

	delete m_MeTa;
	m_MeTa = NULL;
	TQApplication::postEvent(m_parent, new GSMLibEvent(GSMLibEvent::DISCONNECTED));
	kdDebug( 14160 ) << "GSMLibThread exited"<<endl;
}

void GSMLibThread::send(const Kopete::Message& msg)
{
	if( m_MeTa )
	{
		m_outMessagesMutex.lock();
		m_outMessages.push_back(msg);
		m_outMessagesMutex.unlock();
	}
	else
	{
		GSMLibEvent* e = new GSMLibEvent( GSMLibEvent::MSG_NOT_SENT );
		e->Reason = TQString("GSMLib: Not Connected");
		e->Message = msg;
		TQApplication::postEvent(m_parent, e);
	}
}


bool GSMLibThread::doConnect()
{
	// open the port and ME/TA
	try
	{
		kdDebug( 14160 ) << "Connecting to: '"<<m_device<<"'"<<endl;
		
		gsmlib::Ref<gsmlib::Port> port = new gsmlib::KopeteUnixSerialPort(m_device.latin1(), 9600, gsmlib::DEFAULT_INIT_STRING, false);
		
		kdDebug( 14160 ) << "Port created"<<endl;
				
		m_MeTa = new gsmlib::MeTa(port);
		std::string dummy1, dummy2, receiveStoreName;
		m_MeTa->getSMSStore(dummy1, dummy2, receiveStoreName );
		m_MeTa->setSMSStore(receiveStoreName, 3);

		m_MeTa->setMessageService(1);

		// switch on SMS routing
		m_MeTa->setSMSRoutingToTA(true, false, false, true);

		m_MeTa->setEventHandler(this);
		TQApplication::postEvent(m_parent, new GSMLibEvent(GSMLibEvent::CONNECTED));
		return true;
	}
	catch(gsmlib::GsmException &e)
	{
		kdWarning( 14160 ) << k_funcinfo<< e.what()<<endl;
		m_run = false;
		return false;
	}
}

void GSMLibThread::SMSReception(gsmlib::SMSMessageRef newMessage, SMSMessageType messageType)
{
	try
	{
		IncomingMessage m;
		m.Type = messageType;
		m.Message = newMessage;
		
		m_newMessages.push_back(m);
	}
	catch(gsmlib::GsmException &e)
	{
		kdWarning( 14160 ) << k_funcinfo<< e.what()<<endl;
		m_run = false;
	}
}

void GSMLibThread::SMSReceptionIndication(std::string storeName, unsigned int index, SMSMessageType messageType)
{
	kdDebug( 14160 ) << k_funcinfo << "New Message in store: "<<storeName.c_str() << endl;

	try
	{
		if( messageType != gsmlib::GsmEvent::NormalSMS )
			return;
		
		IncomingMessage m;
		m.Index = index;
		m.StoreName = storeName.c_str();
		m.Type = messageType;
		m_newMessages.push_back(m);
	}
	catch(gsmlib::GsmException &e)
	{
		kdWarning( 14160 ) << k_funcinfo<< e.what()<<endl;
		m_run = false;
	}
}

void GSMLibThread::pollForMessages( )
{
	if( m_MeTa == NULL )
		return;
	
	try
	{
		struct timeval timeoutVal;
		timeoutVal.tv_sec = 1;
		timeoutVal.tv_usec = 0;
		m_MeTa->waitEvent(&timeoutVal);
		
		MessageList::iterator it;
		for( it=m_newMessages.begin(); it!=m_newMessages.end(); it++)
		{
			IncomingMessage m = *it;
			
			// Do we need to fetch it from the ME?
			if( m.Message.isnull() )
			{
				gsmlib::SMSStoreRef store = m_MeTa->getSMSStore(m.StoreName.latin1());
				store->setCaching(false);

				m.Message = (*store.getptr())[m.Index].message();
				store->erase(store->begin() + m.Index);
			}

			GSMLibEvent* e = new GSMLibEvent( GSMLibEvent::NEW_MESSAGE );
			e->Text = m.Message->userData().c_str();
			e->Number = m.Message->address().toString().c_str();

			TQApplication::postEvent(m_parent, e);

		}
		m_newMessages.clear();
	}
	catch(gsmlib::GsmException &e)
	{
		kdWarning( 14160 ) << k_funcinfo<< e.what()<<endl;
		m_run = false;
	}
}

void GSMLibThread::sendMessageQueue()
{
	TQMutexLocker _(&m_outMessagesMutex);
	
	if(m_outMessages.size() == 0 )
		return;

	KopeteMessageList::iterator it;
	for( it=m_outMessages.begin(); it!=m_outMessages.end(); it++)
		sendMessage(*it);
	m_outMessages.clear();
}

void GSMLibThread::sendMessage(const Kopete::Message& msg)
{
	TQString reason;

	if (!m_MeTa)
	{
		GSMLibEvent* e = new GSMLibEvent( GSMLibEvent::MSG_NOT_SENT );
		e->Reason = TQString("GSMLib: Not Connected");
		e->Message = msg;
		TQApplication::postEvent(m_parent, e);
	}

	TQString message = msg.plainBody();
	TQString nr = msg.to().first()->contactId();

	// send SMS
	try
	{
		gsmlib::Ref<gsmlib::SMSSubmitMessage> submitSMS = new gsmlib::SMSSubmitMessage();
		gsmlib::Address destAddr( nr.latin1() );
		submitSMS->setDestinationAddress(destAddr);
		m_MeTa->sendSMSs(submitSMS, message.latin1(), true);
		
		GSMLibEvent* e = new GSMLibEvent( GSMLibEvent::MSG_SENT );
		e->Message = msg;
		TQApplication::postEvent(m_parent, e);
	}
	catch(gsmlib::GsmException &e)
	{
		GSMLibEvent* ev = new GSMLibEvent( GSMLibEvent::MSG_NOT_SENT );
		ev->Reason = TQString("GSMLib: ") + e.what();
		ev->Message = msg;
		TQApplication::postEvent(m_parent, ev);
	}
}

/////////////////////////////////////////////////////////////////////

GSMLib::GSMLib(Kopete::Account* account)
	: SMSService(account)
{
	prefWidget = 0L;
	m_thread = NULL;
	
	loadConfig();
}

GSMLib::~GSMLib()
{
	disconnect();
}

void GSMLib::saveConfig()
{
	if( m_account != NULL )
	{
		TDEConfigGroup* c = m_account->configGroup();

		c->writeEntry(TQString("%1:%2").arg("GSMLib").arg("Device"), m_device);
	}
}

void GSMLib::loadConfig()
{
	m_device = "/dev/bluetooth/rfcomm0";
	if( m_account != NULL )
	{
		TQString temp;
		TDEConfigGroup* c = m_account->configGroup();
		
		temp = c->readEntry(TQString("%1:%2").arg("GSMLib").arg("Device"), TQString());
		if( temp != TQString() )
			m_device = temp;
	}
}

void GSMLib::connect()
{
	
	m_thread = new GSMLibThread(m_device, this);
	m_thread->start();
	
}

void GSMLib::disconnect()
{
	kdDebug( 14160 ) << k_funcinfo <<endl;

	if( m_thread )
	{
		m_thread->stop();
		delete m_thread;
		m_thread = NULL;
		emit disconnected();
	}

}

void GSMLib::setWidgetContainer(TQWidget* parent, TQGridLayout* layout)
{
	m_parent = parent;
	m_layout = layout;
	TQWidget *configWidget = configureWidget(parent);
	layout->addMultiCellWidget(configWidget, 0, 1, 0, 1);
	configWidget->show();
}

void GSMLib::send(const Kopete::Message& msg)
{
	m_thread->send(msg);
}

TQWidget* GSMLib::configureWidget(TQWidget* parent)
{

	if (prefWidget == 0L)
		prefWidget = new GSMLibPrefsUI(parent);

	loadConfig();
	prefWidget->device->setURL(m_device);

	return prefWidget;
}

void GSMLib::savePreferences()
{
	if( prefWidget )
	{
		m_device = prefWidget->device->url();
	}
	saveConfig();
}

int GSMLib::maxSize()
{
	return 160;
}

void GSMLib::customEvent(TQCustomEvent* e)
{
	if( e->type() != TQEvent::User+GSMLIB_EVENT_ID )
		return;

	if( m_account == NULL )
		return;

	GSMLibEvent* ge = (GSMLibEvent*)e;
	
	kdDebug( 14160 ) << "Got event "<<ge->subType()<<endl;
	switch( ge->subType() )
	{
		case GSMLibEvent::CONNECTED:
			emit connected();
			break;
		case GSMLibEvent::DISCONNECTED:
			disconnect();
			break;
		case GSMLibEvent::MSG_SENT:
			emit messageSent(ge->Message);
			break;
		case GSMLibEvent::MSG_NOT_SENT:
			emit messageNotSent(ge->Message, ge->Reason);
			break;
		case GSMLibEvent::NEW_MESSAGE:
		{
			TQString nr = ge->Number;
			TQString text = ge->Text;
			
			// Locate a contact
			SMSContact* contact = static_cast<SMSContact*>( m_account->contacts().find( nr ));
			if ( contact==NULL )
			{
				// No contact found, make a new one
				Kopete::MetaContact* metaContact = new Kopete::MetaContact ();
				metaContact->setTemporary ( true );
				contact = new SMSContact(m_account, nr, nr, metaContact );
				Kopete::ContactList::self ()->addMetaContact( metaContact );
				contact->setOnlineStatus( SMSProtocol::protocol()->SMSOnline );
			}
			
			// Deliver the msg
			Kopete::Message msg( contact, m_account->myself(), text, Kopete::Message::Inbound, Kopete::Message::RichText );
			contact->manager(Kopete::Contact::CanCreate)->appendMessage( msg );
			break;
		}
	}
}




const TQString& GSMLib::description()
{
	TQString url = "http://www.pxh.de/fs/gsmlib/";
	m_description = i18n("<qt>GSMLib is a library (and utilities) for sending SMS via a GSM device. The program can be found on <a href=\"%1\">%1</a></qt>").arg(url).arg(url);
	return m_description;
}

#include "gsmlib.moc"
