/*
   Copyright (C) 1996-1997 Id Software, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

   See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

 */

/*
 * vid_allg.c - Quick'n'dirty (unfinished) Allegro driver
 * Based on vid_svgalib.c
 *
 * By Peter Wang <tjaden@users.sourceforge.net>
 */

#undef stricmp
#include <allegro.h>

#include "quakedef.h"
#include "d_local.h"



unsigned short d_8to16table[256];
static byte *vid_surfcache;
static int VID_highhunkmark;

static int scantokey[KEY_MAX];


static int UseMouse = 1;
static int UseDisplay = 1;
static int UseKeyboard = 1;


static cvar_t vid_mode =		{"vid_mode", "5", false};
static cvar_t vid_redrawfull = 	{"vid_redrawfull", "0", false};
static cvar_t vid_waitforrefresh = 	{"vid_waitforrefresh", "0", true};

static cvar_t mouse_button_commands[3] = {
    {"mouse1", "+attack"},
    {"mouse2", "+strafe"},
    {"mouse3", "+forward"},
};

static cvar_t m_filter = {"m_filter", "0"};



/* Common video modes (only 8 bpp for now) */
struct modeline {
    int width, height;
    int bytesperpixel;
};

static struct modeline modes[] = {
    { 320, 200, 1 }, { 320, 240, 1 },
    { 640, 400, 1 }, { 640, 480, 1 },
    { 512, 384, 1 }, { 800, 600, 1 },
    { 1024, 768, 1 }, { 1280, 1024, 1 }		/* Ha! */
};

#define NUM_MODES	(sizeof(modes) / sizeof(struct modeline))

static int current_mode = 0;



static void _init_allegro()
{
    static int virgin = 1;
    if (virgin) {
	allegro_init();
	virgin = 0;
    }	
}



void D_BeginDirectRect(int x, int y, byte * pbitmap, int width, int height)
{				       
    /* TODO */
}


void D_EndDirectRect(int x, int y, int width, int height)
{
    /* TODO */
}



/*
 =================
 VID_Gamma_f

 Keybinding command
 =================
 */
void VID_Gamma_f(void)
{
    float gamma, f, inf;
    unsigned char palette[768];
    int i;

    if (Cmd_Argc() == 2) {
	gamma = Q_atof(Cmd_Argv(1));

	for (i = 0; i < 768; i++) {
	    f = pow((host_basepal[i] + 1) / 256.0, gamma);
	    inf = f * 255 + 0.5;
	    if (inf < 0)
		inf = 0;
	    if (inf > 255)
		inf = 255;
	    palette[i] = inf;
	}

	VID_SetPalette(palette);

	vid.recalc_refdef = 1;	// force a surface cache flush

    }
}


void VID_DescribeMode_f(void)
{
    int modenum;
    
    modenum = Q_atoi (Cmd_Argv(1));
    if ((modenum >= NUM_MODES) || (modenum < 0 ) || !modes[modenum].width) {
	Con_Printf("Invalid video mode: %d!\n", modenum);
	return;
    }
    
    Con_Printf("%d: %d x %d - %d bpp\n", modenum, modes[modenum].width, modes[modenum].height, 
	       modes[modenum].bytesperpixel << 3);
}


void VID_DescribeModes_f(void)
{
    int i;
    for (i=0; i<NUM_MODES; i++) {
	Con_Printf("%d: %d x %d - %d bpp\n", i, modes[i].width, modes[i].height, 
		   modes[i].bytesperpixel << 3);
    }
}


static int get_mode(char *name, int width, int height, int depth)
{
    int i, match = -1;
    
    if (name) {
	i = Q_atof(name);
	if (i < 0 || i >= NUM_MODES) {
	    Sys_Printf("Mode [%s] not supported\n", name);
	    return 0;
	}
	return i;
    } 

    for (i=0; i<NUM_MODES; i++) 
	if (modes[i].width) {
	    if (modes[i].width == width 
		&& modes[i].height == height
		&& modes[i].bytesperpixel == depth / 8) 
		break;
	    
	    if ((match == -1) && (modes[i].width == width || modes[i].height == height))
		match = i;
	}
    
    if (i == NUM_MODES) {
	if (match == -1) 
	    Sys_Printf("Mode %dx%d (%d bits) not supported\n", width, height, depth);
	i = (match == -1) ? 0 : match;
    }

    return i;
}



