#include "clib.h"
#include "i386.h"
#include "console.h"
/*
 * implementazione console virtuali.
 * la procedura di print riconosce molte delle sequenze escape utilizzate
 * dal driver ansi.sys del dos.
 * che sono :
 *
 * e[x;yH   posiziona cursore x,y
 * e[x;yf   idem
 * e[nA     cursore in alto di n char
 * e[nB     cursore in basso di n char
 * e[nC	    cursore avanti n char
 * e[nD     cursore indietro n char
 * e[s	    salva posizione cursore
 * e[u      ripristina posizione salvata
 * e[2J	    cancella schermo, e mette il cursore in (0,0)
 * e[K	    cancella riga
 * e[n;...;nm imposta colori di primo piano e sfondo, ed il blink:
 *          5:attiva blink 0: disattiva blink
 *   Colori in primo piano

 *     30    Nero
 *     31    Rosso
 *     32    Verde
 *     33    Giallo
 *     34    Blu
 *     35    Magenta
 *     36    Azzurro
 *     37    Bianco
 *
 *   Colori di sfondo = primo piano+10
 *
 * NB: c->width deve essere <= c->hwwidth !!
 */

char sc2ascii[128] =
{
/* 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F             */
   0,   27,  '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8,   9,       /* 0 */
   'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13,  0,   'a', 's',     /* 1 */
   'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 39,  '`', 0,   92,  'z', 'x', 'c', 'v',     /* 2 */
   'b', 'n', 'm', ',', '.', '/', 0,   '*', 0,   ' ', 0,   0,   0,   0,   0,   8,       /* 3 */
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   '-', 0,   0,   0,   '+', 0,       /* 4 */
   0,   0,   0,   127, 0,   0,   92,  0,   0,   0,   0,   0,   0,   0,   0,   0,       /* 5 */
   0,   0,   '/', 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   127,     /* 6 */
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0        /* 7 */
};

char ssc2ascii[128] =
{
/* 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F             */
   0,   27,  '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 8,    9,      /* 0 */
   'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}',  13, 0,   'A', 'S',     /* 1 */
   'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', 34,  '~', 0,   '|', 'Z', 'X', 'C', 'V',     /* 2 */
   'B', 'N', 'M', '<', '>', '?', 0,   '*', 0,   ' ', 0,   0,   0,   0,   0,   0,       /* 3 */
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   '-', 0,   0,   0,   '+', 0,       /* 4 */
   0,   0,   0,   127, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,       /* 5 */
   0,   0,   '/', 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   127,     /* 6 */
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0        /* 7 */
};



/* stampa una stringa "cosi` com'e`", NESSUN carattere viene interpretato */
static
int printraw(console_t *c, const char *str, int max_len)
{
	int l = 0;
        if (max_len < 0) max_len = strlen(str);
        /*
         * FIXME: questo loop si puo` ottimizzare un tot, evitando il check
         * per c->cY, ed anche la moltiplicazione
         */
        while (l < max_len && *str) {
                if (c->cX >= c->width) {
                	c->cY++;
                        c->cX = 0;
                }
                if (c->cY >= c->height) {
                        memmove(&c->video[0], &c->video[c->width*(c->cY - c->height+1)], (c->width-1)*c->height*2);
                        c->cY = c->height-1;
                }
        	c->video[c->cX+c->cY*c->width] = *str + c->color;
                c->cX++;
                str++;
                l++;
        }
        return l;
}

/* gestisce le sequenze escape con parametri */
static
const char* do_escape2(console_t *c, const char *s)
{
        const char *end = s;
	int i = 0;
        int j;
        int nums[5];

	do {
                nums[i] = strtoul(s, (char**)&end, 0);
                if (s != end) i++;
                s = end+1;
        } while ((*end == ';') && (i < 5));

        switch (*end) {
        	case 'H':
                case 'f': /* posiziona cursore */
                	if (i == 0) {
                        	c->cX = c->cY = 0;
                                return end+1;
                        } else if (i == 2) {
                        	if (nums[0] < c->width) c->cX = nums[0];
                                if (nums[1] < c->height) c->cY = nums[1];
                        	return end+1;
                        }
                        return end;

                case 'A': /* Y-- */
                        j = c->cY;
                        j-= nums[0];
                        if (j > 0 && j < c->height) c->cY = j;
                        return end+1;

                case 'B': /* Y++ */
                        j = c->cY;
                        j+= nums[0];
                        if (j > 0 && j < c->height) c->cY = j;
                        return end+1;

                case 'C': /* X++ */
                        j = c->cX;
                        j+= nums[0];
                        if (j > 0 && j < c->width) c->cX = j;
                        return end+1;

                case 'D': /* X-- */
                        j = c->cX;
                        j-= nums[0];
                        if (j > 0 && j < c->width) c->cX = j;
                        return end+1;

                case 'm':
                	{
                        	char fgc[] = {0,4,2,14,1,5,9,15};
                                char bgc[] = {0,4,2,6,1,5,3,7};
                                for (j=0; j < i; j++) {
                                	switch (nums[j]) {
                                        	case 0: /* disattiva attributi */
                                              		c->color &= ~0x8000;
                                              		break;
                                                case 5: /* attiva blink */
                                                        c->color |= 0x8000;
                                                        break;
                                                case 30 ... 37:
                                                        /* imposta colore primo piano */
                                                        c->color &= 0xF000;
                                                	c->color |= fgc[nums[j] - 30] << 8;
                                                        break;
                                                case 40 ... 47:
                                                        /* imposta colore sfondo */
                                                        c->color &= 0x8F00;
                                                	c->color |= bgc[nums[j] - 40] << 12;
                                                        break;
                                                default: break;
                                        }
                                }
                                return end+1;
                        }
                case 'p':
                	/* questa e` la definizione dei tasti */
                        return end+1;
                default: return end;
       }


}

/* gestisce caratteri escape semplici */
static
const char* do_escape(console_t *c, const char *s)
{

	switch (*s) {
        	case 'K':
                	/* cancella riga dalla posizione del cursore compresa */

                        memset(&c->video[c->cY*c->width+c->cX], 0, (c->width-c->cX)*2);
                        return s+1;

                case 'u':
                	/* ripristina posizione salvata */
                	c->cX = c->savX;
                        c->cY = c->savY;
                        return s+1;
                case 's':
                	/* salva posizione cursore */
                	c->savX = c->cX;
                	c->savY = c->cY;
                        return s+1;


                case '2': // '2J'
                	/* cancella schermo */
                        if (s[1] == 'J') {
                        	/* clrscr */
                                memset(&c->video[0], 0, c->width*c->height*2);
                                c->cX = c->cY = 0;
                        	return s+2;
                        }
                        return s;
                default: return do_escape2(c, s);
        }


}

/* procedura di print con riconoscimento dei caratteri escape
   NB: tutti i caratteri ascii < 32 se non riconosciuti validi
       sono ignorati
*/
static
int print(console_t *c, const char *str)
{
        const char* last = str;
        int r = 0;
	while (*str) {
        	if (*str < 32) {
                	r += printraw(c, last, str - last);
                        switch (*str) {
                        	case '\a': /* bell 07 */
                                	break;
                                case '\b': /* backspace 08 */
                                	c->cX--;
                                	break;
				case '\f': /* from feed 12 */
                                	c->cX = 0;
                                	break;
                        	case '\n': /* line feed 10 */
                                	c->cX = 0;
                                        c->cY++;
                                	break;
                                case '\t': /* tab 09 */
                                	c->cX += c->fl.htabsize;
                                	break;
                                case '\e': /* escape 27 */
                                	if (str[1] == '[' && str[2] != '\0')
                                		str = do_escape(c, &str[2]) -1;
                                	break;
                                case '\v': /* vertical tab 11 */
                                	c->cY+= c->fl.vtabsize;
                                	break;
                                case '\r': /* 13 */
                                	c->cY++;
                                        break;
                                default: break;
                        }
                        last = ++str;

                } else str++;
        }
        return r+printraw(c, last, -1);
}

/*
 * procedura di print, ritorna il numero di caratteri stampati
 *
 */

int con_write(console_t* c, const char *str)
{
	int n;
try_again:
;
	asm_cli;

	if (c->fl.outrdy) {
		c->fl.outrdy = 0;
                asm_sti;
                if (c->fl.rawout) n = printraw(c,str,-1);
                else n = print(c, str);
                if (c->fl.movecursor) {
                	unsigned i = c->hwwidth*c->cY+c->cX;
                	outportb(0x3D4, 0xF);
                	outportb(0x3D5, (byte)i);
                        outportb(0x3D4, 0xE);
                        outportb(0x3D5, i shr 8);
                }
                if ((c->video[c->cX+c->cY*c->width] shr 8) == 0) {
                        c->video[c->cX+c->cY*c->width] |= c->color;
                }
                c->fl.outrdy = 1;
                return n;
        } else {
        	/* wait */
                goto try_again;
        }

}

/* inserisce un carattere nel buffer, se il buffer e` pieno, viene ignorato*/
static
char_t insert_char_t(console_t*c, char_t curc, int sc)
{
	char_t* cbuf;
        int	j;
        j = (c->kbstart+1) & 0xF;
        if (j == c->kbend) return curc; // full
        c->kbstart = j;
        cbuf = &c->keybuf[j];


        curc.scanc = sc;
        if (c->shift.shift || c->shift.capslock)
                curc.ascii = ssc2ascii[sc];
        else    curc.ascii = sc2ascii[sc];

        if (c->shift.shift) curc.shift = 1;
        if (c->shift.alt) curc.alt = 1;
        if (c->shift.ctrl) curc.ctrl = 1;
        if (c->shift.capslock) curc.capslock = 1;
        if (c->shift.numlock) curc.numlock = 1;
        if (c->prev.scanc = 0xE0) curc.extended = 1;
        return *cbuf = curc;
}

char_t con_get_key(console_t* c)
{
	char_t r;
        *(int*)&r = 0;
        asm_cli;
	if (c->kbstart != c->kbend) {
                c->kbend = (c->kbend +1) & 0x0F;
                r = c->keybuf[c->kbend];
        }
        asm_sti;
        return r;
}

void con_convert_key(console_t* c, int sc)
{
	char_t curc;
        char_t prevshift = c->shift;

        *(int*)&curc = 0;

        if (c->fl.rawin) {
                c->prev = insert_char_t(c, curc, sc);
                return;
        }

	if (sc >= 0x80) {
        	/* tasto rilasciato */
                if (sc == 0xE0) {
                	/* codice esteso */
                	c->prev.scanc = 0xE0;
                        return;
                }

        	switch (sc & 0x7F) {
                	case 0x1D: /* ctrl */
                        	c->shift.ctrl = 0;
                                break;
                	case 0x2A: /* Lshift */
                                c->shift.shift = 0;
                                break;
                        case 0x36: /* Rshift */
                                c->shift.shift = 0;
                                break;
                        case 0x38: /* alt */
                                c->shift.alt = 0;
                                break;
                        case 0x3A: /* caps */
                                c->shift.capslock = 0;
                                break;
                        case 0x45: /* num */
                                c->shift.numlock = 0;
                                break;
                        case 0x46: /* scroll */
                                c->shift.scrollock = 0;
                                break;
                        case 0x52: /* ins */
                        	c->shift.insert = 0;
                                break;

                }


                if (c->fl.want_released) {
	                curc.released = 1;
	                c->prev = insert_char_t(c, curc, sc);
                } else {
                        curc.scanc = sc;
                	c->prev = curc;
                }

        } else {
        	switch (sc) {
                	case 0x1D: /* ctrl */
                        	c->shift.ctrl = 1;
                                break;
                	case 0x2A: /* Lshift */
                                c->shift.shift = 1;
                                break;
                        case 0x36: /* Rshift */
                                c->shift.shift = 1;
                                break;
                        case 0x38: /* alt */
                                c->shift.alt = 1;
                                break;
                        case 0x3A: /* caps */
                                c->shift.capslock = 1;
                                break;
                        case 0x45: /* num */
                                c->shift.numlock = 1;
                                break;
                        case 0x46: /* scroll */
                                c->shift.scrollock = 1;
                                break;
                        case 0x52: /* ins */
                        	c->shift.insert = 1;
                                break;
                }

                c->prev = insert_char_t(c, curc, sc);
        }

        if (*(int*)&c->shift != *(int*)&prevshift) {
        /* mettere a posto i leds */
        }
}