/*******************************************************************************/
/* COS2000 - Compatible Operating System - LGPL v3 - Hordé Nicolas             */
/*                                                                             */
#include "asm.h"
#include "math.h"
#include "video.h"
#include "stdarg.h"
#include "string.h"
#include "VGA/8x8fnt.c"
#include "VGA/8x16fnt.c"

/******************************************************************************/
/* VARIABLES */

static drivers registred[MAXDRIVERS];

static font fonts[MAXFONTS];

static videoinfos *vinfo;

static  width, height;

static font *currentfont;

static console vc[8] = {
	{0x07, 0, 0, 0, 0, 0, 0, 0, true},
	{0x07, 0, 0, 0, 0, 0, 0, 0, true},
	{0x07, 0, 0, 0, 0, 0, 0, 0, true},
	{0x07, 0, 0, 0, 0, 0, 0, 0, true},
	{0x07, 0, 0, 0, 0, 0, 0, 0, true},
	{0x07, 0, 0, 0, 0, 0, 0, 0, true},
	{0x07, 0, 0, 0, 0, 0, 0, 0, true},
	{0x07, 0, 0, 0, 0, 0, 0, 0, true}
};

static u8 usedvc = 0;

/******************************************************************************/
/* FONCTIONS CONSOLE */

/*******************************************************************************/
/* Fixe l'attribut courant */

void setattrib(u8 att)
{
	static const u8 ansitovga[] = {
		0, 4, 2, 6, 1, 5, 3, 7
	};
	u8      tempattr;

	tempattr = vc[usedvc].attrib;
	if (att == 0)
		tempattr = 0x07;	/* Couleur Grise sur fond noir */
	else if (att == 5)
		tempattr |= 0x80;
	else if (att == 7)
		tempattr =
			((tempattr & 0x0F) << 4) +
			((tempattr & 0xF0) >> 4);
	else if (att == 8)
		tempattr = 0;
	else if (att == 1)
		tempattr |= 0x08;	/* Forte intensité */
	else if (att >= 30 && att <= 37)
	{
		att = ansitovga[att - 30];
		tempattr = (tempattr & ~0x07) | att;	/* couleur de premier plan */
	}
	else if (att >= 40 && att <= 47)
	{
		att = ansitovga[att - 40] << 4;
		tempattr = (tempattr & ~0x70) | att;	/* couleur de fond */
	}
	vc[usedvc].attrib = tempattr;
}

/*******************************************************************************/
/* gere l'ansi */

bool makeansi(u8 c)
{
/* state machine to handle the escape sequences */
	switch (vc[usedvc].ansi)
	{
		case 0:
/* ESC -- next state */
			if (c == 0x1B)
			{
				vc[usedvc].ansi++;
				return 1;	/* "I handled it" */
			}
			break;
/* ESC */
		case 1:
			if (c == '[')
			{
				vc[usedvc].ansi++;
				vc[usedvc].param1 = 0;
				return 1;
			}
			break;
/* ESC[ */
		case 2:
			if (isdigit(c))
			{
				vc[usedvc].param1 =
					vc[usedvc].param1 * 10 + c - '0';
				return 1;
			}
			else if (c == ';')
			{
				vc[usedvc].ansi++;
				vc[usedvc].param2 = 0;
				return 1;
			}
/* ESC[2J -- efface l'ecran */
			else if (c == 'J')
			{
				if (vc[usedvc].param1 == 2)
				{
					fill(vc[usedvc].attrib);
					vc[usedvc].cursX = 0;
					vc[usedvc].cursY = 0;
					cursor_set(0, 0);
					vc[usedvc].ansi = 0;
					return 1;
				}
			}
/* ESC[num1m -- met l'attribut num1 */
			else if (c == 'm')
			{
				setattrib(vc[usedvc].param1);
				vc[usedvc].ansi = 0;
				return 1;
			}
/* ESC[num1A -- bouge le curseur de num1 vers le haut */
			else if (c == 'A')
			{
				vc[usedvc].cursY -= vc[usedvc].param1;
				if (vc[usedvc].cursY < 0)
					vc[usedvc].cursY = 0;
				vc[usedvc].ansi = 0;
				cursor_set(vc[usedvc].cursX,
					   vc[usedvc].cursY);
				return 1;
			}
/* ESC[num1B -- bouge le curseur de num1 vers le bas */
			else if (c == 'B')
			{
				vc[usedvc].cursY += vc[usedvc].param1;
				if (vc[usedvc].cursY >= getheight() - 1)
					vc[usedvc].cursY = getheight();
				vc[usedvc].ansi = 0;
				cursor_set(vc[usedvc].cursX,
					   vc[usedvc].cursY);
				return 1;
			}
/* ESC[num1D -- bouge le curseur de num1 vers la gauche */
			else if (c == 'D')
			{
				vc[usedvc].cursX -= vc[usedvc].param1;
				if (vc[usedvc].cursX < 0)
					vc[usedvc].cursX = 0;
				vc[usedvc].ansi = 0;
				cursor_set(vc[usedvc].cursX,
					   vc[usedvc].cursY);
				return 1;
			}
/* ESC[num1C -- bouge le curseur de num1 vers la droite */
			else if (c == 'C')
			{
				vc[usedvc].cursX += vc[usedvc].param1;
				if (vc[usedvc].cursX >= getwidth() - 1)
					vc[usedvc].cursX = getwidth();
				vc[usedvc].ansi = 0;
				cursor_set(vc[usedvc].cursX,
					   vc[usedvc].cursY);
				return 1;
			}
			break;
/* ESC[num1; */
		case 3:
			if (isdigit(c))
			{
				vc[usedvc].param2 =
					vc[usedvc].param2 * 10 + c - '0';
				return 1;
			}
			else if (c == ';')
			{
				vc[usedvc].ansi++;
				vc[usedvc].param3 = 0;
				return 1;
			}
/* ESC[num1;num2H ou ESC[num1;num2f-- bouge le curseur en num1,num2 */
			else if ((c == 'H') || (c == 'f'))
			{
				/* Remet la position du curseur matériel  a num1,num2 */
				cursor_set(vc[usedvc].param2,
					   vc[usedvc].param1);
				/* Remet la position du curseur logiciel  a num1,num2 */
				vc[usedvc].cursX = vc[usedvc].param2;
				vc[usedvc].cursY = vc[usedvc].param1;
				vc[usedvc].ansi = 0;
				return 1;
			}
/* ESC[num1;num2m -- met les attributs num1,num2 */
			else if (c == 'm')
			{
				setattrib(vc[usedvc].param1);
				setattrib(vc[usedvc].param2);
				vc[usedvc].ansi = 0;
				return 1;
			}
			break;
/* ESC[num1;num2;num3 */
		case 4:
			if (isdigit(c))
			{
				vc[usedvc].param3 =
					vc[usedvc].param3 * 10 + c - '0';
				return 1;
			}
/* ESC[num1;num2;num3m -- met les attributs num1,num2,num3 */
			else if (c == 'm')
			{
				setattrib(vc[usedvc].param1);
				setattrib(vc[usedvc].param2);
				setattrib(vc[usedvc].param3);
				vc[usedvc].ansi = 0;
				return 1;
			}
			break;
/* Mauvais etat >> reset */
		default:
			vc[usedvc].ansi = 0;
			break;
	}
	vc[usedvc].ansi = 0;
	return 0;		/* Ansi fini ;) */
}

