/* cd: /usr/local/bin/lirccd# */

/* From /usr/include/bits/signum.h */
#define	SIGHUP		1	/* Hangup (POSIX).  */
#define	SIGINT		2	/* Interrupt (ANSI).  */
#define	SIGQUIT		3	/* Quit (POSIX).  */
#define	SIGILL		4	/* Illegal instruction (ANSI).  */
#define	SIGTRAP		5	/* Trace trap (POSIX).  */
#define	SIGABRT		6	/* Abort (ANSI).  */
#define	SIGIOT		6	/* IOT trap (4.2 BSD).  */
#define	SIGBUS		7	/* BUS error (4.2 BSD).  */
#define	SIGFPE		8	/* Floating-point exception (ANSI).  */
#define	SIGKILL		9	/* Kill, unblockable (POSIX).  */
#define	SIGUSR1		10	/* User-defined signal 1 (POSIX).  */
#define	SIGSEGV		11	/* Segmentation violation (ANSI).  */
#define	SIGUSR2		12	/* User-defined signal 2 (POSIX).  */
#define	SIGPIPE		13	/* Broken pipe (POSIX).  */
#define	SIGALRM		14	/* Alarm clock (POSIX).  */
#define	SIGTERM		15	/* Termination (ANSI).  */
#define	SIGSTKFLT	16	/* Stack fault.  */
#define	SIGCLD		SIGCHLD	/* Same as SIGCHLD (System V).  */
#define	SIGCHLD		17	/* Child status has changed (POSIX).  */
#define	SIGCONT		18	/* Continue (POSIX).  */
#define	SIGSTOP		19	/* Stop, unblockable (POSIX).  */
#define	SIGTSTP		20	/* Keyboard stop (POSIX).  */
#define	SIGTTIN		21	/* Background read from tty (POSIX).  */
#define	SIGTTOU		22	/* Background write to tty (POSIX).  */
#define	SIGURG		23	/* Urgent condition on socket (4.2 BSD).  */
#define	SIGXCPU		24	/* CPU limit exceeded (4.2 BSD).  */
#define	SIGXFSZ		25	/* File size limit exceeded (4.2 BSD).  */
#define	SIGVTALRM	26	/* Virtual alarm clock (4.2 BSD).  */
#define	SIGPROF		27	/* Profiling alarm clock (4.2 BSD).  */
#define	SIGWINCH	28	/* Window size change (4.3 BSD, Sun).  */
#define	SIGPOLL		SIGIO	/* Pollable event occurred (System V).  */
#define	SIGIO		29	/* I/O now possible (4.2 BSD).  */
#define	SIGPWR		30	/* Power failure restart (System V).  */
#define SIGSYS		31	/* Bad system call.  */

global files = array(), custom = array(), playlist = array();
global args = array("");
global alarms = array();
global alarmnames = array("Playback", "Stop", "Volume raise");
global child = -1, paused = 0, curfile = 0, oldfile;
global dieaction = 2, rprev = -1, rnext = -1;
global cookie = -1;
global bsl = -1, bsr = -1;

function say(str)
{
    exec("say \c" + str + "\c &");
}

function saysync(str, pause)
{
    var prepaused;
    
    prepaused = paused;
    pause(pause);
    exec("say \c" + str + "\c");
    if(!prepaused)
	pause(0);
}

function died(id)
{
    discardchild(id);
    if(child == id)
	child = -1;
    if(dieaction == 1)
    {
	if((++curfile) >= (int)playlist)
	    curfile = 0;
	restart(1);
    }
    if(dieaction == 2)
	restart(1);
    if(dieaction == 3)
    {
	rprev = curfile;
	rnext = -1;
	curfile = rand((int)playlist);
	restart(1);
    }
}

function killcur()
{
    if(child >= 0)
    {
	if(running(child))
	{
	    if(paused)
		killchild2(child, SIGCONT);
	    killchild(child);
	}
	discardchild(child);
	child = -1;
    }
    paused = 0;
}

