/* Extended Module Player
 * Copyright (C) 1996, 1997 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * This file is part of the Extended Module Player and is distributed
 * under the terms of the GNU General Public License. See doc/COPYING
 * for more information.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "xpanel.h"

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "bg1.xpm"
#include "bg2.xpm"
#include "bg3.xpm"

#define rightmsg(f,x,y,s,c,b) \
    writemsg (f, (x) - writemsg (f,0,0,s,-1,0), y, s, c, b)

#define centermsg(f,x,y,s,c,b) \
    shadowmsg (f, (x) - writemsg (f,0,0,s,-1,0) / 2, y, s, c, b)


struct chn_c {
    int s;
    int x, y, w, h;
    int old_y;
};

extern struct font_header font1;
extern struct font_header font2;
extern struct ipc_info *ii;
extern int skip;
static char **bg;
static struct chn_c chn[64];



void volume_bars ()
{
    int x, y, w, n, r, i, v;

    n = (RES_X - 16) / ii->mi.chn;
    r = ((RES_X - 16) - n * ii->mi.chn) >> 1;

    w = n - 2;

    setcolor (12);
    for (i = 0; i < ii->mi.chn; i++) {

	v = ii->vol[i];
	if (v < 0)
	    v = 0;
	if (v > 0x40)
	    v = 0x40;
	x = i * n + 8 + r;
	y = (RES_Y - 8) - (RES_Y - 16) * v / 0x40;

	if (ii->mute[i]) {
	    if (chn[i].old_y > 0) {
		erase_rectangle (x, chn[i].old_y, w,
			RES_Y - 8 - chn[i].old_y);
		draw_rectangle (x, 8, w, RES_Y - 16);
		erase_rectangle (x + 2, 10, w - 4, RES_Y - 20);
		chn[i].old_y = -1;
	    }
	    chn[i].s = 1;
	    chn[i].y = 8;
	    chn[i].h = RES_Y - 16;
	    continue;
	} else if (chn[i].old_y < 0) {
	    draw_rectangle (x + 2, 10, w - 4, RES_Y - 20);
	    erase_rectangle (x, 8, w, RES_Y - 16);
	    chn[i].old_y = RES_Y - 8;
	    chn[i].s = 1;
	    chn[i].y = 8;
	    chn[i].h = RES_Y - 16;
	}

	if (y > chn[i].old_y) {
	    erase_rectangle (x, chn[i].old_y, w, y - chn[i].old_y);
	    if (!chn[i].s) {
		chn[i].s = 1;
		chn[i].y = chn[i].old_y;
		chn[i].h = y - chn[i].old_y;
	    }
	} else if (y < chn[i].old_y) {
	    draw_rectangle (x, y, w, chn[i].old_y - y);
	    if (!chn[i].s) {
		chn[i].s = 1;
		chn[i].y = y;
		chn[i].h = chn[i].old_y - y;
	    }
	}
	chn[i].old_y = y;
    }
}


void shadowmsg (struct font_header *f, int x, int y, char *s, int c, int b)
{
    writemsg (f, x + 2, y + 2, s, 0, b);
    writemsg (f, x, y, s, c, b);
}


void set_palette ()
{
    char **_bg[] =
    {bg1, bg2, bg3};

    bg = _bg[rand () % 3];
    setpalette (bg);
}


void prepare_screen ()
{
    char buf[90];

    draw_xpm (bg, RES_X, RES_Y);

#ifdef HAVE_SNPRINTF
    snprintf (buf, 80, "%s", ii->mi.name);
#else
    sprintf (buf, "%s", ii->mi.name);
#endif

    if (writemsg (&font1, 0, 0, buf, -1, 0) > RES_X) {
	while (writemsg (&font1, 0, 0, buf, -1, 0) > RES_X - 16)
	    buf[strlen (buf) - 1] = 0;
	strcat (buf, "...");
    }
    centermsg (&font1, RES_X / 2, 26, buf, 1, -1);
    sprintf (buf, "Channels: %d", ii->mi.chn);
    centermsg (&font2, RES_X / 2, 48, buf, 2, -1);
    sprintf (buf, "Instruments: %d", ii->mi.ins);
    centermsg (&font2, RES_X / 2, 66, buf, 2, -1);
    sprintf (buf, "Length: %d patterns", ii->mi.len);
    centermsg (&font2, RES_X / 2, 84, buf, 2, -1);
    sprintf (buf, "Pattern:");
    shadowmsg (&font2, 66, 102, buf, 2, -1);
    sprintf (buf, "Row:");
    shadowmsg (&font2, 176, 102, buf, 2, -1);
    sprintf (buf, "Progress:   %%");
    centermsg (&font2, RES_X / 2, 120, buf, 2, -1);

    putimage (0, 0, RES_X, RES_Y);
    update_display ();
}


void x11_event_callback (long i)
{
    static int chn = 0;
    static int ord = 0;
    long msg = i >> 4;
    int m, cmd = ii->cmd;

    switch (i & 0xf) {
    case XMP_ECHO_ORD:
	ord = msg & 0xff;
	ii->pat = msg >> 8;
	break;
    case XMP_ECHO_ROW:
	if ((m = msg >> 8) == 0)
	    m = 0x100;
	ii->progress = ord * 100 / ii->mi.len +
	    (msg & 0xff) * 100 / ii->mi.len / m;
	ii->row = msg & 0xff;
	break;
    case XMP_ECHO_CHN:
	chn = msg & 0xff;
	break;
    case XMP_ECHO_VOL:
	ii->vol[chn] = msg & 0xff;
	break;
    }

    switch (cmd) {
    case 'q':			/* quit */
	skip = -2;
	xmp_mod_stop ();
	if (ii->pause)
	    ii->pause = xmp_mod_pause ();
	break;
    case 'f':			/* jump to next order */
	xmp_ord_next ();
	if (ii->pause)
	    ii->pause = xmp_mod_pause ();
	break;
    case 'b':			/* jump to previous order */
	xmp_ord_prev ();
	if (ii->pause)
	    ii->pause = xmp_mod_pause ();
	break;
    case 'n':			/* skip to next module */
	skip = 1;
	xmp_mod_stop ();
	if (ii->pause)
	    ii->pause = xmp_mod_pause ();
	break;
    case 'p':			/* skip to previous module */
	skip = -1;
	xmp_mod_stop ();
	if (ii->pause)
	    ii->pause = xmp_mod_pause ();
	break;
    case ' ':			/* pause module */
	ii->pause = xmp_mod_pause ();
	break;
    default:
        if (cmd >= '1' && cmd <= '9') {
	    xmp_channel_mute (cmd - '1', 1, -1);
	    ii->mute[cmd - '1'] = !ii->mute[cmd - '1'];
	    break;
	}
        if (cmd == '0') {
	    xmp_channel_mute (9, 1, -1);
	    ii->mute[9] = !ii->mute[9];
	    break;
	}
        if (cmd == '!') {
	    xmp_channel_mute (0, 64, 0);
	    for (i = 0; i < 64; i++)
		ii->mute[i] = 0;
	    break;
	}
	if (cmd < 0) {
	    xmp_channel_mute (-cmd - 1, 1, !ii->mute[-cmd - 1]);
	    ii->mute[-cmd - 1] ^= 1;
	    break;
	}
    }

    ii->cmd = 0;
}


