//-*-c++-*-
/***************************************************************************
 *   Copyright (C) 2003 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 "tdeiobluetooth.h"

#include <sys/stat.h>
#include <dcopclient.h>
#include <tdeapplication.h>
#include <tdecmdlineargs.h>
#include <tqregexp.h>

#include <devicemimeconverter.h>
#include <deviceImpl.h>
#include <btuuids.h>

using namespace TDEBluetooth;

static const TDECmdLineOptions options[] =
{
        { "+protocol", I18N_NOOP( "Protocol name" ), 0 },
        { "+pool", I18N_NOOP( "Socket name" ), 0 },
        { "+app", I18N_NOOP( "Socket name" ), 0 },
        TDECmdLineLastOption
};

extern "C"
{
    TDE_EXPORT int kdemain(int argc, char **argv) {
        TDEInstance instance( "tdeio_bluetooth" );
        kdDebug() << "*** Starting tdeio_bluetooth " << endl;
        if (argc != 4) {
            kdDebug() << "Usage: tdeio_bluetooth  protocol domain-socket1 domain-socket2" << endl;
            exit(-1);
        }

        putenv(strdup("SESSION_MANAGER="));
        TDECmdLineArgs::init(argc, argv, "tdeio_bluetooth", 0, 0, 0, 0);
        TDECmdLineArgs::addCmdLineOptions( options );

        TDEApplication app( false, false, false );
        app.dcopClient()->attach();

        TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
        TDEioBluetooth slave( args->arg(0), args->arg(1), args->arg(2) );
        kdDebug() << "*** protocol    " << args->arg(0) << endl;
        kdDebug() << "*** pool socket " << args->arg(1) << endl;
        kdDebug() << "*** app socket  " << args->arg(2) << endl;
        slave.dispatchLoop();
        kdDebug() << "*** tdeio_bluetooth Done" << endl;
        return 0;
    }
}


TDEioBluetooth::TDEioBluetooth(const TQCString &protocol, const TQCString &pool_socket, const TQCString &app_socket) :
        ForwardingSlaveBase(protocol, pool_socket, app_socket)
{
    kdDebug() << k_funcinfo << endl;

    TDELocale::setMainCatalogue("tdebluez");
    TQT_DBusError error;
    adapter = 0;

    manager = new TDEBluetooth::ObjectManagerImpl("org.bluez", "/"/*, this, "ObexObjectManager"*/);

    if (!manager)
    {
        ForwardingSlaveBase::error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, i18n("Bluetooth Manager not found"));
        closeConnection();
    }
    else
    {
        if (!manager->getAdapters().isEmpty())
        {
            TDEBluetooth::ObjectManagerImpl::AdapterList al = manager->getAdapters();
            TDEBluetooth::ObjectManagerImpl::AdapterList::Iterator ait = al.begin();
            for (ait; ait != al.end(); ++ait)
            {
                TDEBluetooth::AdapterImpl *ad = new TDEBluetooth::AdapterImpl("org.bluez", (*ait));
                ad->setConnection((*(manager->getConnection())));
                // FIXME implement multiple adapters
                if (ad->getPowered(error))
                {
                    adapter = ad;
                    break;
                }
            }
            connect(manager, TQ_SIGNAL(deviceAdded(const TQString &)),
                    this, TQ_SLOT(slotAddDevice(const TQString &)));
            connect(manager, TQ_SIGNAL(deviceRemoved(const TQString &)),
                    this, TQ_SLOT(slotRemoveDevice(const TQString &)));
            connect(manager, TQ_SIGNAL(adapterPowerOnChanged(const TQString&, bool )),
                    this, TQ_SLOT(slotAdapterPowerOnChanged(const TQString &, bool )));
        }
        else
        {
            ForwardingSlaveBase::error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, i18n("No adapter found"));
            closeConnection();
        }
    }
}

TDEioBluetooth::~TDEioBluetooth()
{
    kdDebug() << k_funcinfo << endl;
    if (manager) delete manager;
    if (adapter) delete adapter;
}