/*
   ================
   VID_NumModes
   ================
 */
int VID_NumModes()
{
    return NUM_MODES;
}


void VID_NumModes_f(void)
{
    Con_Printf("%d modes\n", VID_NumModes());
}


void VID_Debug_f(void)
{
    Con_Printf("mode: %d\n", current_mode);
    Con_Printf("height x width: %d x %d\n", vid.height, vid.width);
    Con_Printf("bpp: %d\n", modes[current_mode].bytesperpixel * 8);
    Con_Printf("vid.aspect: %f\n", vid.aspect);
}


void VID_InitModes(void)
{
    /* TODO (?) */
}


void VID_Shutdown(void)
{
}



void VID_ShiftPalette(unsigned char *p)
{
    VID_SetPalette(p);
}


void VID_SetPalette(byte * palette)
{
    byte *ch = palette;
    PALETTE pal;
    int i;

    for (i = 0; i < 256; i++) {
	pal[i].r = *ch++ >> 2;
	pal[i].g = *ch++ >> 2;
	pal[i].b = *ch++ >> 2;
    }

    set_palette(pal);
}


int VID_SetMode(int modenum, unsigned char *palette)
{
    int bsize, zsize, tsize;

    if ((modenum >= NUM_MODES) || (modenum < 0) || !modes[modenum].width) {
	Cvar_SetValue ("vid_mode", (float)current_mode);
	Con_Printf("No such video mode: %d\n", modenum);
	return 0;
    }

    if (set_gfx_mode(GFX_AUTODETECT, modes[modenum].width, modes[modenum].height, 0, 0) < 0) {
	set_gfx_mode(GFX_SAFE, 320, 200, 0, 0);
	modenum = 0;
    }

    set_window_title("Quake/Allegro");
    VID_SetPalette(palette);
    vid.direct = screen->dat;

    Cvar_SetValue("vid_mode", (float)modenum);

    vid.width = SCREEN_W;
    vid.height = SCREEN_H;
    vid.rowbytes = vid.width;

    vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0);
    vid.colormap = (pixel_t *) host_colormap;
    vid.fullbright = 256 - LittleLong(*((int *)vid.colormap + 2048));
    vid.conrowbytes = vid.rowbytes;
    vid.conwidth = vid.width;
    vid.conheight = vid.height;
    vid.numpages = 1;

    vid.maxwarpwidth = WARP_WIDTH;
    vid.maxwarpheight = WARP_HEIGHT;

    // alloc zbuffer and surface cache
    if (d_pzbuffer) {
	D_FlushCaches();
	Hunk_FreeToHighMark(VID_highhunkmark);
	d_pzbuffer = NULL;
	vid_surfcache = NULL;
    }

    bsize = vid.rowbytes * vid.height;
    tsize = D_SurfaceCacheForRes(vid.width, vid.height);
    zsize = vid.width * vid.height * sizeof(*d_pzbuffer);

    VID_highhunkmark = Hunk_HighMark();

    d_pzbuffer = Hunk_HighAllocName(bsize + tsize + zsize, "video");

    vid_surfcache = ((byte *) d_pzbuffer) + zsize;

    vid.conbuffer = vid.buffer = (pixel_t *) (((byte *) d_pzbuffer) + zsize + tsize);

    D_InitCaches(vid_surfcache, tsize);

    vid.recalc_refdef = 1;	// force a surface cache flush
    
    current_mode = modenum;

    return 0;
}


static void keyhandler(int scancode)
{
    int sc = scancode & 0x7f;
    
    if (!sc)
	return;

    if (scantokey[sc] == -1) {
	Con_Printf("Unsupported key: %d. Please add to vid_allg.c\n", sc);
	return;
    }
    
    Key_Event(scantokey[sc], !(scancode & 0x80));
}


