/*
 *  screen.c - Handling of the screen using the ncurses-package.
 *
 *  (C) 1994 Mikael Nordqvist (d91mn@efd.lth.se, mech@df.lth.se)
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ncurses.h>

#include "mod.h"

#define ATTR_LINE      (COLOR_PAIR(7) | A_ALTCHARSET)
#define ATTR_BOLDLINE  (COLOR_PAIR(7) | A_ALTCHARSET | A_BOLD)

#define ATTR_FILE      ATTR_LINE
#define ATTR_BOLDFILE  ATTR_BOLDLINE
#define ATTR_DIR       (COLOR_PAIR(2) | A_BOLD)
#define ATTR_BOLDDIR   (COLOR_PAIR(4) | A_BOLD)

#define ATTR_REDBAR    (COLOR_PAIR(1) | A_BOLD | A_ALTCHARSET)
#define ATTR_DIMREDBAR (COLOR_PAIR(11) | A_DIM  | A_ALTCHARSET)

#define ATTR_PANEL     (COLOR_PAIR(3) | A_BOLD)
#define ATTR_PANELTEXT (COLOR_PAIR(9) | A_BOLD)

#define ATTR_PANEL2    (COLOR_PAIR(8) | A_BOLD)

#define ATTR_PANELBOX  (COLOR_PAIR(5) | A_BOLD)
#define ATTR_PANELBOXDIM (COLOR_PAIR(10))
#define ATTR_PANELBOX2 (COLOR_PAIR(6) | A_BOLD)

WINDOW *swin;

int swin_LINES;

extern struct mod_info M;
extern struct options opt;
extern int nr_visible_voices;
extern char loaded;

extern int nr_songs, nr_files, nr_dirs;

inline void wsetcol(WINDOW *w, chtype col)
{ 
	wattrset(w, col);
}

inline void setcol(chtype col)
{
	attrset(col);
}

void init_screen(void)
{
    if(!opt.interactive)
	return;

    initscr();

#ifdef NCURSES_CHECK_FOR_COLOR
    if(!has_colors())
	error("No colour support on this terminal\n");
#endif
    if(LINES < 19)
	error("You have to have at least 19 lines to run this program.\n");

    swin_LINES=MIN(opt.max_lines, LINES-16-2);
    
    swin=newwin(swin_LINES, COLS, 17, 0);
    setup_color_mode();
    cbreak();
    noecho();
    scrollok(stdscr, FALSE);
    leaveok(stdscr, TRUE);
    leaveok(swin, TRUE);
    keypad(stdscr, TRUE);
    curs_set(0); /* Turn the cursor off */
}


void cleanup_screen(void)
{
    if(!opt.interactive)
	return;

    delwin(swin);
    keypad(stdscr, FALSE);
    setcol(COLOR_PAIR(0));
    clear();
    refresh();
    curs_set(1); /* Make cursor visible again */
    endwin();
}


void draw_screen()
{
    int i;

    if(!opt.interactive)
	return;

    setcol(ATTR_LINE);
    clear();
    refresh();
    wsetcol(swin, ATTR_LINE);
    wclear(swin);
    wrefresh(swin);

    setcol(ATTR_PANEL);
    for(i=0; i < 16; ++i)
	mvaddstr(i, 0, "                                                   "
		 "                             ");

    mvaddstr(1, 1, "Status                            "
	     "Type    MOD  Songpos    /     Speed");
    mvaddstr(2, 1, "                                  "
	     "Voices   16  Pattern    /     Tempo");
    mvaddstr(3, 1, "Song                              "
	     "Samples  16  Line       /");
    mvaddstr(5, 1, "File                              "
	     "Size         Time        :");

    refresh();

    print_pos(-1,0);
    print_speed(-1,0);
    print_songinfo(-1,0,0,0,0,0);
    print_time(-1);

    print_minihelp();

    setcol(ATTR_PANEL);
    mvaddstr(7, 2, "# Samplename               Size Volume Looped Tune Bits "
	     "Type");
    refresh();
    
    print_songname(" ");
    print_filename(-1);
    print_samples(-1);

    print_channelnumbers(0);
    print_status("");

    setcol(ATTR_REDBAR);
    mvaddstr(swin_LINES+18-1, 0, "                   Mod version 0.6 - "
	     "(C) 1994 Mikael Nordqvist                  ");
    refresh();
}


