/**************************************************************************
 *   Copyright (C) 2006-2007 by Danny Kukawka                              *
 *                              <dkukawka@suse.de>, <danny.kukawka@web.de> *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of version 2 of the GNU General Public License     *
 *   as published by the Free Software Foundation.                         *
 *                                                                         *
 *   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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU 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.             *
 ***************************************************************************/

/*!
 * \file 	hardware.cpp
 * \brief 	In this file can be found the hardware information related code.
 * \author 	Danny Kukawka, <dkukawka@suse.de>, <danny.kukawka@web.de>
 * \date    	2006-2007
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

// include global header
#include <fcntl.h>
#include <math.h>

// include TQt header
#include <tqtimer.h>
#include <tqdir.h>

// keycode definitions
#include <linux/input.h>

// include own header
#include "hardware.h"
#include "tdepowersave_debug.h"

// #define USE_EVENT_DEVICES_DIRECTLY 1

/*! The default constructor of the class HardwareInfo */
HardwareInfo::HardwareInfo()
{
    kdDebugFuncIn(trace);

    // init members
    acadapter = true;
    lidclose = false;
    laptop = false;
    brightness = false;
    brightness_in_hardware = false;
    sessionIsActive = true;	 // assume as first we are active

    // initialize connection to the TDE hardware library
    m_hwdevices = TDEGlobal::hardwareDevices();
    connect(m_hwdevices, TQ_SIGNAL(hardwareUpdated(TDEGenericDevice*)),
            this, TQ_SLOT(processHardwareChangedEvent(TDEGenericDevice*)));

#ifdef USE_EVENT_DEVICES_DIRECTLY
	connect(m_hwdevices, TQ_SIGNAL(eventDeviceKeyPressed(unsigned int, TDEEventDevice*)),
	        this, TQ_SLOT(processKeyPressEvent(unsigned int, TDEEventDevice*)));
#endif

    // update everything the first time
    update_info_ac_changed = true;
    update_info_cpufreq_policy_changed = true;
    update_info_primBattery_changed = true;

    currentCPUFreqPolicy = UNKNOWN_CPUFREQ;
    primaryBatteriesWarnLevel = 12;
    primaryBatteriesLowLevel = 7;
    primaryBatteriesCriticalLevel = 2;

    allUDIs = TQStringList();
    unknownUDIs = TQStringList();
    consoleKitSession = TQString();
    BatteryList.setAutoDelete(true); // the list owns the objects

    primaryBatteries = new BatteryCollection(BAT_PRIMARY);
    setPrimaryBatteriesWarningLevel(); // force default settings

    // connect to D-Bus
    dbus_iface = new dbusInterface();
    sessionIsActive = dbus_iface->checkActiveSession();
    connect(dbus_iface, TQ_SIGNAL(activeSessionChanged(bool)), this, TQ_SLOT(handleSessionState(bool)));

    checkPowermanagement();
    checkIsLaptop();
    checkBrightness();
    checkCPUFreq();
    checkSuspend();
    intialiseHWInfo();

    updatePrimaryBatteries();

    kdDebugFuncOut(trace);
}

/*! The default desctuctor of the class HardwareInfo */
HardwareInfo::~HardwareInfo()
{
    kdDebugFuncIn(trace);

    kdDebugFuncOut(trace);
}

/*!
 * This funtion is used to reinit all hardware information.
 * \return 		Boolean with result of the call
 * \retval true		if reinit HW infos correct
 * \retval false	if not
 */
bool HardwareInfo::reinitHardwareInfos()
{
    kdDebugFuncIn(trace);

    /* first cleanup */
    acadapter = true;
    lidclose = false;
    laptop = false;
    brightness = false;
    has_APM = false;
    has_ACPI = false;

    update_info_ac_changed = true;
    update_info_cpufreq_policy_changed = true;
    update_info_primBattery_changed = true;

    allUDIs = TQStringList();
    unknownUDIs = TQStringList();

    BatteryList.clear();
    primaryBatteries = new BatteryCollection(BAT_PRIMARY);

    /* reinit hardware data */
    checkPowermanagement();
    checkIsLaptop();
    checkBrightness();
    checkCPUFreq();
    checkSuspend();
    intialiseHWInfo();
    updatePrimaryBatteries();

    kdDebugFuncOut(trace);
    return true;
}

/*!
 * \b Slot called if the state of the current session change
 * \param state boolean represent the state of the session
 */
void HardwareInfo::handleSessionState(bool state)
{
    if (state != sessionIsActive)
    {
        sessionIsActive = state;
        TQTimer::singleShot(50, this, TQ_SLOT(emitSessionActiveState()));
    }
}

/*!
 * This function is used to parse changed hardware nofifications from the TDE hardware library
 * \param device 	a \ref TDEGenericDevice* which should be processed
 */
void HardwareInfo::processHardwareChangedEvent(TDEGenericDevice *device)
{
    kdDebugFuncIn(trace);

    if (allUDIs.contains(device->uniqueID()))
    {
        if (device->type() == TDEGenericDeviceType::PowerSupply)
        {
            TQTimer::singleShot(50, this, TQ_SLOT(checkACAdapterState()));
        }
        else if (device->type() == TDEGenericDeviceType::Battery)
        {
            // this is a battery event
            updateBatteryValues(device);
        }
        else if (device->type() == TDEGenericDeviceType::Event)
        {
            TDEEventDevice *edevice = dynamic_cast<TDEEventDevice*>(device);
            if (edevice)
            {
                if (edevice->eventType() == TDEEventDeviceType::ACPILidSwitch)
                {
                    TQTimer::singleShot(50, this, TQ_SLOT(checkLidcloseState()));
                }
            }
        }
        else if (device->type() == TDEGenericDeviceType::Backlight)
        {
            TQTimer::singleShot(50, this, TQ_SLOT(checkBrightness()));
        }
        // TODO: add needed code
    }
    else
    {
        TQString duid = device->uniqueID();
        if (!unknownUDIs.contains(duid))
        {
            kdDebug() << "unmonitored device changed: " << duid << endl;
            unknownUDIs.append(duid);
        }
    }

    kdDebugFuncOut(trace);
}