void VID_Init(unsigned char *palette)
{
    int i;
    int w, h, d;

    //Cmd_AddCommand ("gamma", VID_Gamma_f);

    if (UseDisplay) {
	_init_allegro();

	VID_InitModes();

	Cvar_RegisterVariable(&vid_mode);
	Cvar_RegisterVariable(&vid_redrawfull);
	Cvar_RegisterVariable(&vid_waitforrefresh);

	Cmd_AddCommand("vid_nummodes", VID_NumModes_f);
	Cmd_AddCommand("vid_describemode", VID_DescribeMode_f);
	Cmd_AddCommand("vid_describemodes", VID_DescribeModes_f);
	Cmd_AddCommand("vid_debug", VID_Debug_f);

	// interpret command-line params

	w = h = d = 0;
	if (COM_CheckParm("-mode"))
	    current_mode = get_mode(com_argv[COM_CheckParm("-mode")+1], w, h, d);
	else if (COM_CheckParm("-w") || COM_CheckParm("-h") || COM_CheckParm("-d"))
	{
	    if (COM_CheckParm("-w"))
		w = Q_atoi(com_argv[COM_CheckParm("-w")+1]);
	    if (COM_CheckParm("-h"))
		h = Q_atoi(com_argv[COM_CheckParm("-h")+1]);
	    if (COM_CheckParm("-d"))
		d = Q_atoi(com_argv[COM_CheckParm("-d")+1]);
	    current_mode = get_mode(0, w, h, d);
	}
	else
	    current_mode = 0;

	// set vid parameters
	VID_SetMode(current_mode, palette);

	// we do want to run in the background when switched away
	set_display_switch_mode(SWITCH_BACKGROUND);
    }

    if (COM_CheckParm("-nokbd"))
	UseKeyboard = 0;

    if (UseKeyboard) {
	_init_allegro();
	install_keyboard();

	for (i = 0; i < KEY_MAX; i++)
	    scantokey[i] = -1;

	/*
	 *	Fun (tm).
	 */

	scantokey[KEY_LSHIFT] = K_SHIFT;
	scantokey[KEY_RSHIFT] = K_SHIFT;
	scantokey[KEY_UP] = K_UPARROW;
	scantokey[KEY_DOWN] = K_DOWNARROW;
	scantokey[KEY_LEFT] = K_LEFTARROW;
	scantokey[KEY_RIGHT] = K_RIGHTARROW;
	scantokey[KEY_LCONTROL] = K_CTRL;
	scantokey[KEY_RCONTROL] = K_CTRL;
	scantokey[KEY_ALT] = K_ALT;
	scantokey[KEY_ALTGR] = K_ALT;
	//scantokey[KEY_CAPSLOCK] = JK_CAPS;
	//scantokey[KEY_NUMLOCK] = JK_NUM_LOCK;
	scantokey[KEY_HOME] = K_HOME;
	scantokey[KEY_PGUP] = K_PGUP;
	scantokey[KEY_END] = K_END;
	scantokey[KEY_PGDN] = K_PGDN;
	scantokey[KEY_INSERT] = K_INS;
	scantokey[KEY_DEL] = K_DEL;
	scantokey[KEY_ESC] = K_ESCAPE;
	scantokey[KEY_ENTER] = K_ENTER;
	scantokey[KEY_TAB] = K_TAB;
	scantokey[KEY_BACKSPACE] = K_BACKSPACE;
	scantokey[KEY_PAUSE] = K_PAUSE;
	scantokey[KEY_SPACE] = ' ';

	scantokey[KEY_0_PAD] = K_INS;
	scantokey[KEY_1_PAD] = K_END;
	scantokey[KEY_2_PAD] = K_DOWNARROW;
	scantokey[KEY_3_PAD] = K_PGDN;
	scantokey[KEY_4_PAD] = K_LEFTARROW;
	scantokey[KEY_5_PAD] = '5';
	scantokey[KEY_6_PAD] = K_RIGHTARROW;
	scantokey[KEY_7_PAD] = K_HOME;
	scantokey[KEY_8_PAD] = K_UPARROW;
	scantokey[KEY_9_PAD] = K_PGUP;
	//scantokey[KEY_NUMLOCK] = K_NUMLOCK;
	scantokey[KEY_MINUS_PAD] = '-';
	scantokey[KEY_PLUS_PAD] = '+';
	scantokey[KEY_ASTERISK] = '*';
	scantokey[KEY_SLASH_PAD] = '/';
	scantokey[KEY_DEL_PAD] = K_DEL;
	scantokey[KEY_ENTER_PAD] = K_ENTER;


	scantokey[KEY_1] = '1';
	scantokey[KEY_2] = '2';
	scantokey[KEY_3] = '3';
	scantokey[KEY_4] = '4';
	scantokey[KEY_5] = '5';
	scantokey[KEY_6] = '6';
	scantokey[KEY_7] = '7';
	scantokey[KEY_8] = '8';
	scantokey[KEY_9] = '9';
	scantokey[KEY_0] = '0';
	scantokey[KEY_MINUS] = '-';
	scantokey[KEY_EQUALS] = '=';
	scantokey[KEY_TILDE] = '`';
	scantokey[KEY_OPENBRACE] = '[';
	scantokey[KEY_CLOSEBRACE] = ']';
	scantokey[KEY_COLON] = ';';
	scantokey[KEY_QUOTE] = '\'';
	scantokey[KEY_COMMA] = ',';
	scantokey[KEY_STOP] = '.';
	scantokey[KEY_SLASH] = '/';
	scantokey[KEY_BACKSLASH] = '\\';

	scantokey[KEY_F1] = K_F1;
	scantokey[KEY_F2] = K_F2;
	scantokey[KEY_F3] = K_F3;
	scantokey[KEY_F4] = K_F4;
	scantokey[KEY_F5] = K_F5;
	scantokey[KEY_F6] = K_F6;
	scantokey[KEY_F7] = K_F7;
	scantokey[KEY_F8] = K_F8;
	scantokey[KEY_F9] = K_F9;
	scantokey[KEY_F10] = K_F10;
	scantokey[KEY_F11] = K_F11;
	scantokey[KEY_F12] = K_F12;
	scantokey[KEY_A] = 'a';
	scantokey[KEY_B] = 'b';
	scantokey[KEY_C] = 'c';
	scantokey[KEY_D] = 'd';
	scantokey[KEY_E] = 'e';
	scantokey[KEY_F] = 'f';
	scantokey[KEY_G] = 'g';
	scantokey[KEY_H] = 'h';
	scantokey[KEY_I] = 'i';
	scantokey[KEY_J] = 'j';
	scantokey[KEY_K] = 'k';
	scantokey[KEY_L] = 'l';
	scantokey[KEY_M] = 'm';
	scantokey[KEY_N] = 'n';
	scantokey[KEY_O] = 'o';
	scantokey[KEY_P] = 'p';
	scantokey[KEY_Q] = 'q';
	scantokey[KEY_R] = 'r';
	scantokey[KEY_S] = 's';
	scantokey[KEY_T] = 't';
	scantokey[KEY_U] = 'u';
	scantokey[KEY_V] = 'v';
	scantokey[KEY_W] = 'w';
	scantokey[KEY_X] = 'x';
	scantokey[KEY_Y] = 'y';
	scantokey[KEY_Z] = 'z';

	keyboard_lowlevel_callback = keyhandler;
    }
}