void clear_screen(void)
{
    if(!opt.interactive)
	return;

    wsetcol(swin, COLOR_PAIR(7));
    werase(swin);
    wrefresh(swin);
}


void print_channelnumbers(int nr)
{
    char buf[81];
    int i, x, y, delta, actualvoices;
    struct event dummy={0,0,0,0,0,0};
    
    if(!opt.interactive)
	return;
    
    setcol(ATTR_REDBAR);
    if(!loaded) {
	mvprintw(16, 0, fill_string("", 80));
    }
    else {
	delta=print_event(buf, &dummy); /* to find out characters/channel */
	actualvoices=MIN(nr_visible_voices, M.nr_voices);
	mvprintw(16, 0, "  %s %s", (nr ? "<<" : "  "),
		 fill_string("", (80-5-4-actualvoices*delta)/2+(delta-2)/2));
	refresh();
	
	for(i=nr; i < nr+actualvoices-1; ++i) {
	    if(opt.active_voices & (1<<i))
		setcol(ATTR_REDBAR);
	    else
		setcol(ATTR_DIMREDBAR);
	    printw("%2d%s", i+1, fill_string("", delta-2));
	    refresh();
	}
	if(opt.active_voices & (1<<i))
	    setcol(ATTR_REDBAR);
	else
	    setcol(ATTR_DIMREDBAR);
	
	getyx(stdscr, y, x);
	printw("%2d%s", i+1, fill_string("", 80-1-2-x-4));
	refresh();
	setcol(ATTR_REDBAR);
	printw("%s  ",	(i < M.nr_voices-1 ? ">>" : "  "));
    }
    refresh();
}


void print_selectingbar(void)
{
    if(!opt.interactive)
	return;

    setcol(ATTR_REDBAR);
    
    mvprintw(16, 0, "   Size  Filename                      "
	     "            Directories: %3d Files: %4d ",
	     nr_dirs, nr_files-nr_dirs);
    refresh();
}


void print_songname(char *n)
{
    char noname[]="<no name>";

    if(!opt.interactive)
	return;

    if(n && !strlen(n))
	n=noname;
    setcol(ATTR_PANELBOX);
    mvaddstr(3, 6, fill_string(n, 27));
    refresh();
}


void print_filename(int s)
{
    if(!opt.interactive)
	return;

    setcol(ATTR_PANELBOX);
    mvaddstr(5, 6, fill_string((s == -1 ? "" : get_modulename(s)), 27));
    refresh();
}


void print_status(char *s)
{
    if(!opt.interactive)
	return;

    setcol(ATTR_PANELBOX);
    mvaddstr(1, 8, fill_string(s ? s : "", 25));
    refresh();
}


void print_files(int n)
{
    int i, f;
    int oldattr, newattr;

    if(!opt.interactive)
	return;

    f=n-swin_LINES/2;
    if(!(swin_LINES%2))
	f++;
    
    oldattr=ATTR_FILE;
    wsetcol(swin, oldattr);
    for(i=0; i < swin_LINES; ++i, ++f) {
	if(f < 0 || f >= nr_files) {
	    wmove(swin, i, 0);
	}
	else {
	    if(f != n) {
		if(is_dir(f))
		    newattr=ATTR_DIR;
		else
		    newattr=ATTR_FILE;
	    }
	    else {
		if(is_dir(f))
		    newattr=ATTR_BOLDDIR;
		else
		    newattr=ATTR_BOLDFILE;
	    }
	    if(newattr != oldattr) {
		wrefresh(swin);
		wsetcol(swin, newattr);
		oldattr=newattr;
	    }
	    mvwaddstr(swin, i, 0, get_filestring(f));
	}
	wclrtoeol(swin);
    }
    wrefresh(swin);
}