/*!
 * This funtion is used to parse changed hardware nofifications from the TDE hardware library
 * \param keycode 	a keycode which should be processed
 * \param edevice 	the \ref TDEEventDevice* from whence the keypress originated
 */
void HardwareInfo::processKeyPressEvent(unsigned int keycode, TDEEventDevice *edevice)
{
    kdDebugFuncIn(trace);

    // FIXME
    // How can I get specific button press/release information (instead of just "something happened to the button") from the TDE hardware library?
    kdDebug() << "ButtonPressed event from TDE HW library " << endl;
    if (((edevice->eventType() == TDEEventDeviceType::ACPIPowerButton) || (edevice->eventType() == TDEEventDeviceType::ACPIOtherInput)) && (keycode == KEY_POWER))
    {
        TQTimer::singleShot(50, this, TQ_SLOT(emitPowerButtonPressed()));
    }
    else if (((edevice->eventType() == TDEEventDeviceType::ACPISuspendButton) || (edevice->eventType() == TDEEventDeviceType::ACPIOtherInput)) && (keycode == KEY_SLEEP))
    {
        TQTimer::singleShot(50, this, TQ_SLOT(emitSuspendButtonPressed()));
    }
    else if (((edevice->eventType() == TDEEventDeviceType::ACPISuspendButton) || (edevice->eventType() == TDEEventDeviceType::ACPIOtherInput)) && (keycode == KEY_SUSPEND))
    {
        TQTimer::singleShot(50, this, TQ_SLOT(emitS2diskButtonPressed()));
    }

    kdDebugFuncOut(trace);
}

/*!
 * This slot is used to fetch the resume signal and multiplex. If needed some
 * actions after resume, do this here.
 * \param result 	integer with the result of the resume/suspend
 */
void HardwareInfo::handleResumeSignal(int result)
{
    if (trace)
        kdDebug() << funcinfo << "IN: " << "(int result: " << result << ")" << endl;

    if (result == -1)
    {
        // check if time since suspend is higher than 6 hours,
        // the magic D-Bus timeout for pending calls
        if (calledSuspend.elapsed() > 21600000)
        {
            emit resumed(INT_MAX);
        }
    }
    else
    {
        emit resumed(result);
    }

    calledSuspend = TQTime();
    kdDebugFuncOut(trace);
}

/*!
 * This function check for a given UDI, if we should handle a device
 * \param _udi		TQString with the UDI of the device
 * \param *type		pointer to a integer to return the type of the device, see \ref device_type
 * \return 		Boolean with info if we should handle the device.
 * \retval true		if we should handle
 * \retval false	if not
 */
bool HardwareInfo::checkIfHandleDevice(TQString _udi, int *type)
{
    kdDebugFuncIn(trace);

    TQStringList _cap;
    bool ret = true;

    TDEGenericDevice *hwdevice = m_hwdevices->findByUniqueID(_udi);

    if (hwdevice)
    {
        TDEGenericDeviceType::TDEGenericDeviceType devtype = hwdevice->type();
        if (devtype == TDEGenericDeviceType::PowerSupply)
        {
            *type = BATTERY;
        }
        else if (devtype == TDEGenericDeviceType::Event)
        {
            TDEEventDevice *edevice = dynamic_cast<TDEEventDevice*>(hwdevice);
            if (edevice)
            {
                if (edevice->eventType() == TDEEventDeviceType::ACPILidSwitch)
                {
                    *type = LID;
                }
                else if (edevice->eventType() == TDEEventDeviceType::ACPIPowerButton)
                {
                    *type = BUTTON_POWER;
                }
                else if (edevice->eventType() == TDEEventDeviceType::ACPISuspendButton)
                {
                    *type = BUTTON_SLEEP;
                }
                else
                {
                    ret = false;
                }
            }
            else
            {
                ret = false;
            }
        }
        else if (devtype == TDEGenericDeviceType::Battery)
        {
            *type = BATTERY;
        }
        else if (devtype == TDEGenericDeviceType::Backlight)
        {
            *type = LAPTOP_PANEL;
        }
        else
        {
            ret = false;
            kdDebug() << "Device with type " << devtype << " unhandled" << endl;
        }
    }
    else
    {
        ret = false;
    }

    if (!ret)
        *type = UNKNOWN_DEVICE;

    kdDebugFuncOut(trace);
    return ret;
}

// --> set some values for devices
/*!
 * This function set the warning level for the primary battery collection
 * If all give param are -1 or not set this function force the current
 * settings to the primary battery collection.
 * \param _warn		value for the state BAT_WARN or -1
 * \param _low		value for the state BAT_LOW or -1
 * \param _crit		value for the state BAT_CRIT or -1
 */
void HardwareInfo::setPrimaryBatteriesWarningLevel(int _warn, int _low, int _crit)
{
    if (trace)
        kdDebug() << funcinfo << "IN: " << "warn: " << _warn << " low: " << _low << " crit: " << _crit << endl;

    if (_warn > -1 && _low > -1 && _crit > -1)
    {
        primaryBatteriesWarnLevel = _warn;
        primaryBatteriesLowLevel = _low;
        primaryBatteriesCriticalLevel = _crit;
    }

    if (primaryBatteries)
    {
        primaryBatteries->setWarnLevel(primaryBatteriesWarnLevel);
        primaryBatteries->setLowLevel(primaryBatteriesLowLevel);
        primaryBatteries->setCritLevel(primaryBatteriesCriticalLevel);
        if (!BatteryList.isEmpty())
        {
            primaryBatteries->refreshInfo(BatteryList, true);
        }
    }

    kdDebugFuncOut(trace);
}