function restart(force)
{
    var running;
    
    if(child >= 0)
    {
	running = running(child);
	killcur();
    } else {
	running = 0;
    }
    if(running || force)
    {
	if(curfile < 0)
	{
	    curfile = ((int)playlist) - 1;
	    saysync("Negative song number", 0);
	}
	if(curfile >= (int)playlist)
	{
	    curfile = 0;
	    saysync("Song number overflow", 0);
	}
	ext = strend(playlist[curfile], strrchr(playlist[curfile], "."));
	if(strlower(ext) == ".mp3")
	    child = execarray("mpg123", array("mpg123", "-o", "oss", "-q", playlist[curfile]));
	if(strlower(ext) == ".ogg")
	    child = execarray("sox", array("sox", "-t", ".ogg", playlist[curfile], "-t", "ossdsp", "/dev/dsp"));
	childnotify(child, "died");
    }
}

function pause(pause)
{
    paused = 0;
    if(child != -1)
    {
	if(running(child))
	{
	    if(paused = pause)
		killchild2(child, SIGSTOP);
	    else
		killchild2(child, SIGCONT);
	} else {
	    discardchild(child);
	    child = -1;
	}
    }
}

function globmusic()
{
    files = array();
    dirs = expand(file(getenv("HOME") + "/.mp3/dirs"), "\n");
    for(i = 0; i < (int)dirs; i++)
    {
	tfiles = glob(dirs[i] + "/*.[Mm][Pp]3", 0);
	tfiles += glob(dirs[i] + "/*.[Oo][Gg][Gg]", 0);
	sort(tfiles);
	files += tfiles;
    }
}

function init()
{
    globmusic();
    playlist = files;
}

function gualarm(id)
{
    var i, info;
    
    for(i = 0; i < (int)alarms; i++)
    {
	if(alarms[i][0] == id)
	{
	    info = alarms[i];
	    alarms -= i;
	    return(info);
	}
    }
    /* Beware of NULL values */
}

function volalarm(id)
{
    gualarm(id);
    exec("aumix -v100 -w75");
}

function playalarm(id)
{
    var song;
    
    song = gualarm(id)[3];
    if(song >= 0)
	curfile = song;
    restart(1);
}

function stopalarm(id)
{
    gualarm(id);
    killcur();
}

function grargs()
{
    var largs;
    
    largs = args;
    args = array("");
    return(largs);
}

function playsound(file)
{
    exec("sox " + getenv("HOME") + "/sounds/" + file + " -t ossdsp /dev/dsp &");
}

bind recursively fallback "[0-9]"
{
    args[(int)args - 1] += button;
    say(button);
}

bind recursively "Zoom" args += "";

bind recursively fallback "Vol-"
{
    var largs;
    
    largs = grargs();
    if(largs[0] == "")
	exec("aumix -w-5");
    else
	exec("aumix -w" + largs[0]);
}

bind recursively fallback "Vol\\+"
{
    var largs;
    
    largs = grargs();
    if(largs[0] == "")
	exec("aumix -w+5");
    else
	exec("aumix -v" + largs[0]);
}
    
bind recursively ".*"
{
    if(!regcmp("[0-9]", button, NULL))
	playsound("click02.wav");
    else if(button == "Zoom")
	playsound("click01.wav");
    else if((button == "Vol+") || (button == "Vol-"))
	playsound("click02.wav");
    else if(button == "Chan_Last")
	playsound("click03.wav");
    else
	playsound("click04.wav");
}

bind recursively "Chan_Last" grargs();

bind recursively "Info" saysync((string)file(getenv("HOME") + "/phone/lastname"), 1);