void display_loop ()
{
    int c, x, y, n, r;
    static char s[8];
    static int *b, *p, *q;

    p = malloc (15 * 13 * sizeof (int));
    b = malloc (22 * 13 * sizeof (int));
    q = malloc (22 * 13 * sizeof (int));

    n = (RES_X - 16) / ii->mi.chn;
    r = ((RES_X - 16) - n * ii->mi.chn) >> 1;

    get_rectangle (177, 106, 15, 13, p);
    get_rectangle (140, 88, 22, 13, b);
    get_rectangle (220, 88, 22, 13, q);

    for (c = 0; c < 64; c++)
	chn[c].old_y = RES_Y - 8;

    while (42) {
	switch ((c = process_events (&x, &y))) {
	case -1:		/* mute channel */
	    for (c = 0; c < ii->mi.chn; c++) {
		if (x >= chn[c].x && x < (chn[c].x + chn[c].w)) {
		    c = -c - 1;
		    break;
		}
	    }
	    if (c == 64)
		break;
	default:
	    if (!ii->cmd)
		ii->cmd = c;
	}

	if (ii->pause) {
	    update_display ();
	    if (!xmp_check_parent (125))
		continue;
	}

	put_rectangle (177, 106, 15, 13, p);
	put_rectangle (140, 88, 22, 13, b);
	put_rectangle (220, 88, 22, 13, q);
	volume_bars ();
	get_rectangle (177, 106, 15, 13, p);
	get_rectangle (140, 88, 22, 13, b);
	get_rectangle (220, 88, 22, 13, q);

	sprintf (s, "%d", ii->progress);
	shadowmsg (&font2, 177, 106 + 14, s, 2, -1);
	sprintf (s, "%02d", ii->pat);
	shadowmsg (&font2, 140, 86 + 16, s, 2, -1);
	sprintf (s, "%02d", ii->row);
	shadowmsg (&font2, 220, 86 + 16, s, 2, -1);

	for (c = 0; c < ii->mi.chn; c++) {
	    if (ii->vol[c] > 4)
		ii->vol[c] -= 4;
	    else
		ii->vol[c] = 0;
	}

	if (xmp_check_parent (40)) {
	    int n, r;
	    char buf[90];

	    xmp_wait_parent ();
	    put_rectangle (177, 106, 15, 13, p);
	    put_rectangle (140, 88, 22, 13, b);
	    put_rectangle (220, 88, 22, 13, q);
	    volume_bars ();
	    prepare_screen ();
	    get_rectangle (177, 106, 15, 13, p);
	    get_rectangle (140, 88, 22, 13, b);
	    get_rectangle (220, 88, 22, 13, q);

	    for (c = 0; c < 64; c++) {
		n = (RES_X - 16) / ii->mi.chn;
		r = ((RES_X - 16) - n * ii->mi.chn) >> 1;
        	chn[c].x = c * n + 8 + r;
        	chn[c].w = n - 2;
		chn[c].old_y = RES_Y - 8;
		ii->vol[c] = 0;
		ii->mute[c] = 0;
	    }

	    sprintf (buf, "xmp: %s", ii->filename);
	    settitle (buf);
	    xmp_tell_parent ();
	}

	update_display ();

	putimage (177, 106, 15, 13);
	putimage (140, 88, 22, 13);
	putimage (220, 88, 22, 13);

	for (c = 0; c < ii->mi.chn; c++) {
	    if (chn[c].s) {
		putimage (chn[c].x, chn[c].y, chn[c].w, chn[c].h);
		chn[c].s = 0;
	    }
	}

	update_display ();
    }
}