/*******************************************************************************/
/* affiche un caractère a l'écran */

void putchar(u8 thechar)
{
	page_show(usedvc);
	page_set(usedvc);
	if (makeansi(thechar))
		return;
	switch (thechar)
	{
		case 0x11:
			if (vc[usedvc].cursY > 0)
				vc[usedvc].cursY--;
			break;
		case 0x12:
			if (vc[usedvc].cursY < getheight() - 1)
				vc[usedvc].cursY++;
			break;
		case 0x13:
			if (vc[usedvc].cursX > 0)
				vc[usedvc].cursX--;
			break;
		case 0x14:
			if (vc[usedvc].cursX < getwidth() - 1)
				vc[usedvc].cursX++;
			break;
		case 0x2:
			vc[usedvc].cursX = 0;
			vc[usedvc].cursY = 0;
			break;
		case 0x3:
			vc[usedvc].cursX = 0;
			vc[usedvc].cursY = getheight() - 1;
			break;
		case 0x19:
			vc[usedvc].cursX = getwidth() - 1;
			break;
		case '\b':
			if (vc[usedvc].cursX == 0)
			{
				if (vc[usedvc].cursY > 0)
				{
					vc[usedvc].cursX = getwidth() - 1;
					vc[usedvc].cursY--;
				}
			}
			else
			{
				vc[usedvc].cursX--;
			}
			showchar(vc[usedvc].cursX, vc[usedvc].cursY, ' ',
				 vc[usedvc].attrib);
			break;
		case '\t':
			vc[usedvc].cursX =
				(vc[usedvc].cursX + 8) & ~(8 - 1);
			break;
		case '\n':
			vc[usedvc].cursX = 0;
			break;
		case '\r':
			vc[usedvc].cursX = 0;
			vc[usedvc].cursY++;
			break;
		default:
			if (thechar >= ' ')
			{
				showchar(vc[usedvc].cursX,
					 vc[usedvc].cursY, thechar,
					 vc[usedvc].attrib);
				vc[usedvc].cursX++;
			}
			break;
	}
	if (vc[usedvc].cursX >= getwidth())
	{
		vc[usedvc].cursX = 0;
		vc[usedvc].cursY++;
	}
	if (vc[usedvc].cursY >= getheight())
	{
		scroll(1, vc[usedvc].attrib);
		vc[usedvc].cursY = getheight() - 1;
	}
	cursor_set(vc[usedvc].cursX, vc[usedvc].cursY);
}

/*******************************************************************************/
/* Change la console en cours d'utilisation */

void changevc(u8 avc)
{
	usedvc = avc;
	page_show(usedvc);
	page_set(usedvc);
	cursor_set(vc[usedvc].cursX, vc[usedvc].cursY);
}

/*******************************************************************************/
/* Renvoie la taille horizontale */
u16 getwidth(void)
{
	return width;
}

/*******************************************************************************/
/* Renvoie la taille verticale */
u16 getheight(void)
{
	return height;
}

/*******************************************************************************/
/* Efface la console en cours d'utilisation */
void clearscreen(void)
{
	fill(vc[usedvc].attrib);
	vc[usedvc].cursX = 0;
	vc[usedvc].cursY = 0;
	cursor_set(0, 0);
}

/******************************************************************************/
/* Active le scrolling */

void scroll_enable(void)
{
	vc[usedvc].scroll = true;
}

/******************************************************************************/
/* Désactive le scrolling */

void scroll_disable(void)
{
	vc[usedvc].scroll = false;
}

/******************************************************************************/
/* FONCTIONS VIDEO BASIQUES */

/*******************************************************************************/
/* initialise le tableau des pilotes vidéo */
void initdriver()
{
	for (u32 i = 0; i < MAXDRIVERS; i++)
		registred[i].nom = NULL;
}