bind "F_RADIO" subtree
{
    say("MP3 Control");
    
    bind "Play"
    {
	var largs;
	
	if((child == -1) || !paused)
	{
	    largs = grargs();
	    if(largs[0] != "")
		curfile = (int)(largs[0]);
	    restart(1);
	} else {
	    pause(0);
	}
    }

    bind "Stop"
    {
	if(child == -1)
	    curfile = 0;
	else
	    killcur();
    }
    
    bind "Pause"
    {
	pause(!paused);
    }

    bind "Rewind"
    {
	if((dieaction == 3) && (rprev != -1))
	{
	    rnext = curfile;
	    curfile = rprev;
	    rprev = -1;
	} else {
	    if(dieaction == 3)
		rnext = -1;
	    if((--curfile) < 0)
		curfile = (int)playlist - 1;
	}
	restart(0);
    }
    
    bind "FF"
    {
	if((dieaction == 3) && (rnext != -1))
	{
	    rprev = curfile;
	    curfile = rnext;
	    rnext = -1;
	} else {
	    if(dieaction == 3)
		rprev = -1;
	    if((++curfile) >= (int)playlist)
		curfile = 0;
	}
	restart(0);
    }

    bind "Redo"
    {
	if((++dieaction) > 3)
	    dieaction = 0;
	if(dieaction == 0)
	    say("Stop");
	if(dieaction == 1)
	    say("Continue");
	if(dieaction == 2)
	    say("Repeat");
	if(dieaction == 3)
	    say("Random");
	rprev = rnext = -1;
    }

    bind recursively "Preview" saysync(pipe("date +%H:%M:%S"), 1);

    bind "Next"
    {
	file = strend(playlist[curfile], strrchr(playlist[curfile], "/") + 1);
	file = strbeg(file, strrchr(file, "."));
	say((string)curfile + " -- " + file);
    }
    
    bind "Left"
    {
	var largs;
	
	largs = grargs();
	if((int)largs > 1) {
	    bsl = 0;
	}
	if((bsl != -1) && (curfile > bsl)) {
	    bsr = curfile - 1;
	    say((string)(bsr - bsl + 1));
	    curfile = bsl + ((bsr - bsl) / 2);
	    restart(1);
	}
    }

    bind "Right"
    {
	var largs;
	
	largs = grargs();
	if((int)largs > 1) {
	    bsr = (int)playlist;
	}
	if((bsl != -1) && (curfile < bsr)) {
	    bsl = curfile + 1;
	    say((string)(bsr - bsl + 1));
	    curfile = bsl + ((bsr - bsl) / 2);
	    restart(1);
	}
    }

    bind "Middle"
    {
	var i, o, groups, conts, curg, largs;
	
	largs = grargs();
	if((int)largs > 1) {
	    bsl = 0;
	    bsr = (int)playlist - 1;
	    curfile = bsr / 2;
	} else {
	    if(dieaction == 3)
	    {
		rprev = curfile;
		rnext = -1;
	    }
	    curfile = rand((int)playlist);
	}
/*
	groups = array();
	conts = array();
	for(i = 0; i < (int)playlist; i++)
	{
	    curg = NULL;
	    for(o = 0; (strmid(playlist[i], o, 1) != "") && (strmid(playlist[i], o, 3) != " - "); o++);
	    if(strmid(playlist[i], o, 3) == " - ")
	    {
		curg = strbeg(playlist[i], o);
	    } else {
		curg = "";
	    }
	    for(o = 0; (o < (int)groups) && (groups[o] != curg); o++);
	    if(o == (int)groups)
	    {
		groups += (string)curg;
		conts += array(array());
	    }
	    conts[o] += (int)i;
	}
	i = rand((int)conts);
	o = rand((int)(conts[i]));
	echo(i); echo((int)(conts[i])); echo(o);
	curfile = conts[i][o];
*/
	restart(1);
    }
    
    bind "Menu" subtree
    {
	say("Choose sub menu");
	
	bind "1" subtree
	{
	    say("Scheduling mode");

	    bind "Play"
	    {
		var largs, day;
		
		largs = grargs();
		hour = (int)(largs[0]) / 100;
		min = (int)(largs[0]) - (hour * 100);
		day = timearray(time())[2];
		time = mktime(-1, -1, day, hour, min, 0);
		if(time < time())
		    time = mktime(-1, -1, day + 1, hour, min, 0);
		if(((int)largs > 1) && (largs[1] != ""))
		{
		    alarms += (array)array(alarm(time, "playalarm"), 0, time, (int)(largs[1]));
		    file = strend(playlist[(int)(largs[1])], strrchr(playlist[(int)(largs[1])], "/") + 1);
		    file = strbeg(file, strrchr(file, "."));
		    say("Playback of " + file + " scheduled at " + (string)hour + ":" + (string)min);
		} else {
		    alarms += (array)array(alarm(time, "playalarm"), 0, time, -1);
		    say("Playback scheduled at " + (string)hour + ":" + (string)min);
		}
	    }
	    
	    bind "Stop"
	    {
		var largs, day;
		
		largs = grargs();
		hour = (int)(largs[0]) / 100;
		min = (int)(largs[0]) - (hour * 100);
		day = timearray(time())[2];
		time = mktime(-1, -1, day, hour, min, 0);
		if(time < time())
		    time = mktime(-1, -1, day + 1, hour, min, 0);
		alarms += (array)array(alarm(time, "stopalarm"), 1, time);
		say("Stop scheduled at " + (string)hour + ":" + (string)min);
	    }
	    
	    bind "Vol\\+"
	    {
		var largs, day;
		
		largs = grargs();
		hour = (int)(largs[0]) / 100;
		min = (int)(largs[0]) - (hour * 100);
		day = timearray(time())[2];
		time = mktime(-1, -1, day, hour, min, 0);
		if(time < time())
		    time = mktime(-1, -1, day + 1, hour, min, 0);
		alarms += (array)array(alarm(time, "volalarm"), 2, time);
		say("Volume raise scheduled at " + (string)hour + ":" + (string)min);
	    }
	    
	    bind "^L$"
	    {
		var alist, i, ti;
		
		alist = "Alarm list: ";
		for(i = 0; i < (int)alarms; i++)
		{
		    alist += (string)i + ", ";
		    if((alarms[i][1] == 0) && (alarms[i][3] == -1))
			alist += "playback";
		    if((alarms[i][1] == 0) && (alarms[i][3] != -1))
			alist += "sc playback";
		    if(alarms[i][1] == 1)
			alist += "stop";
		    if(alarms[i][1] == 2)
			alist += "volraise";
		    ti = timearray(alarms[i][2]);
		    alist += " " + (string)(ti[3]) + ":" + (string)(ti[4]);
		    alist += ": ";
		}
		say(alist);
	    }
	    
	    bind "Power"
	    {
		var largs, a;
		
		largs = grargs();
		a = (int)(largs[0]);
		if((a >= (int)alarms) || (calarm(alarms[a][0])))
		{
		    say("No such alarm");
		} else {
		    alarms -= a;
		    say("Alarm " + (string)a + " cancelled");
		}
	    }
	    
	    bind "Undo"
	    {
		say("Exit scheduling");
		endbinding();
	    }
	}
	
	bind "2"
	{
	    globmusic();
	    playlist = files;
	    say("Music files reglobbed");
	}
	
	bind "3" subtree
	{
	    {
		say("Playlist editor");
		playlist = files;
		oldfile = curfile;
		curfile = 0;
	    }
	    
	    bind "Record"
	    {
		custom += playlist[curfile++];
		restart(0);
	    }
	    
	    bind "Play"
	    {
		var largs;
		
		largs = grargs();
		if(largs[0] != "")
		    curfile = (int)(largs[0]);
		restart(1);
	    }
	    
	    bind "Stop" killcur();
	    
	    bind "Rewind"
	    {
		if((--curfile) < 0)
		    curfile = (int)playlist - 1;
		restart(0);
	    }
	    
	    bind "FF"
	    {
		if((++curfile) >= (int)playlist)
		    curfile = 0;
		restart(0);
	    }
	    
	    bind "Redo"
	    {
		custom = array();
		say("Playlist erased");
	    }
	    
	    bind "Left"
	    {
		var largs;
		
		largs = grargs();
		custom = expand(file(getenv("HOME") + "/.mp3/pl" + largs[0]), "\n");
		say("Playlist " + largs[0] + " loaded");
	    }
	    
	    bind "Right"
	    {
		var largs;
		var i, data;
		
		largs = grargs();
		data = "";
		for(i = 0; i < (int)custom; i++)
		    data += custom[i] + "\n";
		tofile(getenv("HOME") + "/.mp3/pl" + largs[0], data);
		say("Playlist " + largs[0] + " saved");
	    }
	    
	    bind "Undo"
	    {
		say("Exit playlist editor");
		curfile = oldfile;
		if((int)custom > 0)
		    playlist = custom;
		else
		    playlist = files;
		endbinding();
	    }
	}
	
	bind "4" subtree
	{
	    say("Lock mode");
	    
	    bind "Vol[+-]"
	    {
		;
	    }
	    
	    bind "Middle"
	    {
		var time;
		
		if(child >= 0)
		{
		    killcur();
		    exec("aumix -v85 -w40");
		    time = time() + 400 + rand(300);
		    alarms += (array)array(alarm(time, "playalarm"), 0, time, (int)curfile);
		    alarms += (array)array(alarm(time, "volalarm"), 2, time);
		    say("Snoozing");
		}
	    }
	    
	    bind "Undo"
	    {
		var largs;
		
		largs = grargs();
		if((cookie < 0) || ((int)(largs[0]) == 0))
		{
		    cookie = rand(9999) + 1;
		    say("Magic cookie: " + (string)cookie);
		} else if((int)(largs[0]) == cookie) {
		    cookie = -1;
		    say("Exit lock mode");
		    endbinding();
		}
	    }
	}
	
	bind "5"
        {
	    var who, dpy, i;
	    
	    who = expand(pipe("who"), "\n");
	    for(i = 0; i < (int)who; i++)
	    {
		if(!regcmp("^fredrik +(:[0-9]+)", who[i], dpy))
		    exec("/usr/X11R6/bin/xset -display " + dpy[1] + " dpms force off");
	    }
        }
	
	bind "6"
        {
	    var who, dpy, i;
	    
	    who = expand(pipe("who"), "\n");
	    for(i = 0; i < (int)who; i++)
	    {
		if(!regcmp("^fredrik +(:[0-9]+)", who[i], dpy))
		    exec("/usr/X11R6/bin/xset -display " + dpy[1] + " dpms force on");
	    }
        }
	
	bind "Undo"
	{
	    say("Exit function menu");
	    endbinding();
	}
    }

    bind "Undo"
    {
	say("Exit MP3 control");
	endbinding();
    }
}

bind "F_TV" subtree
{
    say("MPlayer control");

    bind "Left"
    {
	var largs;
	
	largs = grargs();
	if(largs[0])
	    sendstring("mplayer", "seek -" + largs[0]);
	else
	    sendstring("mplayer", "seek -10");
    }
    
    bind "Right"
    {
	var largs;
	
	largs = grargs();
	if(largs[0])
	    sendstring("mplayer", "seek +" + largs[0]);
	else
	    sendstring("mplayer", "seek +10");
    }
    
    bind "Pause" sendstring("mplayer", "pause");
    bind "Fullscreen" sendstring("mplayer", "vo_fullscreen");
    bind "Ch-" sendstring("mplayer", "audio_delay -0.100");
    bind "Ch\\+" sendstring("mplayer", "audio_delay 0.100");
    
    bind "Undo"
    {
	say("Exit MPlayer control");
	endbinding();
    }
}