void print_samples(int n)
{
    int i;
    struct sample_info *s;
    
    if(!opt.interactive)
	return;

    setcol(ATTR_PANELBOX);
    for(i=0; i < 8; ++i) {
	if(n == -1)
	    mvwaddstr(stdscr, 8+i, 1,
		      "                                                "
		      "               ");
	else {
	    s=&M.sample[n];
	    if(s->length > 4)
		setcol(ATTR_PANELBOX);
	    else
		setcol(ATTR_PANELBOXDIM);

	    mvwprintw(stdscr, 8+i, 1,
		      "%2d %s %6d  %3d    %s   %+2d   %2s  %-6s",
		      n, fill_string((M.format == MODFORMAT_ULT ? s->dosname :
				      s->name), 22),
		      s->length, s->volume,
		      (s->looped ?
		       (s->looped == LOOP_BIDIR ? "BIDI" : "Yes ") : "    "),
		      s->finetune, (s->bits_16 ? "16" : " 8"),
		      s->length > 4 ? "mono" : "unused");
	}
	refresh();
	if(n != -1 && ++n > M.nr_samples)
	    n=-1;
    }
#ifdef FLUSH_INPUT
    read(STDIN_FILENO, buf, 80); /* Flush input (for slow CPU's) */
#endif
}

char *help_pages[6][9]= {
    {
	"  Welcome!!  ",
	"             ",
	" I hope you  ",
	" will enjoy  ",
	" using mod!  ",
	"             ",
	"F1 and h will",
	"let you flip ",
	" help pages. "
    },
    {
	"   Always:   ",
	"             ",
	" q  Quit     ",
	" h  Help     ",
	" l  Load mode",
	"    on/off   ",
	"+/- Volume   ",
	" m  Mute     ",
	"    on/off   "
    },
    {
	"In load-mode:",
	"             ",
	"n/p Next/prev",
	"    file     ",
	"N/P Next/prev",
	"    file page",
	" u  Parent   ",
	"    directory",
	"ENT Select   "
    },
    {
	" Otherwise:  ",
	"             ",
	"f/b FF/RW    ",
	" r  Restart  ",
	"             ",
	"n/p Next/prev",
        "    song     ",
	"             ",
	"ENT Play/Stop"
    },
    {
	" Otherwise:  ",
	"             ",
	" s  Scrolling",
	"    on/off   ",
	" v  Voice    ",
	"    detail   ",
	"0-9 Toggle   ",
	"    voices   ",
        "    on/off   "
    },
    {
	"  There are  ",
	"  more keys  ",
	"  available. ",
	"(up/down etc)",
	"             ",
        "Read The Fine",
	" Manual for  ",
	"    more     ",
	" information."
    }
};

void print_minihelp(void)
{
    static int cur_page=0;

    if(!opt.interactive)
	return;

    setcol(ATTR_REDBAR);
    move(5, 65);
    vline(ACS_VLINE, 9);
    move(5, 79);
    vline(ACS_VLINE, 9);

    move(4, 65);
    addch(ACS_ULCORNER);
    hline(ACS_HLINE, 13);
    move(4, 79);
    addch(ACS_URCORNER);

    move(14, 65);
    addch(ACS_LLCORNER);
    hline(ACS_HLINE, 13);
    move(14, 79);
    addch(ACS_LRCORNER);

    mvaddstr(5, 66,  help_pages[cur_page][0]);
    mvaddstr(6, 66,  help_pages[cur_page][1]);
    mvaddstr(7, 66,  help_pages[cur_page][2]);
    mvaddstr(8, 66,  help_pages[cur_page][3]);
    mvaddstr(9, 66,  help_pages[cur_page][4]);
    mvaddstr(10, 66, help_pages[cur_page][5]);
    mvaddstr(11, 66, help_pages[cur_page][6]);
    mvaddstr(12, 66, help_pages[cur_page][7]);
    mvaddstr(13, 66, help_pages[cur_page][8]);
    refresh();
    cur_page=(cur_page+1)%6;
}


