/*
 * KMix -- KDE's full featured mini mixer
 *
 * Copyright 2006-2007 Christian Esken <esken@kde.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "core/kmixdevicemanager.h"
#include <kdebug.h>

#ifdef X_KMIX_KF5_BUILD
#ifdef __GNUC__
#warning KMix KF5 build does not support hotplugging yet
#endif
#endif

#include <iostream>

#include <QRegExp>
#include <QString>

#ifndef X_KMIX_KF5_BUILD
#include <solid/device.h>
#include <solid/devicenotifier.h>
#include <solid/audiointerface.h>
#endif

KMixDeviceManager* KMixDeviceManager::s_KMixDeviceManager = 0;

KMixDeviceManager::KMixDeviceManager()
{
}

KMixDeviceManager::~KMixDeviceManager()
{
}

KMixDeviceManager* KMixDeviceManager::instance()
{
    if ( s_KMixDeviceManager == 0 ) {
        s_KMixDeviceManager = new KMixDeviceManager();
    }
    return s_KMixDeviceManager;
}

void KMixDeviceManager::initHotplug()
{
#ifndef X_KMIX_KF5_BUILD
	connect (Solid::DeviceNotifier::instance(), SIGNAL(deviceAdded(QString)), SLOT(pluggedSlot(QString)) );
    connect (Solid::DeviceNotifier::instance(), SIGNAL(deviceRemoved(QString)), SLOT(unpluggedSlot(QString)) );
#endif
}

QString KMixDeviceManager::getUDI_ALSA(int num)
{
#ifdef X_KMIX_KF5_BUILD
	QString udi("hw%i");
	udi.arg(num);
	return udi;
#else
	QList<Solid::Device> dl = Solid::Device::listFromType(Solid::DeviceInterface::AudioInterface);

	QString numString;
	numString.setNum(num);
	bool found = false;
	QString udi;
	QString devHandle;
	foreach ( const Solid::Device &device, dl )
	{
	//        std::cout << "Coldplug udi = '" << device.udi().toUtf8().data() << "'\n";
		// LEAK audiohw leaks, but Solid does not document whether it is its own or "my" Object.
		const Solid::AudioInterface *audiohw = device.as<Solid::AudioInterface>();
		if (audiohw != 0)
		{
			if (audiohw->deviceType() & ( Solid::AudioInterface::AudioControl))
			{
				switch (audiohw->driver())
				{
					case Solid::AudioInterface::Alsa:
						devHandle = audiohw->driverHandle().toList().first().toString();
						if ( numString == devHandle )
						{
							found = true;
							udi = device.udi();
						}
						break;
					default:
						break;
				} // driver type
			} // is an audio control

			// If I delete audiohw, kmix crashes. If I do not, there is a definite leak according to valgrind:
//			==24561== 4,958 (1,200 direct, 3,758 indirect) bytes in 25 blocks are definitely lost in loss record 1,882 of 1,918
//			==24561==    at 0x4C27D49: operator new(unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
//			==24561==    by 0x5665462: ??? (in /usr/lib64/libsolid.so.4.11.3)
//			==24561==    by 0x565EC57: ??? (in /usr/lib64/libsolid.so.4.11.3)
//			==24561==    by 0x563610B: Solid::Device::asDeviceInterface(Solid::DeviceInterface::Type const&) const (in /usr/lib64/libsolid.so.4.11.3)
//			==24561==    by 0x4EB7DA6: Solid::AudioInterface const* Solid::Device::as<Solid::AudioInterface>() const (device.h:254)
//			==24561==    by 0x4EB6F6F: KMixDeviceManager::getUDI_ALSA(int) (kmixdevicemanager.cpp:70)
//			==24561==    by 0x4E6C809: Mixer_ALSA::open() (mixer_alsa9.cpp:139)
//			==24561==    by 0x4E6334E: Mixer_Backend::openIfValid() (mixer_backend.cpp:84)
//			==24561==    by 0x4EBD4F9: Mixer::openIfValid(int) (mixer.cpp:268)
//			==24561==    by 0x4EB5F10: MixerToolBox::possiblyAddMixer(Mixer*) (mixertoolbox.cpp:310)
//			==24561==    by 0x4EB57AE: MixerToolBox::initMixerInternal(MixerToolBox::MultiDriverMode, QList<QString>, QString&) (mixertoolbox.cpp:165)
//			==24561==    by 0x4EB52B6: MixerToolBox::initMixer(MixerToolBox::MultiDriverMode, QList<QString>, QString&) (mixertoolbox.cpp:85)
//			==24561==    by 0x4EB5267: MixerToolBox::initMixer(bool, QList<QString>, QString&) (mixertoolbox.cpp:80)
//			==24561==    by 0x4E7ED75: KMixWindow::KMixWindow(bool) (kmix.cpp:96)
//			==24561==    by 0x4E8A7CF: KMixApp::newInstance() (KMixApp.cpp:112)
//			==24561==    by 0x5B30A7E: KUniqueApplication::Private::_k_newInstanceNoFork() (in /usr/lib64/libkdeui.so.5.11.3)
//			==24561==    by 0x774A11D: QObject::event(QEvent*) (in /usr/lib64/libQtCore.so.4.8.5)
//			==24561==    by 0x61338A2: QApplication::event(QEvent*) (in /usr/lib64/libQtGui.so.4.8.5)
//			==24561==    by 0x612E8AB: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/lib64/libQtGui.so.4.8.5)
//			==24561==    by 0x6134E6F: QApplication::notify(QObject*, QEvent*) (in /usr/lib64/libQtGui.so.4.8.5)

			// The data seems to be "static" device info from Solid, so I will leave it alone.
//				delete audiohw;
		}
		if (found)
		{
			break;
		}
	} // foreach
	return udi;
#endif
}

QString KMixDeviceManager::getUDI_OSS(const QString& devname)
{
#ifdef X_KMIX_KF5_BUILD
	QString udi(devname);
	return udi;
#else
    QList<Solid::Device> dl = Solid::Device::listFromType(Solid::DeviceInterface::AudioInterface);

    bool found = false;
    QString udi;
    QString devHandle;
    foreach ( const Solid::Device &device, dl )
    {
//        std::cout << "Coldplug udi = '" << device.udi().toUtf8().data() << "'\n";
        const Solid::AudioInterface *audiohw = device.as<Solid::AudioInterface>();
        if (audiohw && (audiohw->deviceType() & ( Solid::AudioInterface::AudioControl))) {
            switch (audiohw->driver()) {
                case Solid::AudioInterface::OpenSoundSystem:
                    devHandle = audiohw->driverHandle().toString();
//                    std::cout << ">>> Coldplugged OSS ='" <<  devHandle.toUtf8().data() << "'\n";
                    if ( devname == devHandle ) {
                        found = true;
//                        std::cout << ">>> Match!!! Coldplugged OSS ='" <<  devHandle.toUtf8().data() << "'\n";
                        udi = device.udi();
                    }
                     break;
                default:
                    break;
            } // driver type
        } // is an audio control
        if ( found) break;
    } // foreach
    return udi;
#endif
}


void KMixDeviceManager::pluggedSlot(const QString& udi) {
#ifdef X_KMIX_KF5_BUILD
return;
#else

//   std::cout << "Plugged udi='" <<  udi.toUtf8().data() << "'\n";
   Solid::Device device(udi);
   Solid::AudioInterface *audiohw = device.as<Solid::AudioInterface>();
   if (audiohw && (audiohw->deviceType() & ( Solid::AudioInterface::AudioControl))) {
       QString dev;
       QRegExp devExpr( QLatin1String( "^\\D+(\\d+)$" ));
        switch (audiohw->driver()) {
           case Solid::AudioInterface::Alsa:
               if ( _hotpluggingBackend == "ALSA" || _hotpluggingBackend == "*" ) {
                    dev = audiohw->driverHandle().toList().first().toString();
                    emit plugged("ALSA", udi, dev);
               }
               break;
           case Solid::AudioInterface::OpenSoundSystem:
                if ( _hotpluggingBackend == "OSS" || _hotpluggingBackend == "*" ) {
                    dev = audiohw->driverHandle().toString();
                    if ( devExpr.indexIn(dev) > -1 ) {
                        dev = devExpr.cap(1); // Get device number from device name (e.g "/dev/mixer1" or "/dev/sound/mixer2")
                    }
                    else {
                        dev = '0'; // "/dev/mixer" or "/dev/sound/mixer"
                    }
                    emit plugged("OSS", udi, dev);
                }
                break;
           default:
               kError(67100) <<  "Plugged UNKNOWN Audio device (ignored)";
               break;
       }
    }
#endif
}


void KMixDeviceManager::unpluggedSlot(const QString& udi) {
//    std::cout << "Unplugged udi='" <<  udi.toUtf8().data() << "'\n";
//    Solid::Device device(udi);
    // At this point the device has already been unplugged by the user. Solid doesn't know anything about the
    // device except the UDI (not even device.as<Solid::AudioInterface>() is possible). Thus I'll forward any
    // unplugging action (could e.g. also be HID or mass storage). The receiver of the signal has to deal with it,
    // but a simple UDI matching is enough.
    emit unplugged(udi);

}


#include "kmixdevicemanager.moc"