void TDEioBluetooth::closeConnection()
{
	kdDebug() << k_funcinfo << endl;
	exit();
}

void TDEioBluetooth::stat(const KURL &url)
{
    kdDebug() << __func__ << "(" << url.prettyURL() << ")" << endl;

    TDEIO::UDSEntry entry;

    if (!adapter)
    {
//        ForwardingSlaveBase::warning(i18n("Bluetooth Adapter not found"));
//        ForwardingSlaveBase::error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, i18n("Bluetooth Adapter not found"));
        TQString name = "No device found";
        TQ_UINT32 devClass = 0;

        addAtom(entry, TDEIO::UDS_NAME, name);
        addAtom(entry, TDEIO::UDS_FILE_TYPE, S_IFDIR);
        addAtom(entry, TDEIO::UDS_ACCESS, 0555);
        addAtom(entry, TDEIO::UDS_MIME_TYPE, DeviceMimeConverter::classToMimeType(devClass));
        addAtom(entry, TDEIO::UDS_ICON_NAME, DeviceMimeConverter::classToIconName(devClass));

        return;
    }

    TQT_DBusError dbuserror;
    TQString path = url.path();
    if (path.isEmpty() || path == "/")
    {
        // The root is "virtual" - it's not a single physical directory
        createTopLevelEntry(entry);
    }
    else if (path.find(TQRegExp("/\\[([0-9A-F]{2}:){5}[0-9A-F]{2}\\]"), 0) != -1)
    {
        createDirEntry(entry, path, path);
    }
    else
    {
        ForwardingSlaveBase::error(TDEIO::ERR_MALFORMED_URL, i18n("Could not stat %1.").arg(url.prettyURL()));
    }
    statEntry(entry);
    finished();
}

void TDEioBluetooth::listDir(const KURL &url)
{
    kdDebug() << k_funcinfo << endl;

    if (!adapter)
    {
        ForwardingSlaveBase::error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, i18n("Bluetooth Adapter not found"));
        return;
    }

    TDEIO::UDSEntry entry;
    TQValueList<TDEIO::UDSEntry> list;
    TQT_DBusError error;
    TDEIO::UDSEntryList entries;

    TQString path = url.path();
    TQString name = adapter->getName(error);

    kdDebug() << __func__ << "(" << path << ")" << endl;

    TQRegExp rx("/" + name + "/\\[([0-9A-F]{2}:){5}[0-9A-F]{2}\\]");
    kdDebug() << "Regex: " << rx.search(path) << endl;

    if (rx.search(path) == 0)
    {
        listServices(list, url);
    }
    else if (path == "/" + name)
    {
        listDevices(list, url);
    }
    else if (path == "/")
    {
        createTopLevelEntry(entry);
        listEntry(entry, false);
    }
    else
    {
        ForwardingSlaveBase::listDir(url);
    }

    if (list.count() > 0)
    {
        kdDebug() << __func__ << "(" << path << ")" << endl;
        totalSize(list.count() + 1);

        TDEIO::UDSEntryListIterator it = list.begin();
        TDEIO::UDSEntryListIterator end = list.end();
        for (; it != end; ++it)
        {
            entries.append(*it);
        }
        listEntries(entries);
    }
    listEntry(entry, true);

    finished();
    return;
}

bool TDEioBluetooth::rewriteURL(const KURL &url, KURL &newUrl)
{
    kdDebug() << k_funcinfo << endl;

    TQString path = url.path();
    TQString protocol = url.protocol();

    if (protocol == "obexopp" || protocol == "obexftp")
    {
        newUrl = url;
        return true;
    }
    else
    {
        ForwardingSlaveBase::error(TDEIO::ERR_MALFORMED_URL, url.prettyURL());
        return false;
    }
}