void VID_Update(vrect_t * rects)
{
    /* TODO */
    /* if (vid_redrawfull.value) { */
    if (1) {
	int y, w = vid.rowbytes;
	char *p = vid.buffer;
	char *dest;

	for (y = 0; y < vid.height; y++) {
	    dest = (char *)bmp_write_line(screen, y);
	    memcpy(dest, p, w);
	    p += w;
	}
	bmp_unwrite_line(screen);

    }
#if 0
    else {
	while (rects) {
	    ycount = rects->height;
	    offset = rects->y * vid.rowbytes + rects->x;
	    while (ycount--) {
		register int i = offset % 0x10000;

		if ((offset / 0x10000) != vidpga
		    e) {
		    vidpage = offset / 0x10000;
		    vga_setpage(vidpage);
		}
		if (rects->width + i > 0x10000) {
		    memcpy(framebuffer_ptr + i,
			   vid.buffer + offset,
			   0x10000 - i);
		    vga_setpage(++vidpage);
		    memcpy(framebuffer_ptr,
			   vid.buffer + offset + 0x10000 - i,
			   rects->width - 0x10000 + i);
		}
		else
		    memcpy(framebuffer_ptr + i,
			   vid.buffer + offset,
			   rects->width);
		offset += vid.rowbytes;
	    }

	    rects = rects->pnext;
	}
    }
#endif
}