// --> init HW information section -- START <---

/*!
 * The function checks if the machine is a laptop.
 */
void HardwareInfo::checkIsLaptop()
{
    kdDebugFuncIn(trace);

    TQString ret;

    TDERootSystemDevice *rdevice = m_hwdevices->rootSystemDevice();

    if (rdevice->formFactor() == TDESystemFormFactor::Laptop)
    {
        laptop = true;
    }
    else
    {
        laptop = false;
    }

    kdDebugFuncOut(trace);
}

/*!
 * The function checks whether the machine support ACPI/APM/PMU or not.
 */
void HardwareInfo::checkPowermanagement()
{
    kdDebugFuncIn(trace);

    TQString ret;

    has_APM = false;
    has_ACPI = false;
    has_PMU = false;

    TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::OtherACPI);
    if (hwlist.count() > 0)
    {
        has_ACPI = true;
    }

    // FIXME
    // Do we even need to detect APM and PMU devices in the year 2012?!?

    kdDebugFuncOut(trace);
}

/*!
 * The function checks whether the machine can suspend/standby.
 */
void HardwareInfo::checkSuspend()
{
    kdDebugFuncIn(trace);

    suspend_states = SuspendStates();

    suspend_states.suspend2ram = false;
    suspend_states.suspend2ram_can = false;
    suspend_states.suspend2ram_allowed = -1;
    suspend_states.suspend2disk = false;
    suspend_states.suspend2disk_can = false;
    suspend_states.suspend2disk_allowed = -1;
    suspend_states.freeze = false;
    suspend_states.freeze_can = false;
    suspend_states.freeze_allowed = -1;
    suspend_states.standby = false;
    suspend_states.standby_can = false;
    suspend_states.standby_allowed = -1;
    suspend_states.suspend_hybrid = false;
    suspend_states.suspend_hybrid_can = false;
    suspend_states.suspend_hybrid_allowed = -1;

    TDERootSystemDevice *rdevice = m_hwdevices->rootSystemDevice();
    TDESystemPowerStateList powerStates = rdevice->powerStates();
    if (powerStates.count() > 0)
    {
        TDESystemPowerStateList::iterator it;
        for (it = powerStates.begin(); it != powerStates.end(); ++it)
        {
            if ((*it) == TDESystemPowerState::Active)
            {
                //
            }
            else if ((*it) == TDESystemPowerState::Standby)
            {
                suspend_states.standby = true;
                suspend_states.standby_allowed = rdevice->canStandby();
                suspend_states.standby_can = suspend_states.standby_allowed && suspend_states.standby;
            }
            else if ((*it) == TDESystemPowerState::Freeze)
            {
                suspend_states.freeze = true;
                suspend_states.freeze_allowed = rdevice->canFreeze();
                suspend_states.freeze_can = suspend_states.freeze_allowed && suspend_states.freeze;
            }
            else if ((*it) == TDESystemPowerState::Suspend)
            {
                suspend_states.suspend2ram = true;
                suspend_states.suspend2ram_allowed = rdevice->canSuspend();
                suspend_states.suspend2ram_can = suspend_states.suspend2ram_allowed && suspend_states.suspend2ram;
            }
            else if ((*it) == TDESystemPowerState::Hibernate)
            {
                suspend_states.suspend2disk = true;
                suspend_states.suspend2disk_allowed = rdevice->canHibernate();
                suspend_states.suspend2disk_can = suspend_states.suspend2disk_allowed && suspend_states.suspend2disk;
            }
            else if ((*it) == TDESystemPowerState::HybridSuspend)
            {
                suspend_states.suspend_hybrid = true;
                suspend_states.suspend_hybrid_allowed = rdevice->canHybridSuspend();
                suspend_states.suspend_hybrid_can = suspend_states.suspend_hybrid_allowed && suspend_states.suspend_hybrid;
            }
            else if ((*it) == TDESystemPowerState::PowerOff)
            {
                //
            }
        }
    }

    kdDebugFuncOut(trace);
}

/*!
 * The function checks whether the machine support CPU frequency changes
 */
void HardwareInfo::checkCPUFreq()
{
    kdDebugFuncIn(trace);

    // Use the first CPU in the list; permissions are probably the same across all CPUs
    TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::CPU);
    TDEGenericDevice *hwdevice;
    hwdevice = hwlist.first();
    TDECPUDevice *cpudevice = static_cast<TDECPUDevice*>(hwdevice);

    if (!cpudevice->scalingDriver().isNull())
    {
        cpuFreq = true;
        cpuFreqAllowed = cpudevice->canSetGovernor();

        checkCurrentCPUFreqPolicy();
    }
    else
    {
        cpuFreq = false;
    }

    kdDebugFuncOut(trace);
}

/*!
 * The function check the currently selected CPU Frequency policy
 * \return the current policy
 */