/*******************************************************************************/
/* Enregistre un pilote dans le tableau des pilotes vidéo */
void registerdriver(videofonction * pointer)
{
	u32     i;
	for (i = 0; i < MAXDRIVERS; i++)
		if (registred[i].pointer == pointer)
			return;
	i = 0;
	while (registred[i].nom != NULL && i < MAXDRIVERS)
		i++;
	if (pointer->detect_hardware() != NULL)
	{
		registred[i].pointer = pointer;
		registred[i].nom = pointer->getvideo_drivername();
	}
}
/*******************************************************************************/
/* Choisi le meilleur driver en terme d'affichage */
void apply_bestdriver(void)
{
	u32     i = 0, j;
	u8      bestdepth = 0x0;
	u32     bestresol = 0x0;
	u8      bestmode = 0x0;
	u8     *bestdriver = NULL;
	capabilities *cap;
	while (registred[i].nom != NULL && i < MAXDRIVERS)
	{
		cap = registred[i].pointer->getvideo_capabilities();
		j = 0;
		while (cap[j].modenumber != 0xFF)
		{
			if (cap[j].depth > bestdepth
			    && (cap[j].width * cap[j].height) >= bestresol)
			{
				bestdepth = cap[j].depth;
				bestresol = cap[j].width * cap[j].height;
				bestmode = cap[j].modenumber;
				bestdriver =
					registred[i].
					pointer->getvideo_drivername();
			}
			j++;
		}
		i++;
	}
	if (bestdriver != NULL)
		apply_driver(bestdriver);
	setvideo_mode(bestmode);
}

/*******************************************************************************/
/* Choisi le meilleur driver spécifié par le nom */
void apply_driver(u8 * name)
{
	u32     i = 0;
	while (registred[i].nom != NULL && i < MAXDRIVERS)
	{
		if (strcmp(name, registred[i].nom) == 0)
		{
			remap_memory = registred[i].pointer->remap_memory;
			detect_hardware =
				registred[i].pointer->detect_hardware;
			setvideo_mode =
				registred[i].pointer->setvideo_mode;
			getvideo_drivername =
				registred[i].pointer->getvideo_drivername;
			getvideo_capabilities =
				registred[i].
				pointer->getvideo_capabilities;
			getvideo_info =
				registred[i].pointer->getvideo_info;
			mem_to_video = registred[i].pointer->mem_to_video;
			video_to_mem = registred[i].pointer->video_to_mem;
			video_to_video =
				registred[i].pointer->video_to_video;
			wait_vretrace =
				registred[i].pointer->wait_vretrace;
			wait_hretrace =
				registred[i].pointer->wait_hretrace;
			page_set = registred[i].pointer->page_set;
			page_show = registred[i].pointer->page_show;
			page_split = registred[i].pointer->page_split;
			cursor_enable =
				registred[i].pointer->cursor_enable;
			cursor_disable =
				registred[i].pointer->cursor_disable;
			cursor_set = registred[i].pointer->cursor_set;
			font_load = registred[i].pointer->font_load;
			font1_set = registred[i].pointer->font1_set;
			font2_set = registred[i].pointer->font2_set;
			blink_enable = registred[i].pointer->blink_enable;
			blink_disable =
				registred[i].pointer->blink_disable;
			changemode(0x0);
			return;
		}
		i++;
	}
}
/*******************************************************************************/
/* Applique le driver suivant */

void apply_nextdriver(void)
{
	u32     i = 0;
	while (registred[i].nom != NULL && i < MAXDRIVERS)
		if (strcmp(getvideo_drivername(), registred[i].nom) == 0)
		{
			i++;
			if (registred[i].nom != NULL)
				i = 0;
			apply_driver(registred[i].nom);
			return;
		}
	i++;
}

/*******************************************************************************/
/* Applique le mode suivant (le driver suivant si dernier mode) */

void apply_nextvideomode(void)
{
	capabilities *cap = getvideo_capabilities();
	videoinfos *info = getvideo_info();
	u32     mode = info->currentmode;
	u8      index = 0;
	while (cap[index].modenumber != 0xFF)
	{
		if (cap[index].modenumber == mode)
		{
			index++;
			if (cap[index].modenumber == 0xFF)
				apply_nextdriver();
			else
				changemode(cap[index].modenumber);
			return;
		}
		index++;
	}
}

/*******************************************************************************/
/* Change de mode video */

void changemode(u8 mode)
{
	setvideo_mode(mode);
	vinfo = getvideo_info();
	if (!vinfo->isgraphic)
	{
		width = vinfo->currentwidth;
		height = vinfo->currentheight;
	}
	else
	{
		width = (vinfo->currentwidth >> 3);
		height = (vinfo->currentheight >> 3);
	}
	for (u32 i = 0; i < MAXFONTS; i++)
		fonts[i].nom[0] = NULL;
	loadfont("BIOS1", font8x8, 8, 8);
	loadfont("BIOS0", font8x16, 8, 16);
	setfont("BIOS1");
	clearscreen();
}

/******************************************************************************/
/* Rempli l'écran avec un attribut donné et des espaces vides */

static u8 space = ' ';

void fill(u8 attrib)
{
	if (!vinfo->isgraphic)
	{
		mem_to_video(space, 0, vinfo->pagesize >> 1, false);
		mem_to_video(attrib, 1, vinfo->pagesize >> 1, false);
	}
	else
	{
		mem_to_video(0x0, 0, vinfo->pagesize >> 2, false);
	}
}

/******************************************************************************/
/* Défile l'écran de N ligne si le scrolling est activé */