void VID_DitherOn(void)
{
}


void VID_DitherOff(void)
{
}


void Sys_SendKeyEvents(void)
{
}


void Force_CenterView_f(void)
{
    cl.viewangles[PITCH] = 0;
}



void IN_Init(void)
{
    if (UseMouse) {
	_init_allegro();
	install_mouse();
	install_timer();

	Cvar_RegisterVariable (&mouse_button_commands[0]);
	Cvar_RegisterVariable (&mouse_button_commands[1]);
	Cvar_RegisterVariable (&mouse_button_commands[2]);
	Cvar_RegisterVariable (&m_filter);
	Cmd_AddCommand ("force_centerview", Force_CenterView_f);
    }
}


void IN_Shutdown(void)
{
}


/*
   ===========
   IN_Commands
   ===========
 */
static int old_mouse_b;

void IN_Commands(void)
{
    static int mask[] = { 1, 2, 4};
    static int k[] = { K_MOUSE1, K_MOUSE2, K_MOUSE3 };
    int i;
    
    if (!UseMouse)
	return;

    for (i=0; i<3; i++) {
	if (mouse_b & mask[i] && !(old_mouse_b & mask[i]))
	    Key_Event (k[i], true);
	else if (!(mouse_b & mask[i]) && (old_mouse_b & mask[i]))
	    Key_Event (k[i], false);
    } 
    
    old_mouse_b = mouse_b;
}


/*
   ===========
   IN_Move
   ===========
 */

static float _mouse_x, _mouse_y;
static float _old_mouse_x, _old_mouse_y;

void IN_MouseMove(usercmd_t * cmd)
{
    int mx, my;
    
    if (!UseMouse)
	return;

    get_mouse_mickeys(&mx, &my);

    if (m_filter.value) {
	_mouse_x = (mx + _old_mouse_x) * 0.5;
	_mouse_y = (my + _old_mouse_y) * 0.5;
    }
    else {
	_mouse_x = mx;
	_mouse_y = my;
    }
    _old_mouse_x = mx;
    _old_mouse_y = my;

    _mouse_x *= sensitivity.value;
    _mouse_y *= sensitivity.value;

    // add mouse X/Y movement to cmd
    if ((in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1)))
	cmd->sidemove += m_side.value * _mouse_x;
    else
	cl.viewangles[YAW] -= m_yaw.value * _mouse_x;
	
    if (in_mlook.state & 1)
	V_StopPitchDrift();
		
    if ((in_mlook.state & 1) && !(in_strafe.state & 1)) {
	cl.viewangles[PITCH] += m_pitch.value * _mouse_y;
	if (cl.viewangles[PITCH] > 80)
	    cl.viewangles[PITCH] = 80;
	if (cl.viewangles[PITCH] < -70)
	    cl.viewangles[PITCH] = -70;
    }
    else {
	if ((in_strafe.state & 1) && noclip_anglehack)
	    cmd->upmove -= m_forward.value * _mouse_y;
	else
	    cmd->forwardmove -= m_forward.value * _mouse_y;
    }
}


void IN_Move(usercmd_t * cmd)
{
    IN_MouseMove(cmd);
}



/*
   ================
   VID_ModeInfo
   ================
 */
char *VID_ModeInfo(int modenum)
{
    static char buf[80];
    sprintf(buf, "%d x %d - %d\n", modes[modenum].width, modes[modenum].height, 
	    modes[modenum].bytesperpixel << 3);
    return buf;
}