cpufreq_type HardwareInfo::checkCurrentCPUFreqPolicy()
{
    kdDebugFuncIn(trace);

    // Use the first CPU in the list; permissions are probably the same across all CPUs
    TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::CPU);
    TDEGenericDevice *hwdevice;
    hwdevice = hwlist.first();
    TDECPUDevice *cpudevice = static_cast<TDECPUDevice*>(hwdevice);

    TQString gov = cpudevice->governor();

    cpufreq_type _current = UNKNOWN_CPUFREQ;

    if (cpuFreq)
    {
        if (!gov.isNull())
        {
            kdDebug() << "got CPU Freq gov: " << gov << endl;
            if ((gov == "ondemand") || (gov == "userspace") || (gov == "conservative"))
            {
                _current = DYNAMIC;
            }
            else if (gov == "powersave")
            {
                _current = POWERSAVE;
            }
            else if (gov == "performance")
            {
                _current = PERFORMANCE;
            }
            else
            {
                kdError() << "Got unknown CPUFreq Policy back: " << gov << endl;
            }
            cpuFreqGovernor = gov;
        }
        else
        {
            kdWarning() << "Could not get information about current governor" << endl;
        }
    }
    else
    {
        kdWarning() << "CPU Frequency interface not supported by machine or TDE hardware library" << endl;
    }

    if (_current != currentCPUFreqPolicy)
    {
        currentCPUFreqPolicy = _current;
        update_info_cpufreq_policy_changed = true;
        emit currentCPUFreqPolicyChanged();
    }
    else
    {
        update_info_cpufreq_policy_changed = false;
    }

    kdDebugFuncOut(trace);
    return currentCPUFreqPolicy;
}

/*!
 * The function checks whether the machine provide a brightness interface and init
 * (if needed) brightness information.
 */
void HardwareInfo::checkBrightness()
{
    kdDebugFuncIn(trace);

    TQStringList devices;

    brightness = false;
    currentBrightnessLevel = -1;
    availableBrightnessLevels = -1;

    // Use the first backlight in the list
    TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::Backlight);
    TDEGenericDevice *hwdevice;
    hwdevice = hwlist.first();
    TDEBacklightDevice *backlightdevice = static_cast<TDEBacklightDevice*>(hwdevice);

    if (backlightdevice)
    {
        udis.insert("laptop_panel", new TQString(hwdevice->uniqueID()));
        if (!allUDIs.contains(hwdevice->uniqueID()))
        {
            allUDIs.append(hwdevice->uniqueID());
        }

        availableBrightnessLevels = backlightdevice->brightnessSteps();

        if (availableBrightnessLevels > 1)
        {
            brightnessAllowed = backlightdevice->canSetBrightness();

            brightness = true;
            // get the current level via GetBrightness
            checkCurrentBrightness();
        }
        else
        {
            availableBrightnessLevels = -1;
        }
    }
    else
    {
        udis.remove("laptop_panel");
        kdDebug() << "no device with category laptop_panel found" << endl;
        kdDebugFuncOut(trace);
        return;
    }

    kdDebugFuncOut(trace);
}

/*!
 * The function check the current brigthness
 */
void HardwareInfo::checkCurrentBrightness()
{
    kdDebugFuncIn(trace);

    if (brightness)
    {
        // Use the first backlight in the list
        TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::Backlight);
        TDEGenericDevice *hwdevice;
        hwdevice = hwlist.first();
        TDEBacklightDevice *backlightdevice = static_cast<TDEBacklightDevice*>(hwdevice);

        if (backlightdevice)
        {
            currentBrightnessLevel = backlightdevice->rawBrightness();
        }
    }

    kdDebugFuncOut(trace);
}

/*!
 * The function initialise the hardware information and collect all
 * initial information from TDE hardware library.
 * \return boolean with result of the operation
 * \retval true  if successful
 * \retval false else, if a error occurs
 */
bool HardwareInfo::intialiseHWInfo()
{
    kdDebugFuncIn(trace);

    TDEGenericDevice *hwdevice;
    TDEGenericHardwareList hwlist;

    hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::PowerSupply);
    for (hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next())
    {
        udis.insert("acadapter", new TQString(hwdevice->uniqueID()));
        if (!allUDIs.contains(hwdevice->uniqueID()))
        {
            allUDIs.append(hwdevice->uniqueID());
        }
        checkACAdapterState();
    }

    hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::Event);
    for (hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next())
    {
        TDEEventDevice *edevice = dynamic_cast<TDEEventDevice*>(hwdevice);
        if (edevice->eventType() == TDEEventDeviceType::ACPILidSwitch)
        {
            udis.insert("lidclose", new TQString(hwdevice->uniqueID()));
            if (!allUDIs.contains(hwdevice->uniqueID()))
            {
                allUDIs.append(hwdevice->uniqueID());
            }
            connect(edevice, TQ_SIGNAL(switchChanged()), this, TQ_SLOT(checkLidcloseState()));
            checkLidcloseState();
        }
    }

    // find batteries and fill battery information
    hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::Battery);
    for (hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next())
    {
        if (!allUDIs.contains(hwdevice->uniqueID()))
        {
            allUDIs.append(hwdevice->uniqueID());
        }
        BatteryList.append(new Battery(hwdevice->uniqueID()));
    }
    // connect to signals for primary batteries:
    Battery *bat;
    for (bat = BatteryList.first(); bat; bat = BatteryList.next())
    {
        if (bat->getType() == BAT_PRIMARY)
        {
            connect(bat, TQ_SIGNAL(changedBattery()), this, TQ_SLOT(updatePrimaryBatteries()));
        }
    }

    kdDebugFuncOut(trace);
    return true;
}

/*!
 * The function/slot checks the state of the AC adapter.
 */