void TDEioBluetooth::createTopLevelEntry(TDEIO::UDSEntry &entry)
{
    kdDebug() << k_funcinfo << endl;

    TQT_DBusError error;
    TQString name = adapter->getName(error);
    TQ_UINT32 devClass = adapter->getClass(error);

    addAtom(entry, TDEIO::UDS_NAME, name);
    addAtom(entry, TDEIO::UDS_FILE_TYPE, S_IFDIR);
    addAtom(entry, TDEIO::UDS_ACCESS, 0555);
    addAtom(entry, TDEIO::UDS_MIME_TYPE, DeviceMimeConverter::classToMimeType(devClass));
    addAtom(entry, TDEIO::UDS_ICON_NAME, DeviceMimeConverter::classToIconName(devClass));
}

bool TDEioBluetooth::listDevice(TDEIO::UDSEntry &entry, const TQString &path, const KURL &url)
{
    kdDebug() << __func__ << "(" << url << ")" << endl;
    TQT_DBusError error;

    TDEBluetooth::DeviceImpl *dev = new TDEBluetooth::DeviceImpl("org.bluez", path);
    dev->setConnection((*(manager->getConnection())));

    const TQString addr = dev->getAddress(error);
    TQString name = dev->getName(error);
    TQString alias = dev->getAlias(error);
    const int devClass = dev->getClass(error);
    TQString aname = adapter->getName(error);

    delete dev;

    entry.clear();

    if (!alias.isEmpty())
        name = alias;
    else
        name = alias = addr;

    createDirEntry(entry, name, TQString("bluetooth:/%1/[%2]").arg(aname).arg(addr),
            DeviceMimeConverter::classToMimeType(devClass));

    return true;
}

bool TDEioBluetooth::listDevices(TQValueList<TDEIO::UDSEntry> &list, const KURL &url)
{
    kdDebug() << __func__ << "(" << url << ")" << endl;

    TDEIO::UDSEntry entry;
    TDEBluetooth::ObjectManagerImpl::DeviceList dl = manager->getDevices();
    TDEBluetooth::ObjectManagerImpl::DeviceList::Iterator dit = dl.begin();
    for (dit; dit != dl.end(); ++dit)
    {
        entry.clear();
        listDevice(entry, (*dit), url);
        list.append(entry);
    }

    return true;
}

bool TDEioBluetooth::listServices(TQValueList<TDEIO::UDSEntry> &list, const KURL &url)
{
    kdDebug() << __func__ << "url: " << url << endl;

    TDEIO::UDSEntry entry;
    TQString path = url.path();

    kdDebug() << __func__ << "path: " << path << endl;

    int pos = path.find(TQRegExp("/\\[([0-9A-F]{2}:){5}[0-9A-F]{2}\\]"), 0);
    if (pos != -1)
    {

        TQString address = path.remove(0, pos + 2).remove(17, path.length());
        kdDebug() << __func__ << "address: " << address << endl;

        TDEBluetooth::ObjectManagerImpl::DeviceList dl = manager->getDevices();
        TDEBluetooth::ObjectManagerImpl::DeviceList::Iterator dit = dl.begin();
        for (dit; dit != dl.end(); ++dit)
        {
            TDEBluetooth::DeviceImpl *d = new TDEBluetooth::DeviceImpl("org.bluez", (*dit));
            d->setConnection((*(manager->getConnection())));

            TQT_DBusError dbuserror;
            TQString addr = d->getAddress(dbuserror);
            if (addr == address)
            {
                TQStringList uuids = d->getUUIDs(dbuserror);
                for (TQStringList::Iterator it = uuids.begin();
                        it != uuids.end(); ++it)
                {
                    entry.clear();
                    // accepted services OBEX OPP, PCE, FTP
                    //FIXME temporary disabled
//                    if ((*it) == "00001105-0000-1000-8000-00805f9b34fb")
//                    {
//                        createDirEntry(entry, resolveUUID((*it)), TQString("obexopp:/[%1]/").arg(address), "bluetooth/obex-objectpush-profile");
//                        addAtom(entry, TDEIO::UDS_NAME, "obexopp");
//                        list.append(entry);
//                    }
//                    else if ((*it) == "00001106-0000-1000-8000-00805f9b34fb")
//                    {
//                        createDirEntry(entry, resolveUUID((*it)), TQString("obexftp:/[%1]/").arg(address), "bluetooth/obex-ftp-profile");
//                        addAtom(entry, TDEIO::UDS_NAME, "obexftp");
//                        list.append(entry);
//                    }
                }
                break;
            }
            delete d;
        }
    }
    else
    {
        ForwardingSlaveBase::error(TDEIO::ERR_MALFORMED_URL, url.url());
    }

    return true;
}