void scroll(u8 lines, u8 attrib)
{
	if (vc[usedvc].scroll)
	{
		if (!vinfo->isgraphic)
		{
			u32     gain = vinfo->currentpitch * lines;
			video_to_video(gain, 0, vinfo->pagesize - gain);
			mem_to_video(space, vinfo->pagesize - gain - 2,
				     gain, false);
			mem_to_video(attrib, vinfo->pagesize - gain - 1,
				     gain, false);
		}
		else
		{
			u32     gain = vinfo->currentpitch * (lines << 3);
			video_to_video(gain, 0, vinfo->pagesize - gain);
			mem_to_video(0x0, vinfo->pagesize - gain - 2, gain,
				     false);
		}
	}
	else
	{
		clearscreen();
	}
}

/******************************************************************************/
/* Retourne une couleur RGB 32 bits depuis une couleur EGA/VGA */

static  convertega[] =
	{ 0, 1, 2, 3, 4, 5, 20, 7, 56, 57, 58, 59, 60, 61, 62, 63 };

u8 egatovga(u8 ega)
{
	return convertega[ega & 0xF];
}

/******************************************************************************/
/* Retourne une couleur RGB 32 bits depuis une couleur EGA/VGA */

static  convertrgb[] =
	{ 0x000000, 0x0000AA, 0x00AA00, 0x00AAAA, 0xAA0000, 0xAA00AA,
	0xAA5500, 0xAAAAAA, 0x555555, 0x5555FF, 0x55FF55, 0x55FFFF,
		0xFF5555,
	0xFF55FF, 0xFFFF55, 0xFFFFFF
};

u32 egatorgb(u8 vga)
{
	return convertrgb[vga & 0xF];
}

/******************************************************************************/
/* Retourne le caractère du mode texte aux coordonnées spécifiées */

u8 getchar(u16 coordx, u16 coordy)
{
	u8      thechar = 0;
	if (!vinfo->isgraphic)
	{
		u32     addr =
			(coordx << 1) + vinfo->currentpitch * coordy;
		video_to_mem(addr, &thechar, 1);
	}
	return thechar;
}

/******************************************************************************/
/* Retourne l'attribut du mode texte aux coordonnées spécifiées */

u8 getattrib(u16 coordx, u16 coordy)
{
	u8      attrib = 0;
	if (!vinfo->isgraphic)
	{
		u32     addr =
			(coordx << 1) + vinfo->currentpitch * coordy;
		video_to_mem(addr + 1, &attrib, 1);
	}
	return attrib;
}

/******************************************************************************/
/* Chargement d'une police de caractère  */

void loadfont(u8 * name, font * pointer, u8 width, u8 height)
{
	u32     i;
	for (i = 0; i < MAXFONTS; i++)
		if (fonts[i].nom[0] != NULL && fonts[i].pointer == pointer)
			return;
	i = 0;
	while (fonts[i].nom[0] != NULL && i < MAXFONTS)
		i++;
	fonts[i].pointer = pointer;
	strcpy(name, fonts[i].nom);
	fonts[i].width = width;
	fonts[i].height = height;
	if (fonts[i].nom[0] == 'B' && fonts[i].nom[1] == 'I'
	    && fonts[i].nom[2] == 'O' && fonts[i].nom[3] == 'S')
	{
		u8      number = (fonts[i].nom[4] - '0');
		font_load(pointer, height, number);
	}
}

/******************************************************************************/
/* Changement de la police */

void setfont(u8 * fontname)
{
	u32     i = 0;
	while (fonts[i].nom != NULL && i < MAXFONTS)
	{
		if (strcmp(fontname, fonts[i].nom) == 0)
		{
			currentfont = &fonts[i];
			return;
		}
		i++;
	}
}

/******************************************************************************/
/* FONCTIONS VIDEO GRAPHIQUES */

/******************************************************************************/
/* Affiche un caractère */

void showchar(u16 coordx, u16 coordy, u8 thechar, u8 attrib)
{
	u8      x, y, pattern, set;
	u32     color;
	if (!vinfo->isgraphic)
	{
		u32     addr =
			(coordx << 1) + vinfo->currentpitch * coordy;
		mem_to_video(thechar, addr, 1, false);
		mem_to_video(attrib, addr + 1, 1, false);
	}
	else
	{
		for (y = 0; y < currentfont->height; y++)
		{
			pattern =
				currentfont->pointer[currentfont->height *
						     thechar + y];
			for (x = 0; x < currentfont->width; x++)
			{
				rol(pattern);
				set = pattern & 0x1;
				if (set == 0)
					if (vinfo->currentdepth == 32)
						color = egatorgb((attrib &
								  0xF0) >>
								 4);
					else
						color = egatovga((attrib &
								  0xF0) >>
								 4);
				else if (vinfo->currentdepth == 32)
					color = egatorgb(attrib & 0x0F);
				else
					color = egatovga(attrib & 0x0F);



				writepxl(currentfont->width * coordx + x +
					 1,
					 currentfont->height * coordy + y,
					 color);
			}
		}
	}
}

/******************************************************************************/
/* Affiche une ligne horizontale entre les points spécifiés */