void HardwareInfo::checkACAdapterState()
{
    kdDebugFuncIn(trace);

    if (udis["acadapter"])
    {
        bool _state;

        TDEMainsPowerDevice *mdevice = dynamic_cast<TDEMainsPowerDevice*>(m_hwdevices->findByUniqueID(*udis["acadapter"]));
        if (mdevice)
        {
            _state = mdevice->online();
            if (_state != acadapter)
            {
                acadapter = _state;
                update_info_ac_changed = true;
                emit ACStatus(acadapter);
            }
            else
            {
                update_info_ac_changed = false;
            }
        }
        else
        {
            // we use true as default e.g. for workstations
            acadapter = true;
        }
    }

    kdDebugFuncOut(trace);
}

/*!
 * The function checks the state of the Lidclose button.
 */
void HardwareInfo::checkLidcloseState()
{
    kdDebugFuncIn(trace);

    if (udis["lidclose"])
    {
        bool _state;

        TDEEventDevice *edevice = dynamic_cast<TDEEventDevice*>(m_hwdevices->findByUniqueID(*udis["lidclose"]));
        if (edevice)
        {
            _state = (edevice->activeSwitches() & TDESwitchType::Lid);
            if (_state != lidclose)
            {
                lidclose = _state;
                emit lidclosetStatus(lidclose);
            }
        }
        else
        {
            lidclose = false;
        }
    }

    kdDebugFuncOut(trace);
}

/*!
 * This funtion is used to call a update of a battery value for a given
 * UDI and the given changed property
 * \param udi 		TQString with the UDI of the battery
 * \param property	TQString with the changed property
 */
void HardwareInfo::updateBatteryValues(TDEGenericDevice *device)
{
    kdDebugFuncIn(trace);

    if (device && allUDIs.contains(device->uniqueID()))
    {
        // find effected battery object
        Battery *bat;
        for (bat = BatteryList.first(); bat; bat = BatteryList.next())
        {
            if (device->uniqueID().startsWith(bat->getUdi()))
            {
                TDEBatteryDevice *bdevice = dynamic_cast<TDEBatteryDevice*>(device);
                if (bdevice)
                {
                    // found a battery with udi
                    bat->updateProperty(bdevice);
                }
            }
        }
    }
    else
    {
        kdDebug() << "UDI is empty or not in the list of monitored devices " << endl;
    }

    kdDebugFuncOut(trace);
    return;
}

/*!
 * This function refresh the information for the primary battery collection.
 */
void HardwareInfo::updatePrimaryBatteries()
{
    kdDebugFuncIn(trace);

    if (!BatteryList.isEmpty())
    {
        if (primaryBatteries->getNumBatteries() < 1)
        {
            setPrimaryBatteriesWarningLevel();
            primaryBatteries->refreshInfo(BatteryList);
            connect(primaryBatteries, TQ_SIGNAL(batteryChanged()), this, TQ_SLOT(setPrimaryBatteriesChanges()));
            connect(primaryBatteries, TQ_SIGNAL(batteryWarnState(int,int)), this, TQ_SLOT(emitBatteryWARNState(int,int)));
        }
        else
        {
            setPrimaryBatteriesWarningLevel();
            primaryBatteries->refreshInfo(BatteryList);
        }
    }
    else
    {
        primaryBatteries = new BatteryCollection(BAT_PRIMARY);
    }

    kdDebugFuncOut(trace);
}

/*!
 * This function set the change status for the primary battery collection
 */
void HardwareInfo::setPrimaryBatteriesChanges()
{
    kdDebugFuncIn(trace);

    update_info_primBattery_changed = true;
    emit primaryBatteryChanged();

    kdDebugFuncOut(trace);
}

/*!
 * This slot emit a signal if a warning state of a battery reached
 */
void HardwareInfo::emitBatteryWARNState(int type, int state)
{
    kdDebugFuncIn(trace);

    if (type == BAT_PRIMARY)
        emit primaryBatteryChanged();
    else
        emit generalDataChanged();

    emit batteryWARNState(type, state);

    kdDebugFuncOut(trace);
}

// --> init HW information section -- END <---
// --> TDE hardware library method call (trigger actions) section -- START <---

/*!
 * Function to trigger a suspend via TDE hardware library
 * \param suspend 	enum of suspend_type with the requested suspend
 * \return boolean with result of the operation
 * \retval true  	if successful
 * \retval false 	else, if a error occurs
 */