void print_pos(int pos, int line)
{
    int i;

    if(!opt.interactive)
	return;

    setcol(ATTR_PANELTEXT); 
    if(pos != -1) {
	mvwprintw(stdscr, 1, 56, "%3d", pos);
	mvwprintw(stdscr, 2, 56, "%3d", M.patterntable[pos]);
	mvwprintw(stdscr, 3, 56, "%3d", line);
    }
    else {
	for(i=1; i <= 3; ++i)
	    mvwprintw(stdscr, i, 56, "   ");
    }
	refresh();
}

void print_speed(int spd, int tempo)
{
    if(!opt.interactive)
	return;

    setcol(ATTR_PANELTEXT);
    if(spd != -1) {
	mvwprintw(stdscr, 1, 71, "%3d", spd);
	mvwprintw(stdscr, 2, 71, "%3d", tempo);
    }
    else {
	mvwprintw(stdscr, 1, 71, "   ");
	mvwprintw(stdscr, 2, 71, "   ");
    }
    refresh();
}


void print_songinfo(int v, int samp, char *type, int sz, int songlen, int pat)
{
    int i;

    if(!opt.interactive)
	return;

    setcol(ATTR_PANELTEXT);

    if(v != -1) {
	mvwprintw(stdscr, 1, 43, "%3s", type);
	mvwprintw(stdscr, 2, 44, "%2d", v);
	mvwprintw(stdscr, 3, 44, "%2d", samp);
	mvwprintw(stdscr, 5, 41, "%4dk", sz/1024);

	mvwprintw(stdscr, 1, 60, "%3d", songlen-1);
	mvwprintw(stdscr, 2, 60, "%3d", pat-1);
	mvwprintw(stdscr, 3, 60, " 63");
    }
    else {
	for(i=1; i <= 3; ++i)
	    mvwprintw(stdscr, i, 43, "   ");
	mvwprintw(stdscr, 5, 41, "     ");
	for(i=1; i <= 3; ++i)
	    mvwprintw(stdscr, i, 60, "   ");
    }
    refresh();
}    


void print_time(int t)
{
    if(!opt.interactive)
	return;

    setcol(ATTR_PANELTEXT);
    if(t != -1) {
	mvwprintw(stdscr, 5, 57, "%3d", t/60);
	mvwprintw(stdscr, 5, 61, "%02d", t%60);
    }
    else {
	mvwprintw(stdscr, 5, 57, "   ");
	mvwprintw(stdscr, 5, 61, "  ");
    }
    refresh();
}


void clear_all_info(void)
{
    if(!opt.interactive)
	return;

    print_samples(-1);
    print_status("");
    print_songname(" ");
    print_filename(-1);

    print_pos(-1,0);
    print_speed(-1,0);
    print_songinfo(-1,0,0,0,0,0);
    print_time(-1);
}