void hline(s16 x1, s16 x2, s16 y, u32 color)
{
	if (vinfo->isgraphic)
	{
		if (x1 < 0)
		{
			if (x2 < 0)
				return;
			x1 = 0;
		}
		else if (x1 > vinfo->currentwidth)
			x1 = vinfo->currentwidth - 1;
		if (x2 < 0)
			x2 = 0;
		else if (x2 > vinfo->currentwidth)
		{
			if (x1 > vinfo->currentwidth)
				return;
			x2 = vinfo->currentwidth - 1;
		}
		if (x1 > vinfo->currentwidth)
			x1 = vinfo->currentwidth - 1;
		if (x2 > x1)
			mem_to_video(color,
				     (vinfo->currentdepth >> 3) * x1 +
				     vinfo->currentpitch * y, x2 - x1,
				     false);
		else
			mem_to_video(color,
				     (vinfo->currentdepth >> 3) * x2 +
				     vinfo->currentpitch * y, x1 - x2,
				     false);
	}
}


/******************************************************************************/
/* Affiche un pixel à l'écran */

void v_writepxl(vertex2d * A, u32 color)
{
	writepxl(A->x, A->y, color);
}

void writepxl(s16 x, s16 y, u32 color)
{
	if (vinfo->isgraphic)
	{
		if (x > 0 && y > 0 && x < vinfo->currentwidth
		    && y < vinfo->currentheight)
		{
			u32     addr =
				(vinfo->currentdepth >> 3) * x +
				vinfo->currentpitch * y;
			mem_to_video(color, addr, 1, false);
		}
	}
}

/******************************************************************************/
/* Affiche une ligne entre les points spécifiés */

void v_line(vertex2d * A, vertex2d * B, u32 color)
{
	line(A->x, A->y, B->x, B->y, color);
}

void line(s16 x1, s16 y1, s16 x2, s16 y2, u32 color)
{
	s16     dx, dy, sdx, sdy;
	float   a, b;
	s16     i, dxabs, dyabs, x, y, px, py;

	dx = x2 - x1;		/*  distance horizontale de la line */
	dy = y2 - y1;		/* distance verticale de la line */

	if (x1 < 0)
	{
		a = 1.0f * dy / dx;
		b = y1 - a * x1;
		x1 = 0;
		y1 = b;
		if (x2 < 0)
			return;
	}
	else if (x2 < 0)
	{
		a = 1.0f * dy / dx;
		b = y2 - a * x2;
		x2 = 0;
		y2 = b;
	}
	if (y1 < 0)
	{
		a = 1.0f * dy / dx;
		b = y1 - a * x1;
		y1 = 0;
		x1 = -b / a;
		if (y2 < 0)
			return;
	}
	else if (y2 < 0)
	{
		a = 1.0f * dy / dx;
		b = y2 - a * x2;
		y2 = 0;
		x2 = -b / a;
	}
	if (x1 > vinfo->currentwidth)
	{
		a = 1.0f * dy / dx;
		b = y1 - a * x1;
		x1 = vinfo->currentwidth - 1;
		y1 = a * x1 + b;
		if (x2 > vinfo->currentwidth)
			return;
	}
	else if (x2 > vinfo->currentwidth)
	{
		a = 1.0f * dy / dx;
		b = y2 - a * x2;
		x2 = vinfo->currentwidth - 1;
		y2 = a * x2 + b;
	}
	if (y1 > vinfo->currentheight)
	{
		a = 1.0f * dy / dx;
		b = y1 - a * x1;
		y1 = vinfo->currentheight - 1;
		x1 = (y1 - b) / a;
		if (y2 > vinfo->currentheight)
			return;
	}
	else if (y2 > vinfo->currentheight)
	{
		a = 1.0f * dy / dx;
		b = y2 - a * x2;
		y2 = vinfo->currentheight - 1;
		x2 = (y2 - b) / a;
	}
	dxabs = abs(dx);
	dyabs = abs(dy);
	sdx = sgn(dx);
	sdy = sgn(dy);
	x = dyabs >> 1;
	y = dxabs >> 1;
	px = x1;
	py = y1;

	writepxl(px, py, color);

	if (dxabs >= dyabs)
	{			/* la ligne est plus horizontale que verticale */
		for (i = 0; i < dxabs; i++)
		{
			y += dyabs;
			if (y >= dxabs)
			{
				y -= dxabs;
				py += sdy;
			}
			px += sdx;
			writepxl(px, py, color);
		}
	}
	else
	{			/* la ligne est plus verticale que horizontale */

		for (i = 0; i < dyabs; i++)
		{
			x += dxabs;
			if (x >= dyabs)
			{
				x -= dyabs;
				px += sdx;
			}
			py += sdy;
			writepxl(px, py, color);
		}
	}
}

/******************************************************************************/
/* Affiche un triangle rempli entre les points spécifiés */

void trianglefilled(vertex2d * AA, vertex2d * BB, vertex2d * CC, u32 color)
{
	vertex2d *A, *B, *C, *TEMP;
	u32     a, b, y, last;
	int     dx1, dx2, dx3, dy1, dy2, dy3, sa, sb;
	A = AA;
	B = BB;
	C = CC;
	while (A->y > B->y || B->y > C->y || A->y == C->y)
	{
		if (A->y > B->y)
			swapvertex(A, B);
		if (B->y > C->y)
			swapvertex(B, C);
		if (A->y > C->y)
			swapvertex(A, C);
	}
	if (A->y == C->y)
	{			//meme ligne
		a = b = A->x;
		if (B->x < a)
			a = B->x;
		else if (B->x > b)
			b = B->x;
		if (C->x < a)
			a = C->x;
		else if (C->x > b)
			b = C->x;
		hline(a, b, A->y, color);
		return;
	}
	dx1 = B->x - A->x;
	dy1 = B->y - A->y;
	dx2 = C->x - A->x;
	dy2 = C->y - A->y;
	dx3 = C->x - B->x;
	dy3 = C->y - B->y;
	sa = 0;
	sb = 0;

	if (B->y == C->y)
		last = B->y;
	else
		last = B->y - 1;

	for (y = A->y; y <= last; y++)
	{
		a = A->x + sa / dy1;
		b = A->x + sb / dy2;
		sa += dx1;
		sb += dx2;
		hline(a, b, y, color);
	}

	sa = dx3 * (y - B->y);
	sb = dx2 * (y - A->y);
	for (; y <= C->y; y++)
	{
		a = B->x + sa / dy3;
		b = A->x + sb / dy2;
		sa += dx3;
		sb += dx2;
		hline(a, b, y, color);
	}
}