bool HardwareInfo::suspend(suspend_type suspend)
{
    kdDebugFuncIn(trace);

    calledSuspend = TQTime();

    TDERootSystemDevice *rdevice = m_hwdevices->rootSystemDevice();

    if (rdevice)
    {
        switch (suspend)
        {
        case SUSPEND2DISK:
            if (suspend_states.suspend2disk && (suspend_states.suspend2disk_allowed != 0))
            {
                if (rdevice->setPowerState(TDESystemPowerState::Hibernate))
                {
                    calledSuspend.start();
                    handleResumeSignal(0);
                    return true;
                }
                else
                {
                    handleResumeSignal(-1);
                    return false;
                }
            }
            else
            {
                if (!suspend_states.suspend2disk)
                    kdDebug() << "The machine does not support hibernation." << endl;
                else
                    kdWarning() << "Policy forbid user to trigger hibernation" << endl;

                return false;
            }
            break;

        case SUSPEND_HYBRID:
            if (suspend_states.suspend_hybrid && (suspend_states.suspend_hybrid_allowed != 0))
            {
                if (rdevice->setPowerState(TDESystemPowerState::HybridSuspend))
                {
                    calledSuspend.start();
                    handleResumeSignal(0);
                    return true;
                }
                else
                {
                    handleResumeSignal(-1);
                    return false;
                }
            }
            else
            {
                if (!suspend_states.suspend_hybrid)
                    kdDebug() << "The machine does not support hybrid suspension." << endl;
                else
                    kdWarning() << "Policy forbid user to trigger hybrid suspension" << endl;

                return false;
            }
            break;

        case SUSPEND2RAM:
            if (suspend_states.suspend2ram && (suspend_states.suspend2ram_allowed != 0))
            {
                if (rdevice->setPowerState(TDESystemPowerState::Suspend))
                {
                    calledSuspend.start();
                    handleResumeSignal(0);
                    return true;
                }
                else
                {
                    handleResumeSignal(-1);
                    return false;
                }
            }
            else
            {
                if (!suspend_states.suspend2ram)
                    kdDebug() << "The machine does not support Suspend mode." << endl;
                else
                    kdWarning() << "Policy forbid user to trigger Suspend mode" << endl;

                return false;
            }
            break;

        case FREEZE:
            if (suspend_states.freeze && (suspend_states.freeze_allowed != 0))
            {
                if (rdevice->setPowerState(TDESystemPowerState::Freeze))
                {
                    calledSuspend.start();
                    handleResumeSignal(0);
                    return true;
                }
                else
                {
                    handleResumeSignal(-1);
                    return false;
                }
            }
            else
            {
                if (!suspend_states.freeze)
                    kdDebug() << "The machine does not support freeze." << endl;
                else
                    kdWarning() << "Policy forbid user to trigger freeze" << endl;

                return false;
            }
            break;

        case STANDBY:
            if (suspend_states.standby && (suspend_states.standby_allowed != 0))
            {
                if (rdevice->setPowerState(TDESystemPowerState::Standby))
                {
                    calledSuspend.start();
                    handleResumeSignal(0);
                    return true;
                }
                else
                {
                    handleResumeSignal(-1);
                    return false;
                }
            }
            else
            {
                if (!suspend_states.standby)
                    kdDebug() << "The machine does not support standby." << endl;
                else
                    kdWarning() << "Policy forbid user to trigger standby" << endl;

                return false;
            }
            break;

        default:
            return false;
        }
    }

    kdDebugFuncOut(trace);
    return false;
}

/*!
 * Function to set brightness level via TDE hardware library (if supported by hardware)
 * \param level		Integer with the level to set, (range: 0 - \ref availableBrightnessLevels )
 * \return boolean with result of the operation
 * \retval true  	if successful
 * \retval false 	else, if a error occurs
 */
bool HardwareInfo::setBrightnessLevel(int level)
{
    if (trace)
        kdDebug() << funcinfo << "IN: " << "level: " << level << endl;

    bool retval = false;
    int maxBrightness = getMaxBrightnessLevel();

    // Use the first backlight in the list
    TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::Backlight);
    TDEGenericDevice *hwdevice;
    hwdevice = hwlist.first();
    TDEBacklightDevice *backlightdevice = static_cast<TDEBacklightDevice*>(hwdevice);

    if (backlightdevice)
    {
        if (!brightness)
        {
            checkBrightness();
        }

        // Make sure level is within valid range
        if (level < 0)
        {
            level = 0;
        }
        else if (level > maxBrightness)
        {
            level = maxBrightness;
        }

        if (!brightness)
        {
            kdError() << "Change brightness not supported " << endl;
        }
        else
        {
            if (currentBrightnessLevel == level)
            {
                kdDebug() << "Brightness level not changed, requested level == current level" << endl;
                retval = true;
            }
            else
            {
                backlightdevice->setRawBrightness(level);
                retval = true;
            }
        }
    }

    // check for actual brightness level to be sure everything was set correct
    checkCurrentBrightness();
    kdDebugFuncOut(trace);
    return retval;
}

/*!
 * Function to set brightness percentage via TDE hardware library (if supported by hardware)
 * \param percent	Integer with the brightness percentage to set
 * \return boolean with result of the operation
 * \retval true  	if successful
 * \retval false 	else, if a error occurs
 */
bool HardwareInfo::setBrightnessPercentage(int percent)
{
    if (trace)
        kdDebug() << funcinfo << "IN: " << " percent: " << percent << endl;

    bool retval = false;

    // Use the first backlight in the list
    TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::Backlight);
    TDEGenericDevice *hwdevice;
    hwdevice = hwlist.first();
    TDEBacklightDevice *backlightdevice = static_cast<TDEBacklightDevice*>(hwdevice);

    if (backlightdevice)
    {
        if (!brightness)
        {
            checkBrightness();
        }

        if (!brightness)
        {
            kdError() << "Change brightness not supported " << endl;
        }
        else
        {
            // Make sure percentage is within valid range
            if (percent < 0)
            {
                percent = 0;
            }
            else if (percent > 100)
            {
                percent = 100;
            }

            int level = (int) round(getMaxBrightnessLevel() * percent / 100.0);
            if (currentBrightnessLevel == level)
            {
                kdDebug() << "Brightness level not changed, requested level == current level" << endl;
                retval = true;
            }
            else
            {
                backlightdevice->setRawBrightness(level);
                retval = true;
            }
        }
    }

    // check for actual brightness level to be sure everything was set correct
    checkCurrentBrightness();
    kdDebugFuncOut(trace);
    return retval;
}

/*!
 * Function to set the CPU frequency policy via TDE hardware library.
 * \param cpufreq 	enum of cpufreq_type with the policy to set
 * \param limit 	integer with range 0 - 100 (only if cpufreq == DYNAMIC)
 * \return boolean with result of the operation
 * \retval true  	if successful
 * \retval false 	else, if a error occurs
 */
