/*
 * laptop_daemon.cpp
 * Copyright (C) 1999 Paul Campbell <paul@taniwha.com>
 *
 * this replaces kcmlaptop - there are 2 parts - one is the panels that
 *	put the setup configuration in the "kcmlaptop" configm, the other
 *	is the laptop_daemon (this guy) who watches the battery state
 *	and does stuff as a result
 *
 * This file contains the implementation of the main laptop battery monitoring daemon
 *
 * $Id: laptop_daemon.cpp,v 1.6 2000/04/23 15:55:46 jansen Exp $
 *
 *  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.
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdlib.h>

#include <kconfig.h>
#include <klocale.h>
#include <kcmdlineargs.h>
#include "laptop_daemon.h"
#include "portable.h"
#include "notify.h"
#include <kaboutdata.h>

#include <stdlib.h>

static const char *description = 
	I18N_NOOP("KDE Laptop Daemon");

static const char *version = "v0.0.1";

laptop_daemon::laptop_daemon(): KUniqueApplication()  
{
	exists = laptop_portable::has_power_management();
        backoff = 0;
        triggered[0] = 0;
        triggered[1] = 0;                 
	timer = 0;
	dock_widget = 0;
	oldTimer = 0;
        connect(this, SIGNAL(signal_checkBattery()), SLOT(checkBatteryNow()));

	//hasAudio = (audioServer.serverStatus() == 0) ? true : false;
}

laptop_daemon::~laptop_daemon()
{
}

int laptop_daemon::newInstance()
{
  restart();
  return 0;
}

void laptop_daemon::restart()
{
	exists = laptop_portable::has_power_management();
        if (oldTimer > 0) {
                killTimer(oldTimer);
		oldTimer=0;
	}
	if (timer) {
		timer->stop();
		delete timer;
		timer = 0;
	}

	KConfig *config = new KConfig("kcmlaptoprc"); 

	if (config == 0) {
		::fprintf(stderr, "laptop_daemon: can't open kcmlaptop config files\n");
		::exit(2);
	}

	config->setGroup("LaptopPower");
	power_action[0] = config->readNumEntry("PowerSuspend", 0);
	power_action[1] = config->readNumEntry("NoPowerSuspend", 1);
  	power_wait[0] = config->readNumEntry("PowerWait", 20);
  	power_wait[1] = config->readNumEntry("NoPowerWait", 5);  

	// General settings
	config->setGroup("BatteryDefault");
       	poll = config->readNumEntry("Poll", 20);
        docked = config->readBoolEntry("Docked", true);
        enabled = config->readBoolEntry("Enable", false);
        noBatteryIcon = config->readEntry("NoBatteryPixmap", "laptop_nobattery");
        noChargeIcon = config->readEntry("NoChargePixmap", "laptop_nocharge");
        chargeIcon = config->readEntry("ChargePixmap", "laptop_charge");   
	if(!enabled)
		docked = 0;

	int can_suspend = laptop_portable::has_suspend();

	config->setGroup("BatteryLow");
        low[0] = config->readNumEntry("LowVal", 15);
        runCommand[0] = config->readBoolEntry("RunCommand", false);
        runCommandPath[0] = config->readEntry("RunCommandPath");
        playSound[0] = config->readBoolEntry("PlaySound", false);
        playSoundPath[0] = config->readEntry("PlaySoundPath");
        systemBeep[0] = config->readBoolEntry("SystemBeep", true);
        notify[0] = config->readBoolEntry("Notify", true);
        do_suspend[0] = config->readBoolEntry("Suspend", false);
        do_standby[0] = config->readBoolEntry("Standby", false);
        have_time = config->readNumEntry("HaveTime", 2);

        if (!have_time && laptop_portable::has_battery_time())
        	have_time = 1;  

	config->setGroup("BatteryCritical");
        low[1] = config->readNumEntry("LowVal", 5);
        runCommand[1] = config->readBoolEntry("RunCommand", false);
        runCommandPath[1] = config->readEntry("RunCommandPath");
        playSound[1] = config->readBoolEntry("PlaySound", false);
        playSoundPath[1] = config->readEntry("PlaySoundPath");
        systemBeep[1] = config->readBoolEntry("SystemBeep", true);
        notify[1] = config->readBoolEntry("Notify", (can_suspend?false:true));
        do_suspend[1] = config->readBoolEntry("Suspend", (can_suspend?true:false));
        do_standby[1] = config->readBoolEntry("Standby", false);

	delete config;
	config = 0;

	//
	//	look for reasons NOT to run the daemon
	//
	if (!exists)
		shutDown(); 
 	if (!enabled && 
		!runCommand[0] && !playSound[0] && !systemBeep[0] && !notify[0] && !do_suspend[0] && !do_standby[0] &&
		!runCommand[1] && !playSound[1] && !systemBeep[1] && !notify[1] && !do_suspend[1] && !do_standby[1] &&
		power_action[0] == 0 && power_action[1] == 0) // if no reason to be running quit
		shutDown();

	// change the dock state if necessary

	if (enabled) {
		if (!dock_widget) {
			dock_widget = new laptop_dock(this);
			if (docked) {
		  		//KWin::setDockWindow(dock_widget->winId());
  				dock_widget->show();
			}
		}
	} else {
		if (dock_widget) {
			delete dock_widget;
			dock_widget = 0;
		}
	}
	

	last_time = time(0);
	if (power_action[0] || power_action[1]) {
		if (powered) {
			power_time = time(0)+60*power_wait[0];
		} else {
			power_time = time(0)+60*power_wait[1];
		}	
		timer =  new QTimer( this );
		connect( timer, SIGNAL(timeout()), this, SLOT(timerDone()) );
		timer->start( 2*1000, TRUE );                 // 1 seconds single-shot
	} else {
		timer = 0;
	}
	start_monitor();
}

void laptop_daemon::timerDone()
{
	unsigned long t = time(0);

	if (t >= (last_time+120)) {	// time suddenly jumped - we just powered up?
		backoff = 0;
		if (powered) {
			power_time = t+60*power_wait[0];
		} else {
			power_time = t+60*power_wait[1];
		}	
	} else
	if (backoff) {
		if (t >= power_time) {
			backoff = 0;
			if (powered) {
				power_time = t+60*power_wait[0];
			} else {
				power_time = t+60*power_wait[1];
			}	
		}
	} else
	if (laptop_portable::poll_activity()) {		// still active?
		if (powered) {
			power_time = t+60*power_wait[0];
		} else {
			power_time = t+60*power_wait[1];
		}	
	} else 
	if (t >= power_time) {
		int val;

		if (powered) {
			val = power_action[0];
		} else {
			val = power_action[1];
		}	
		switch (val) {
		case 1:
			invokeStandby();
			break;
		case 2:
			invokeSuspend();
			break;
		}
		backoff = 1;
		power_time = t+60;		// wait to give us time to get in and out of suspend prior to 
						// suspend
	}
	last_time = t;
	timer->start( 2*1000, TRUE );           // 1 seconds single-shot
}

void laptop_daemon::dock_quit()
{
	if (dock_widget)
		delete dock_widget;
	dock_widget = 0;
}

void laptop_daemon::haveBatteryLow(int t, const int num, const int type)
{
	displayPixmap();

	// beep if we are allowed to
	if (systemBeep[t]) {
		kapp->beep();
	}

	// run a command if we have to
	if (runCommand[t]) {
		// make sure the command exists
		if (!runCommandPath[t].isEmpty()) {
			KProcess command;
			command << runCommandPath[t];
			command.start(KProcess::DontCare);
		}
	}

	if (do_suspend[t])
		invokeSuspend();
	if (do_standby[t])
		invokeStandby();

	// play a sound if we have to
	if (playSound[t] && hasAudio) {
		// make sure something is specified
		if (!playSoundPath[t].isEmpty()) {
			//audioServer.play(playSoundPath[t]);
			//audioServer.sync();
		}
	}

	// notify if we must
	if (notify[t]) {
		KBatteryNotify notify_dlg(num, type);
		notify_dlg.exec();
	}
}


void laptop_daemon::checkBatteryNow()
{
	struct power_result p;

	p = laptop_portable::poll_battery_state();

	powered = p.powered;
	left = p.time;
	val = p.percentage;
	if (timer && oldpowered != powered) {
		if (powered) {
			power_time = time(0)+60*power_wait[0];
		} else {
			power_time = time(0)+60*power_wait[1];
		}	
	}
	changed =  oldpowered != powered||oldexists != exists||oldval != val;
	oldpowered = powered;
	oldexists = exists;
	oldval = val;
	if (changed)
		displayPixmap();
}

void laptop_daemon::start_monitor()
{
	checkBatteryNow();
	displayPixmap();
	oldTimer = startTimer(poll * 1000);
}

void laptop_daemon::setPollInterval(const int interval)
{
        poll = interval;

        // Kill any old timers that may be running
        if (oldTimer > 0) {
                killTimer(oldTimer);

                // Start a new timer will the specified time
                oldTimer = startTimer(interval * 1000);

                emit(signal_checkBattery());
        }
}

void laptop_daemon::timerEvent(QTimerEvent *)
{
        emit(signal_checkBattery());
}

int main(int argc, char **argv) 
{
    KAboutData aboutData( "klaptopdaemon", I18N_NOOP("KLaptop"),
        version, description, KAboutData::License_GPL,
        "(c) 1999, Paul Campbell");
    aboutData.addAuthor("Paul Campbell",0, "paul@taniwha.com");
    KCmdLineArgs::init( argc, argv, &aboutData );

    if (!laptop_daemon::start())
    {
       // Already running
       exit(0);
    }

    laptop_daemon a;
    return(a.exec());
}     

void laptop_daemon::displayPixmap()
{
	if (have_time == 2 && exists && !powered) {		// in some circumstances 
		KConfig *config = new KConfig("kcmlaptoprc"); 
		config->setGroup("BatteryLow");			// we can;t figure this out 'till
		have_time = (val < 0 ? 0 : 1);			// the battery is not charging
		config->writeEntry("HaveTime", have_time);
		config->sync();
		delete config;
	}
	
	if (dock_widget)
		dock_widget->displayPixmap();

	if (left >= 0) {
		if (!triggered[0]) {
			if (exists && !powered && left <= low[0]) {
				triggered[0] = 1;
				haveBatteryLow(0, left, 0);
			}
		} else {	
			if (!triggered[1]) {
				if (exists && !powered && left <= low[1]) {
					triggered[1] = 1;
					haveBatteryLow(1, left, 0);
				}
			}
			if (left > (low[1]+1))
				triggered[1] = 0;
			if (left > low[0])
				triggered[0] = 0;
		}
	} else
	if (have_time != 1) {
		if (!triggered[0]) {
			if (exists && !powered && val <= low[0]) {
				triggered[0] = 1;
				haveBatteryLow(0, val, 1);
			}
		} else {	
			if (!triggered[1]) {
				if (exists && !powered && val <= low[1]) {
					triggered[1] = 1;
					haveBatteryLow(1, val, 1);
				}
			}
			if (val > (low[1]+1))
				triggered[1] = 0;
			if (val > low[0])
				triggered[0] = 0;
		}
	}
}

void laptop_daemon::invokeStandby()
{
	laptop_portable::invoke_standby();
}

void laptop_daemon::invokeSuspend()
{
	laptop_portable::invoke_suspend();
}

#include "laptop_daemon.moc"