void swapvertex(vertex2d * A, vertex2d * B)
{
	vertex2d temp = *A;
	*A = *B;
	*B = temp;
}

/******************************************************************************/
/* Affiche un triangle  entre les points spécifiés */

void triangle(vertex2d * AA, vertex2d * BB, vertex2d * CC, u32 color)
{
	v_line(AA, BB, color);
	v_line(BB, CC, color);
	v_line(CC, AA, color);
}


/******************************************************************************/
/* FONCTIONS VIDEO TEXTE AFFICHAGE */

/*******************************************************************************/
/* affiche une chaine de caractère a l'écran */
/* SYSCALL 
{
"ID":2,
"LIBRARY":"libvideo",
"NAME":"print",
"INTERNALNAME":"print",
"DESCRIPTION":"Show a string on the screen",
"ARGS": [
{"TYPE":"u8*","NAME":"string","DESCRIPTION":"string to show in ascii format"}
],
"RETURN":"u32"
}
END-SYSCALL */

u32 print(u8 * string)
{
	u8     *source;
	u32     i = 0;
	source = string;
	while (*source != 0)
	{
		putchar(*source++);
		i++;
	}
	return i;
}

/*******************************************************************************/
/* Fonction d'affichage (pour printf) */

u32 printstr(u8 * src, u8 ** dest, u32 len)
{
	if (*(src) == '\000')
		return;
	for (u32 i = 0; i < len; i++)
		putchar(*(src++));
	return len;
}

/*******************************************************************************/
/* Fonction d'enregistrement dans une variable (pour sprintf) */

u32 storestr(u8 * src, u8 ** dest, u32 len)
{
	memcpy(src, *dest, len, 1);
	*dest = *dest + len;
	return len;
}

/* EXPORT
{
"LIBRARY":"libsys"
}
*/
#define maxbuffersize 4096

/*******************************************************************************/
/* affiche une chaine de caractère formaté a l'ecran */

u32 printf(const u8 * string, ...)
{
	va_list args;
	va_start(args, string);
	vprintf(string, args);
	va_end(args);
}

/*******************************************************************************/
/* met une chaine de caractère formaté dans une variable */

u32 sprintf(u8 * variable, const u8 * string, ...)
{
	va_list args;
	va_start(args, string);
	vsprintf(variable, string, args);
	va_end(args);
}

/*******************************************************************************/
/* met une chaine de caractère formaté dans une variable de taille fixée */

u32 snprintf(u8 * variable, u32 maxsize, const u8 * string, ...)
{
	va_list args;
	va_start(args, string);
	vsnprintf(variable, maxsize, string, args);
	va_end(args);
}

/*******************************************************************************/
/* affiche une chaine de caractère formaté a l'ecran depuis vararg */

u32 vprintf(const u8 * string, va_list args)
{
	format(string, args, maxbuffersize, &printstr, NULL);
}

/*******************************************************************************/
/* met une chaine de caractère formaté dans une variable depuis vararg */

u32 vsprintf(u8 * variable, const u8 * string, va_list args)
{
	format(string, args, maxbuffersize, &storestr, variable);
}

/*******************************************************************************/
/* met une chaine de caractère formaté dans une variable de taille fixée depuis vararg  */

u32 vsnprintf(u8 * variable, u32 maxsize, const u8 * string, va_list args)
{
	format(string, args, maxsize, &storestr, variable);
}

/*******************************************************************************/
/* affiche une chaine de caractère formaté a l'ecran */