bool HardwareInfo::setCPUFreq(cpufreq_type cpufreq, int limit)
{
    if (trace)
        kdDebug() << funcinfo << "IN: " << "cpufreq_type: " << cpufreq << " limit: " << limit << endl;

    if (!cpuFreq)
    {
        kdError() << "This machine does not support change the CPU Freq via TDE hardware library" << endl;
        return false;
    }

    if (cpuFreqAllowed == 0)
    {
        kdError() << "Could not set CPU Freq - insufficient privileges." << endl;
        return false;
    }

    TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::CPU);

    if (hwlist.count() > 0)
    {
        TQStringList dynamic;

        if (checkCurrentCPUFreqPolicy() == cpufreq)
        {
            if (cpufreq == DYNAMIC && !cpuFreqGovernor.startsWith("ondemand"))
            {
                kdDebug() << "CPU Freq Policy is already DYNAMIC, but not governor is currently " << "not 'ondemand'. Try to set ondemand governor." << endl;
            }
            else
            {
                kdDebug() << "Didn't change Policy, was already set." << endl;
                return true;
            }
        }

        switch (cpufreq)
        {
        case PERFORMANCE:
            if (!setCPUFreqGovernor("performance"))
            {
                kdError() << "Could not set CPU Freq to performance policy" << endl;
                return false;
            }
            break;
        case DYNAMIC:
            dynamic << "ondemand" << "userspace" << "conservative";

            for (TQStringList::Iterator it = dynamic.begin();
                    it != dynamic.end(); it++)
            {
                kdDebug() << "Try to set dynamic CPUFreq to: " << *it << endl;

                if (setCPUFreqGovernor((*it).latin1()))
                {
                    kdDebug() << "Set dynamic successful to: " << *it << endl;
                    break;
                }
            }

            // Set performance limits on all CPUs
            TDEGenericDevice *hwdevice;
            TDECPUDevice *cpudevice;
            for (hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next())
            {
                cpudevice = static_cast<TDECPUDevice*>(hwdevice);
                // set dynamic performance limit
                // scale the desired limit so that when limit==0 the minFrequency() is used, and when limit==100 the maxFrequency() is used
                double cpuLimit = cpudevice->maxFrequency() - cpudevice->minFrequency();
                cpuLimit = (cpuLimit * limit) / 100.0;
                cpuLimit = cpudevice->maxFrequency() + cpuLimit;
                cpudevice->setMaximumScalingFrequency(cpuLimit);

            }

            break;
        case POWERSAVE:
            if (!setCPUFreqGovernor("powersave"))
            {
                kdError() << "Could not set CPU Freq to powersave policy" << endl;
                return false;
            }
            break;
        default:
            kdWarning() << "Unknown cpufreq_type: " << cpufreq << endl;
            return false;
        }

        // check if the policy was really set (and emit signal)
        if (checkCurrentCPUFreqPolicy() == cpufreq)
        {
//			update_info_cpufreq_policy_changed = true;
//			emit currentCPUFreqPolicyChanged();
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
}

/*!
 * Function to set the CPU governor via TDE hardware library.
 * \param governor	char * with the name of the governor
 * \return boolean with result of the operation
 * \retval true  	if successful
 * \retval false 	else, if a error occurs
 */
bool HardwareInfo::setCPUFreqGovernor(const char *governor)
{
    kdDebugFuncIn(trace);

    bool ret = true;

    TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::CPU);
    TDEGenericDevice *hwdevice;
    TDECPUDevice *cpudevice;
    // Set governor on all CPUs
    for (hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next())
    {
        cpudevice = static_cast<TDECPUDevice*>(hwdevice);
        cpudevice->setGovernor(governor);
        if (cpudevice->governor() != governor)
        {
            ret = false;
        }
    }

    kdDebugFuncOut(trace);
    return ret;
}

// --> TDE hardware library method call (trigger actions) section -- END <---

// --> private helper functions/slots to forward/handle events  -- START <--
//     need this functions to make events independent
//     from TQt event loop and to allow TQt3 D-Bus bindings to get not
//     blocked by normal TDE/TQt (GUI) calls
/*!
 * Function to emit the signal for the Power button.
 */
void HardwareInfo::emitPowerButtonPressed()
{
    if (sessionIsActive)
    {
        emit powerButtonPressed();
    }
    else
    {
        kdWarning() << "Session is not active, don't react on power button event!" << endl;
    }
}

/*!
 * Function to emit the signal for the Suspend button.
 */
void HardwareInfo::emitSuspendButtonPressed()
{
    if (sessionIsActive)
    {
        emit sleepButtonPressed();
    }
    else
    {
        kdWarning() << "Session is not active, don't react on sleep button event!" << endl;
    }
}

/*!
 * Function to emit the signal for the s2disk button.
 */
void HardwareInfo::emitS2diskButtonPressed()
{
    if (sessionIsActive)
    {
        emit s2diskButtonPressed();
    }
    else
    {
        kdWarning() << "Session is not active, don't react on suspend2disk button event!" << endl;
    }
}

/*!
 * Function to emit the signal about changes in the session state
 */
void HardwareInfo::emitSessionActiveState()
{
    emit desktopSessionIsActive(sessionIsActive);
}

/*!
 * Function to set the brightess a step up.
 * \param percentageStep Integer of next step should get set
 * \return result of the operation
 * \retval true		if could get set
 * \retval false	else
 */
bool HardwareInfo::setBrightnessUp(int percentageStep)
{
    kdDebugFuncIn(trace);

    bool retval = false;

    checkCurrentBrightness();

    if (supportBrightness() && percentageStep > 0)
    {
        int currentPerc = (int) round(100.0 * getCurrentBrightnessLevel() / getMaxBrightnessLevel());
        retval = setBrightnessPercentage(currentPerc + percentageStep);
    }

    kdDebugFuncOut(trace);
    return retval;
}

/*!
 * Function to set the brightess a step up.
 * \param percentageStep Integer of next step should get set
 * \return result of the operation
 * \retval true		if could get set
 * \retval false	else
 */