bool TDEioBluetooth::createDirEntry(TDEIO::UDSEntry &entry, const TQString &name, const TQString &dir, const TQString &mimeType)
{
    kdDebug() << k_funcinfo << endl;

    addAtom(entry, TDEIO::UDS_NAME, name);
    if (dir != TQString::null)
    {
        addAtom(entry, TDEIO::UDS_URL, dir);
    }
    addAtom(entry, TDEIO::UDS_MIME_TYPE, mimeType);

    if (mimeType == "inode/directory")
        addAtom(entry, TDEIO::UDS_FILE_TYPE, S_IFDIR);
    else
        addAtom(entry, TDEIO::UDS_FILE_TYPE, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);

    return true;
}

void TDEioBluetooth::slotAddDevice(const TQString &path)
{
    kdDebug() << k_funcinfo << endl;
    listDir("bluetooth:/");
}

void TDEioBluetooth::slotRemoveDevice(const TQString &path)
{
    kdDebug() << __func__ << "(" << path << ")" << endl;
    listDir("bluetooth:/");

//	TQT_DBusError error;
//	TDEBluetooth::DeviceImpl *d= new TDEBluetooth::DeviceImpl("org.bluez",path);
//	d->setConnection((*(manager->getConnection())));
//	const TQString address = d->getAddress(error);
//    delete d;
//
//	listDevice(address);
//	listEntry(TDEIO::UDSEntry(), true);

}

void TDEioBluetooth::slotAddService(const KURL &url, const TQString uuid)
{
    kdDebug() << __func__ << "(URL=" << url << ", UUID=" << uuid << ")" << endl;

//	TQT_DBusError error;
//	TDEBluetooth::DeviceImpl *d= new TDEBluetooth::DeviceImpl("org.bluez",path);
//	d->setConnection((*(manager->getConnection())));
//	const TQString address = d->getAddress(error);
//	TQ_UINT32 devclass = d->getClass(error);
//	const TQString devicon = DeviceMimeConverter::classToIconName(devclass);
//	delete d;
//
//	TQMap<TQString,int>::iterator f=qDevicesList.find(address);
//	if(f!=qDevicesList.end() && f.data() == devclass) return;

//	listEntry(UDSEntry(), true);

}

void TDEioBluetooth::slotAdapterPowerOnChanged(TQString const& path, bool state)
{
    kdDebug() << __func__ << "(" << path << ")" << endl;
//
//	TQT_DBusError error;
//	TDEBluetooth::DeviceImpl *d= new TDEBluetooth::DeviceImpl("org.bluez",path);
//	d->setConnection((*(manager->getConnection())));
//	const TQString address = d->getAddress(error);
//	TQ_UINT32 devclass = d->getClass(error);
//	const TQString devicon = DeviceMimeConverter::classToIconName(devclass);
//	delete d;
//
//	TQMap<TQString,int>::iterator f=qDevicesList.find(address);
//	if(f!=qDevicesList.end() && f.data() == devclass) return;
//	qDevicesList.insert(address, devclass);
//
//	listDevice(address);
//	listEntry(UDSEntry(), true);

}

void TDEioBluetooth::addAtom(TDEIO::UDSEntry &entry, TDEIO::UDSAtomTypes type, TQString s)
{
    kdDebug() << k_funcinfo << endl;
    TDEIO::UDSAtom atom;
    atom.m_uds = type;
    atom.m_str = s;
    entry.append(atom);
}

void TDEioBluetooth::addAtom(TDEIO::UDSEntry &entry, TDEIO::UDSAtomTypes type, long l)
{
    kdDebug() << k_funcinfo << endl;
    TDEIO::UDSAtom atom;
    atom.m_uds = type;
    atom.m_long = l;
    entry.append(atom);
}

#include "tdeiobluetooth.moc"