u32 format(const u8 * string, va_list args, u32 maxsize,
	   u32(*fonction) (u8 * src, u8 ** dest, u32 len), u8 * dest)
{
	u64     sizes[] = { 0xFF, 0xFFFF, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF };
	u8      units[][4] =
		{ "o\000\000", "kio", "mio", "gio", "tio", "pio" };
	u8      strbase2[] = "0xb\000";
	u8      strbase8[] = "0xo\000";
	u8      strbase16[] = "0x\000";
	u8      hexadecimal[] = "*0x\000";
	u8      achar, temp;
	u8      asize=2;
	u8	  charadd=0xFF;
	u8 	  unit = 0;
	u8 	  precisioni = 0;
	u8 	  precisionf = 0;
	u8      buffer[maxbuffersize];
	u8     *bufferend;
	u32     buffersize;
	u8     *str = string;
	u8     *strtemp;
	u32     i = 0, counter = 0;
	u64     num = 0;
	bool    flag = false, intok = false, decok = false;

	for (achar = *str; achar != '\000'; i++, achar = *(str + i))
	{
		if (achar != '%' && !flag)
		{
			fonction((str + i), &dest, 1);
			counter++;
			asize = 2;
			precisioni = 0;
			precisionf = 0;
			intok = false;
			decok = false;
			charadd = 0xFF;
		}
		else if (achar == '%' || flag)
		{
			if (!flag)
				++i;
			achar = *(str + i);
			switch (achar)
			{
				case 'z':
					charadd = achar;
					flag = true;
					break;
				case ' ':
					charadd = achar;
					flag = true;
					break;
				case 'h':
					asize--;
					if (asize < 0)
						asize = 0;
					flag = true;
					break;
				case 'l':
					asize++;
					if (asize > 3)
						asize = 3;
					flag = true;
					break;
				case '.':
					intok = true;
					decok = false;
					flag = true;
					break;
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					if (!intok)
					{
						if (!decok)
						{
							precisioni =
								achar -
								'0';
							decok = true;
						}
						else
						{
							precisioni *= 10;
							precisioni +=
								achar -
								'0';
							decok = false;
						}
					}
					else
					{
						if (!decok)
						{
							precisionf =
								achar -
								'0';
							decok = true;
						}
						else
						{
							precisionf *= 10;
							precisionf +=
								achar -
								'0';
							decok = false;
						}
					}
					flag = true;
					break;
				case 'f':
				case 'e':
					if (achar == 'e')
					{
						precisioni = 1;
						precisionf = 8;
					}
					if (asize == 0)
					{
						num = (u64) va_arg(args,
								   u8);
						break;
					}
					else if (asize == 1)
					{
						num = (u64) va_arg(args,
								   u16);
						break;
					}
					else if (asize == 2)
						bufferend =
							rtoasingle((float)
								   va_arg
								   (args,
								    double),
								   &buffer,
								   precisioni,
								   precisionf);
					else
						bufferend =
							rtoadouble((double)
								   va_arg
								   (args,
								    double),
								   &buffer,
								   precisioni,
								   precisionf);
					buffersize =
						bufferend - &buffer[0];
					counter +=
						fonction(&buffer, &dest,
							 buffersize);
					flag = false;
					break;
				case 'u':
					if (asize == 0)
						num = (u64) va_arg(args,
								   u8);
					else if (asize == 1)
						num = (u64) va_arg(args,
								   u16);
					else if (asize == 2)
						num = (u64) va_arg(args,
								   u32);
					else
						num = (u64) va_arg(args,
								   u64);
					if (charadd == 0xFF)
						charadd = '0';
					bufferend =
						itoa(num, &buffer, 10,
						     sizes[asize],
						     charadd);
					buffersize =
						bufferend - &buffer[0];
					counter +=
						fonction(&buffer, &dest,
							 buffersize);
					flag = false;
					break;
				case 'o':
					if (asize == 0)
						num = (u64) va_arg(args,
								   u8);
					else if (asize == 1)
						num = (u64) va_arg(args,
								   u16);
					else if (asize == 2)
						num = (u64) va_arg(args,
								   u32);
					else
						num = (u64) va_arg(args,
								   u64);
					if (charadd == 0xFF)
						charadd = '0';
					counter +=
						fonction(&strbase8, &dest,
							 1);
					bufferend =
						itoa(num, &buffer, 8,
						     sizes[asize],
						     charadd);
					buffersize =
						bufferend - &buffer[0];
					counter +=
						fonction(&buffer, &dest,
							 buffersize);
					flag = false;
					break;
				case '%':
					fonction(&achar, string, 1);
					counter++;
					flag = false;
					break;
				case 'c':
					temp = (u8) va_arg(args, u8);
					fonction(&temp, string, 1);
					counter++;
					flag = false;
					break;
				case 'H':
					if (asize == 0)
						num = (u64) va_arg(args,
								   u8);
					else if (asize == 1)
						num = (u64) va_arg(args,
								   u16);
					else if (asize == 2)
						num = (u64) va_arg(args,
								   u32);
					else
						num = (u64) va_arg(args,
								   u64);
					if (charadd == 0xFF)
						charadd = ' ';
					while (num > 1024 * 10)
					{
						num = num >> 10;
						unit++;
					}
					bufferend =
						sitoa(num, &buffer,
						      sizes[asize]);
					buffersize =
						bufferend - &buffer[0];
					counter +=
						fonction(&buffer, &dest,
							 buffersize);
					counter +=
						fonction(&units[unit],
							 &dest, 3);
					flag = false;
					break;
				case 'd':
				case 'i':
					if (asize == 0)
						num = (u64) va_arg(args,
								   u8);
					else if (asize == 1)
						num = (u64) va_arg(args,
								   u16);
					else if (asize == 2)
						num = (u64) va_arg(args,
								   u32);
					else
						num = (u64) va_arg(args,
								   u64);
					if (charadd == 0xFF)
						charadd = ' ';
					bufferend =
						sitoa(num, &buffer,
						      sizes[asize]);
					buffersize =
						bufferend - &buffer[0];
					counter +=
						fonction(&buffer, &dest,
							 buffersize);
					flag = false;
					break;
				case 's':
					strtemp =
						(u8 *) va_arg(args, u8 *);
					counter +=
						fonction(strtemp, &dest,
							 strlen(strtemp));
					flag = false;
					break;
				case 'p':
					num = (u32) va_arg(args, int);
					if (charadd == 0xFF)
						charadd = '0';
					counter +=
						fonction(&hexadecimal,
							 &dest, 3);
					bufferend =
						itoa(num, &buffer, 16,
						     sizes[asize], '0');
					buffersize =
						bufferend - &buffer[0];
					counter +=
						fonction(&buffer, &dest,
							 buffersize);
					flag = false;
					break;
				case 'x':
				case 'X':
				case 'y':
				case 'Y':
					if (asize == 0)
						num = (u64) va_arg(args,
								   u8);
					else if (asize == 1)
						num = (u64) va_arg(args,
								   u16);
					else if (asize == 2)
						num = (u64) va_arg(args,
								   u32);
					else
						num = (u64) va_arg(args,
								   u64);
					if (charadd == 0xFF)
						charadd = '0';
					if (achar == 'X')
						counter +=
							fonction
							(&strbase16, &dest,
							 2);
					bufferend =
						itoa(num, &buffer, 16,
						     sizes[asize],
						     charadd);
					buffersize =
						bufferend - &buffer[0];
					if (achar == 'X' || achar == 'Y')
						strtoupper(&buffer);
					counter +=
						fonction(&buffer, &dest,
							 buffersize);
					flag = false;
					break;
				case 'b':
					if (asize == 0)
						num = (u64) va_arg(args,
								   u8);
					else if (asize == 1)
						num = (u64) va_arg(args,
								   u16);
					else if (asize == 2)
						num = (u64) va_arg(args,
								   u32);
					else
						num = (u64) va_arg(args,
								   u64);
					if (charadd == 0xFF)
						charadd = '0';
					counter +=
						fonction(&strbase2, &dest,
							 2);
					bufferend =
						itoa(num, &buffer, 2,
						     sizes[asize],
						     charadd);
					buffersize =
						bufferend - &buffer[0];
					counter +=
						fonction(&buffer, &dest,
							 buffersize);
					flag = false;
					break;
				default:
					break;
			}
		}
	}
	buffer[0] = '\000';
	fonction(&buffer, &dest, 1);
	return counter;
}