bool HardwareInfo::setBrightnessDown(int percentageStep)
{
    kdDebugFuncIn(trace);

    bool retval = false;

    checkCurrentBrightness();

    if (supportBrightness() && percentageStep > 0)
    {
        int currentPerc = (int) round(100.0 * getCurrentBrightnessLevel() / getMaxBrightnessLevel());
        retval = setBrightnessPercentage(currentPerc - percentageStep);
    }

    kdDebugFuncOut(trace);
    return retval;
}

// --> private helper slots to forward/handle events -- END <--

// --> get private members section -- START <---

/*!
 * The function return the current state of the ac adapter.
 * \return boolean with the current state
 * \retval true 	if adapter is present/connected or unknown
 * \retval false 	if not
 */
bool HardwareInfo::getAcAdapter() const
{
    return acadapter;
}

/*!
 * The function return the current state of the lidclose button.
 * \return boolean with the current state
 * \retval true 	if the lid is closed
 * \retval false 	if the lid is opend
 */
bool HardwareInfo::getLidclose() const
{
    return lidclose;
}

/*!
 * The function return the maximal available brightness level
 * \return Integer with max level or -1 if not supported
 */
int HardwareInfo::getMaxBrightnessLevel() const
{
    if (brightness)
        return availableBrightnessLevels - 1;
    else
        return -1;
}

/*!
 * The function return the current brightness level
 * \return Integer with max level or -1 if not supported or unkown
 */
int HardwareInfo::getCurrentBrightnessLevel() const
{
    if (brightness)
        return currentBrightnessLevel;
    else
        return -1;
}

/*!
 * The function return the current set CPU Frequency Policy
 * \return Integer with currently set Policy or -1 if not supported or unkown
 */
int HardwareInfo::getCurrentCPUFreqPolicy() const
{
    return currentCPUFreqPolicy;
}

/*!
 * The function return information if the system support the different
 * suspend/standby methodes and if the user can call them.
 * \return struct with information from \ref suspend_states
 * TODO: check if we maybe should replace this by more different functions
 */
SuspendStates HardwareInfo::getSuspendSupport() const
{
    return suspend_states;
}

/*!
 * The function return a pointer to the battery collection of primary batteries.
 * \return BatteryCollection with type == PRIMARY
 */
BatteryCollection* HardwareInfo::getPrimaryBatteries() const
{
    return primaryBatteries;
}

/*!
 * The function return all batteries
 * \return TQPtrList<Battery>
 */
TQPtrList<Battery> HardwareInfo::getAllBatteries() const
{
    return BatteryList;
}

/*!
 * The function return the status of \ref laptop.
 * \return boolean with info if machine is a laptop
 * \retval true 	if a laptop
 * \retval false 	else/if not a laptop
 */
bool HardwareInfo::isLaptop() const
{
    return laptop;
}

/*!
 * The function return info if there is a working connection to TDE hardware library.
 * This mean if we get hardwareinformation
 * \return boolean with info if TDE hardware library work
 * \retval true 	if connected
 * \retval false 	if not connected
 */
bool HardwareInfo::isOnline() const
{
    // FIXME
    // Is there ANY case where the TDE hardware library would not function?
    return true;
}

/*!
 * The function return the status of \ref has_ACPI.
 * \return boolean with info if machine support ACPI
 * \retval true 	if support ACPI
 * \retval false 	else
 */
bool HardwareInfo::hasACPI() const
{
    return has_ACPI;
}

/*!
 * The function return the status of \ref has_APM.
 * \return boolean with info if machine support APM
 * \retval true 	if support APM
 * \retval false 	else
 */
bool HardwareInfo::hasAPM() const
{
    return has_APM;
}

/*!
 * The function return the status of \ref has_PMU.
 * \return boolean with info if machine support PMU
 * \retval true 	if support PMU
 * \retval false 	else
 */
bool HardwareInfo::hasPMU() const
{
    return has_PMU;
}

/*!
 * The function return the status of \ref brightness.
 * \return boolean with info if machine support brightness changes via TDE hardware library
 * \retval true 	if support brightness changes
 * \retval false 	else
 */
bool HardwareInfo::supportBrightness() const
{
    return brightness;
}

/*!
 * The function return the status of \ref cpuFreq.
 * \return boolean with info if machine support change the CPU frequency via TDE hardware library
 * \retval true 	if support brightness changes
 * \retval false 	else
 */
bool HardwareInfo::supportCPUFreq() const
{
    return cpuFreq;
}

/*!
 * The function return the status of \ref sessionIsActive.
 * \return boolean with info if current desktop session is marked in ConsoleKit as activ
 * \retval true 	if the current session is active
 * \retval false 	else
 */
bool HardwareInfo::currentSessionIsActive() const
{
    return sessionIsActive;
}

/*!
 * The function return \ref cpuFreqAllowed and tell by this if the user is allowed
 * to change the CPU Freq.
 * \return \ref cpuFreqAllowed
 * \retval	0 allowed
 * \retval	1 not allowed
 * \retval	-1 unknown
 */
int HardwareInfo::isCpuFreqAllowed()
{
    // Use the first CPU in the list; permissions are probably the same across all CPUs
    TDEGenericHardwareList hwlist = m_hwdevices->listByDeviceClass(TDEGenericDeviceType::CPU);
    TDEGenericDevice *hwdevice;
    hwdevice = hwlist.first();
    TDECPUDevice *cpudevice = static_cast<TDECPUDevice*>(hwdevice);

    cpuFreqAllowed = cpudevice->canSetGovernor();
    return cpuFreqAllowed;
}

// --> get private members section -- END <---

#include "hardware.moc"