void print_line(int songpos, int line, int voice)
{
    static char tmpbuf[256]={"                                        "
				 "                                        "};
    char *buf;
    int pos, pat, step, v, l, i;
    char midline[81];
    int mid_i=0; /* Remove warning */

    if(!opt.interactive)
	return;

    scrollok(swin, FALSE);
    wsetcol(swin, ATTR_LINE);
    pat=M.patterntable[songpos];
    
    l=line-swin_LINES/2;
    if(!(swin_LINES%2))
	l++;
    
    buf=&tmpbuf[80];
    for(i=0; i < swin_LINES; ++i, ++l) {
	wmove(swin, i, 0);
	if(l < 0 || l > 63) {
	    wclrtoeol(swin);
	}
	else {
	    sprintf(buf, "%02d  ", l);
	    pos=4;
	    for(v=voice; v < M.nr_voices && v < voice+nr_visible_voices; ++v) {
		step=print_event(&buf[pos], GET_EVENT_PTR(v, pat, l));
		pos+=step;
	    }
	    sprintf(&buf[pos], "|  %02d", l);
	    pos+=5;

	    if(l != line) {
		waddstr(swin, &buf[(pos-80)/2]);
		wclrtoeol(swin);
	    }
	    else {
		strcpy(midline, &buf[(pos-80)/2]);
		mid_i=i;
	    }
	}
    }
    wrefresh(swin);
    wsetcol(swin, ATTR_BOLDLINE);
    wmove(swin, mid_i, 0);
    waddstr(swin, midline);
    wclrtoeol(swin);
    wrefresh(swin);
}

extern char *notenames[NR_OCTAVES*12+1];
extern char *effectnames[NR_EFX];
extern char *shorteffectnames[NR_EFX];

int print_event(char *buf, struct event *n)
{
    unsigned char note=n->note;

    if(note == NOTE_OFF)
	note=12*8+BASE_NOTE; /* Last of the named notes */
    else {
	if(note && M.format != MODFORMAT_S3M)
	    note-=2*12;
    }

    if(nr_visible_voices == 17) { /* Only notes */
	sprintf(buf, "|%s", 
		(note ? notenames[note-BASE_NOTE] : "   "));
	return 4;
    }
    else if(nr_visible_voices == 9) { /* Notes + instruments */
	sprintf(buf, "|%s ", 
		(note ? notenames[note-BASE_NOTE] : "   "));
	
	if(n->sample)
	    sprintf(buf+5, "%2d", n->sample);
	else
	    strcpy(buf+5, "  ");
	return 7;
    }
    else { /* All of it (unless there are two effects) */
	sprintf(buf, "| %s ",
		(note ? notenames[note-BASE_NOTE] : "   "));
	
	if(n->sample)
	    sprintf(buf+6, "%2d ", n->sample);
	else
	    strcpy(buf+6, "   ");
	
	/* Check if there are one or two effects. If there are two we just
	 * display the effects without the arguments.
	 */
	if(M.format != MODFORMAT_S3M && ((!n->effect && n->arg) || n->effect)
	   && ((!n->effect2 && n->arg2) || n->effect2)) {

	    /* Two effects */
	    sprintf(buf+9, "%s+%s ", shorteffectnames[n->effect],
		    shorteffectnames[n->effect2]);
	}
	else {
	    /* Only one (or no) effect */
	    if((!n->effect && n->arg) || n->effect)
		sprintf(buf+9, "%s %02X ", effectnames[n->effect], n->arg);
	    else if(!M.format == MODFORMAT_S3M &&
		    ((!n->effect2 && n->arg2) || n->effect2))
		sprintf(buf+9, "%s %02X ", effectnames[n->effect2], n->arg2);
	    else
		strcpy(buf+9, "        ");
	}
	return 17;
    }
}

void setup_color_mode(void)
{
    start_color();
    init_pair(1, COLOR_YELLOW, COLOR_RED);
    init_pair(2, COLOR_BLUE, COLOR_BLACK);
    init_pair(3, COLOR_WHITE, COLOR_CYAN);
    init_pair(4, COLOR_CYAN, COLOR_BLACK);
    init_pair(5, COLOR_YELLOW, COLOR_BLUE);
    init_pair(6, COLOR_YELLOW, COLOR_MAGENTA);
    init_pair(7, COLOR_WHITE, COLOR_BLACK);
    init_pair(8, COLOR_WHITE, COLOR_WHITE);
    init_pair(9, COLOR_BLUE, COLOR_CYAN);
    init_pair(10, COLOR_WHITE, COLOR_BLUE);
    init_pair(11, COLOR_WHITE, COLOR_RED);
}