/*******************************************************************************/
/* converti un réel signé en chaine de caractère */

u8     *rtoadouble(double num, u8 * str, u8 precisioni, u8 precisionf)
{
	s8      power10;
	u8     *pointer = str;
	u8      i, j, integerpart, fracpart;
	if (precisioni == 0)
		precisioni = 12;
	if (precisionf == 0)
		precisionf = 8;
	double  round = 0.5;
	for (i = 0; i < precisionf; i++)
		round /= 10;
	num += round;
	bool    intok = false;
	if (num < 0)
	{
		num = -num;
		*(pointer++) = '-';
	}
	power10 = 0;
	{
		while (num >= 10.0)
		{
			num /= 10;
			power10++;
		}
	}
	if (power10 < precisioni)
	{
		fracpart = power10;
		power10 = 0;
	}
	else
	{
		fracpart = precisioni;
		power10 = power10 - precisioni + 1;
	}
	if (power10 == 0)
	{
		if (num != 0.0)
		{
			while (num < 1.0)
			{
				num *= 10;
				power10--;
			}
		}
	}

	i = j = 0;
	while (num > 0)
	{
		if (!intok & (i > fracpart || i >= precisioni))
		{
			*(pointer++) = '.';
			intok = true;
		}
		if (intok && j >= precisionf)
			break;
		num -= (integerpart = num);
		*(pointer++) = integerpart + '0';
		num *= 10.0;
		if (!intok)
			i++;
		else
			j++;
	}
	while ((*(pointer - 1)) == '0' && pointer > str + 1)
		pointer--;
	if ((*(pointer - 1)) == '.')
		pointer--;
	if (abs(power10) > 0)
	{
		*(pointer++) = 'e';
		if (power10 < 0)
		{
			power10 = -power10;
			*(pointer++) = '-';
		}
		*(pointer++) = (power10 / 10 + '0');
		*(pointer++) = (power10 % 10 + '0');
	}
	*(pointer++) = 0;
}

/*******************************************************************************/
/* converti un réel signé en chaine de caractère */

u8     *rtoasingle(float num, u8 * str, u8 precisioni, u8 precisionf)
{
	return rtoadouble((double) num, str, precisioni, precisionf);
}

/*******************************************************************************/
/* converti un entier non signé en chaine de caractère */

u8     *itoa(u64 orignum, u8 * str, u8 base, u64 dim, u8 achar)
{
	u8     *pointer = str, i, size = 0;
	u64     num = orignum;
	if (dim >= 0xFF)
	{
		num &= dim;
		if ((num == 0) && (achar == 0))
		{
			*(pointer++) = '0';
			*pointer = '\000';
			return pointer;
		}
		switch (base)
		{
			case 2:
				size = log2(dim);
				break;
			case 8:
				size = log2(dim) / 2;
				break;
			case 10:
				size = log10(dim);
				break;
			case 16:
				size = log2(dim) / 4;
				break;
		}
	}
	else
		size = dim;
	for (i = 0; i < size; i++)
	{
		if (num == 0)
		{
			if (i == 0)
				*(pointer++) = '0';
			else
				*(pointer++) = achar;
			if (achar == 0)
				break;
		}
		else
		{
			u64     result = num % (u32) base;
			*(pointer++) =
				(result >
				 9) ? (result - 10) + 'a' : result + '0';
			num = num / (u32) base;
		}
	}
	*pointer = '\000';
	strinvert(str);
	return pointer;
}

/*******************************************************************************/
/* converti un entier en chaine de caractère */

u8     *sitoa(u64 num, u8 * str, u64 dim)
{
	u8     *pointer = str;
	bool    isNegative = false;
	num &= dim;
	if (num == 0)
	{
		*(pointer++) = '0';
		*pointer = '\000';
		return pointer;
	}
	if ((((dim + 1) >> 1) & num) > 0)
	{
		isNegative = true;
		num = (~num & dim) + 1;
	}
	while (num != 0)
	{
		u64     result = num % 10;
		*(pointer++) =
			(result > 9) ? (result - 10) + 'a' : result + '0';
		num = num / 10;
	}
	if (isNegative)
		*(pointer++) = '-';
	*pointer = '\000';
	strinvert(str);
	return pointer;
}

/* END-EXPORT */
