/***************************************************************************
 *   Copyright (C) 2004 by Fred Schaettgen <kdebluetooth@schaettgen.de>*
 *                                                                         *
 *   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 "hcilistener.h"

#include <libkbluetooth/adapter.h>
#include <libkbluetooth/hcisocket.h>
//#include <bluetooth/hci.h>

#include <kdebug.h>
#include <klocale.h>
#include <dcopref.h>

#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>


using namespace KBluetooth;

HciListener::HciListener(QObject* parent) :
    QObject(parent)
{
    this->hciSocket = NULL;
    kdDebug() << "HciListener::HciListener()" << endl;
}

void HciListener::start() 
{
    slotHciConnect();
}

HciListener::~HciListener()
{
    kdDebug() << "HciListener::~HciListener()" << endl;
}

void HciListener::slotHciConnect()
{
    if (hciSocket) delete hciSocket;
    this->hciSocket = new KBluetooth::HciSocket(this);
    if (!hciSocket->open())
    {
        delete hciSocket;
        hciSocket = NULL;
        kdDebug() << "Kbluetoothd: Error opening HCI socket"
        << endl;
    }
    else
    {
        connect(hciSocket, SIGNAL(event(unsigned char, QByteArray)),
            this, SLOT(slotHciEvent(unsigned char, QByteArray)));
        connect(hciSocket, SIGNAL(error(int, QString)),
            this, SLOT(slotHciConnectionClosed()));
        connect(hciSocket, SIGNAL(connectionClosed()),
            this, SLOT(slotHciConnectionClosed()));
    }
}

void HciListener::slotHciConnectionClosed()
{
}

void HciListener::slotHciEvent(unsigned char eventCode, QByteArray buf)
{
    kdDebug() << "HCI-event: " <<
        QString::number(int(eventCode), 16) << endl;
    const unsigned char *data = (unsigned char*)buf.data();

    if (eventCode == EVT_CONN_COMPLETE)
    {
        int status = data[0];
        bdaddr_t *bdaddr = (bdaddr_t*)(data+3);
        KBluetooth::DeviceAddress addr(*bdaddr, false);
        int linkType = data[9];
        
        kdDebug() << "HCI event 'connection complete' status:" <<
            QString::number(int(eventCode), 16) << " ltype:" << linkType << endl;
        
        // Only report ACL links for the moment.
        if (linkType != ACL_LINK) return;

        emit connectionComplete(status, addr);
        emit connectionStateChanged();
    }
    else if (eventCode == EVT_CONN_REQUEST) {
        emit connectionStateChanged();
    }
    else if (eventCode == EVT_DISCONN_COMPLETE) {
        emit connectionStateChanged();
    }
    else if (eventCode == EVT_REMOTE_NAME_REQ_COMPLETE) {
        int errorCode = data[0];
        DeviceAddress devAddr(*((bdaddr_t*)(data+1)));
        if (errorCode == 0) {
            QString devName = QString::fromUtf8((const char*)(data+7));
            emit nameRequestComplete(errorCode, devAddr, devName);
        }
        else {
            emit nameRequestComplete(errorCode, devAddr, QString::null);
        }
    }
    else if (eventCode == EVT_INQUIRY_RESULT) {
        int numResults = data[0];
        inquiry_info *results = (inquiry_info*)(data+1);
        for (int n=0; n<numResults; n++)
        {
            DeviceAddress devAddr(results[n].bdaddr);
            int devClass = (results[n].dev_class[2] << 16) |
                (results[n].dev_class[1] << 8) |
                (results[n].dev_class[0] << 0);
            emit inquiryResult(devAddr, devClass);
        }
    }
    else if (eventCode == EVT_INQUIRY_RESULT_WITH_RSSI) {
        int numResults = data[0];
        inquiry_info_with_rssi *results = (inquiry_info_with_rssi*)(data+1);
        for (int n=0; n<numResults; n++)
        {
            DeviceAddress devAddr(results[n].bdaddr);
            int devClass = (results[n].dev_class[2] << 16) |
                (results[n].dev_class[1] << 8) |
                (results[n].dev_class[0] << 0);
            emit inquiryResult(devAddr, devClass);
        }
    }
    else if (eventCode == EVT_INQUIRY_COMPLETE) {
        unsigned char status = data[0];
        emit inquiryComplete(status);  
    }
    else if (eventCode == EVT_CMD_STATUS) {
        int status = data[0];
        //int numHciCmdPkts = data[1];
        if ((data[2] == 0x01 && data[3] == 0x04) /*Inquiry*/ && status == 0)
        {
            emit inquiryStarted();
        }
        else if (data[2] == 0x05 && data[3] == 0x04 /*create_connection*/) {
            emit connectionStateChanged();
        } 
    }
    else if (eventCode == EVT_CMD_COMPLETE) {
	evt_cmd_complete *cc = (evt_cmd_complete *) data;
	if (cc->opcode == cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV) )
		DCOPRef("kbluetoothd", "MetaServer").call("reload");
#if 0
        int status = data[0];
        int opcode = *((uint16_t*)(data+2));
        unsigned char ogf = cmd_opcode_ogf(opcode);
        unsigned short ocf = cmd_opcode_ocf(opcode);
        if (ogf == OGF_LINK_CTL) {
            if (ocf == OCF_PIN_CODE_REPLY) {
                if (status != 0) {
                    emit pinMismatch();
                }
            }
            else if (ocf == OCF_PIN_CODE_NEG_REPLY) {
                emit noPinGiven();
            }
        }
#endif
    }
}

#include "hcilistener.moc"
