/*
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_amiga.c -- amiga video driver

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

#include <exec/exec.h>
#include <graphics/gfx.h>
#include <intuition/intuition.h>
#include <libraries/asl.h>
#include <cybergraphics/cybergraphics.h>
#include <utility/tagitem.h>

#include <clib/chunkyppc_protos.h>
#include <powerup/ppcproto/exec.h>
#include <powerup/ppcproto/graphics.h>
#include <powerup/ppcproto/intuition.h>
#include <powerup/ppcproto/asl.h>

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

#ifdef PPC
extern void ppc_c2p_line (int line, int src, struct BitMap *dst, int cnt);
#endif

extern qboolean using_mouse;
extern short int last_mouse[2];
qboolean mousemove = false;
qboolean fastaga = false;
qboolean force = false;
qboolean nodb = false;
qboolean wpa8 = false;

#define	BASEWIDTH	320
#define	BASEHEIGHT	200

pixel_t *vid_buffer;
short *zbuffer;
byte *surfcache;

unsigned short	d_8to16table[256];

/**********************************************************************/

struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct Library *ChunkyPPCBase = NULL;
struct Library *AslBase = NULL;

static struct ScreenBuffer *sbuffer[3] = {NULL, NULL, NULL};
static struct ScreenBuffer *nextsbuffer = NULL;
static struct Mode_Screen ms;
struct Mode_Screen *msptr = &ms;
static struct Screen *video_screen = NULL;
static struct Window *video_window = NULL;
static struct ScreenModeRequester *smr = NULL;
static struct RastPort rp;
static struct RastPort temprp;
static struct BitMap *next_bm;
static unsigned char *next_sbuf;
static struct BitMap tmp_bm = {
  0, 0, 0, 0, 0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};

static UWORD *emptypointer = NULL;
static BOOL is_cyber_mode = FALSE;
static BOOL is_native_mode = FALSE;

/**********************************************************************/
void	VID_SetPalette (unsigned char *palette)
{
  int i;
  ULONG v;
  static ULONG colourtable[1+3*256+1];

  colourtable[0] = (256 << 16) + 0;
  for (i = 0; i < 3*256; i++) {
    v = *palette++;
    v += (v << 8);
    v += (v << 16);
    colourtable[i+1] = v;
  }
  colourtable[1 + 3*256] = 0;
	if ((fastaga) || (wpa8))
		LoadRGB32 (&video_screen->ViewPort, colourtable);
	else
  	LoadColors (&ms, colourtable);
}

/**********************************************************************/
void	VID_ShiftPalette (unsigned char *palette)
{
  VID_SetPalette (palette);
}

/****************************************************************************/
void	VID_Init (unsigned char *palette)
{
  int mode, d;
	int width = 0, height = 0;
	int w, h;
	ULONG idcmp, flags, propertymask;
	struct TagItem stags[7];
	struct TagItem wtags[8];
	struct TagItem atags[12];

	fastaga = COM_CheckParm ("-aga");
	force = COM_CheckParm ("-force");
	nodb = COM_CheckParm ("-nodb");
	wpa8 = COM_CheckParm ("-wpa8");

	if ((!fastaga) && (!wpa8))
		ChunkyPPCBase = OpenLibrary("chunkyppc.library", 3);

	(struct Library *)GfxBase = OpenLibrary("graphics.library", 39);

	(struct Library *)IntuitionBase = OpenLibrary("intuition.library", 39);

	AslBase = OpenLibrary("asl.library", 38);

	if ((!fastaga) && (!wpa8))
	{
		if (ChunkyPPCBase == NULL)
			Sys_Error ("Can't open chunkyppc.library V3\n");
	}

	if (GfxBase == NULL)
		Sys_Error ("Can't open graphics.library V39\n");

	if (IntuitionBase == NULL)
		Sys_Error ("Can't open intuition.library V39\n");

	if ((AslBase = OpenLibrary ("asl.library", 38L)) == NULL ||
      (smr = AllocAslRequest (ASL_ScreenModeRequest, NULL)) == NULL)
    Sys_Error ("OpenLibrary(""asl.library"", 38) failed");

	if ((fastaga) || (wpa8))
	{
		if (fastaga)
			is_native_mode = TRUE;

		propertymask = DIPF_IS_EXTRAHALFBRITE | DIPF_IS_DUALPF | DIPF_IS_PF2PRI |
									 DIPF_IS_HAM;

		atags[0].ti_Tag = ASLSM_TitleText;
		atags[0].ti_Data = (ULONG)"WarpQuake";
		atags[1].ti_Tag = ASLSM_MinWidth;
		atags[1].ti_Data = BASEWIDTH;
		atags[2].ti_Tag = ASLSM_MinHeight;
		atags[2].ti_Data = BASEHEIGHT;
		atags[3].ti_Tag = ASLSM_InitialDisplayWidth;
		atags[3].ti_Data = BASEWIDTH;
		atags[4].ti_Tag = ASLSM_InitialDisplayHeight;
		atags[4].ti_Data = BASEHEIGHT;
		atags[5].ti_Tag = ASLSM_MinDepth;
		atags[5].ti_Data = 8;
		atags[6].ti_Tag = ASLSM_MaxDepth;
		atags[6].ti_Data = 8;
		atags[7].ti_Tag = ASLSM_DoWidth;
		atags[7].ti_Data = TRUE;
		atags[8].ti_Tag = ASLSM_DoHeight;
		atags[8].ti_Data = TRUE;
		atags[9].ti_Tag = ASLSM_PropertyMask;
		atags[9].ti_Data = propertymask;
		atags[10].ti_Tag = ASLSM_PropertyFlags;
		atags[10].ti_Data = 0;
		atags[11].ti_Tag = TAG_DONE;
		atags[11].ti_Data = 0;

		if((AslRequest (smr, atags)) == NULL)
			Sys_Error ("AslRequest() failed\n");

		mode = smr->sm_DisplayID;
		width = smr->sm_DisplayWidth;
		height = smr->sm_DisplayHeight;

		stags[0].ti_Tag = SA_Type;
		stags[0].ti_Data = CUSTOMSCREEN;
		stags[1].ti_Tag = SA_DisplayID;
		stags[1].ti_Data = mode;
		stags[2].ti_Tag = SA_Width;
		stags[2].ti_Data = width;
		stags[3].ti_Tag = SA_Height;
		stags[3].ti_Data = height;
		stags[4].ti_Tag = SA_Depth;
		stags[4].ti_Data = 8;
		stags[5].ti_Tag = SA_Quiet;
		stags[5].ti_Data = TRUE;
		stags[6].ti_Tag = TAG_END;
		stags[6].ti_Data = 0;

		if((video_screen = OpenScreenTagList (NULL, stags)) == NULL)
			Sys_Error ("OpenScreenTagList() failed\n");

		idcmp = IDCMP_RAWKEY;
		flags = WFLG_ACTIVATE | WFLG_BORDERLESS | WFLG_NOCAREREFRESH |
          	WFLG_SIMPLE_REFRESH;
  	if (using_mouse)
		{
    	idcmp |= IDCMP_MOUSEBUTTONS | IDCMP_DELTAMOVE | IDCMP_MOUSEMOVE;
    	flags |= WFLG_RMBTRAP | WFLG_REPORTMOUSE;
  	}

		wtags[0].ti_Tag = WA_Left;
		wtags[0].ti_Data = 0;
		wtags[1].ti_Tag = WA_Top;
		wtags[1].ti_Data = 0;
		wtags[2].ti_Tag = WA_Width;
		wtags[2].ti_Data = width;
		wtags[3].ti_Tag = WA_Height;
		wtags[3].ti_Data = height;
		wtags[4].ti_Tag = WA_IDCMP;
		wtags[4].ti_Data = idcmp;
		wtags[5].ti_Tag = WA_Flags;
		wtags[5].ti_Data = flags;
		wtags[6].ti_Tag = WA_CustomScreen;
		wtags[6].ti_Data = (ULONG) video_screen;
		wtags[7].ti_Tag = TAG_DONE;
		wtags[7].ti_Data = 0;

		if((video_window = OpenWindowTagList (NULL, wtags)) == NULL)
			Sys_Error ("OpenWindowTagList() failed\n");

		if ((emptypointer = AllocVec (16, MEMF_CHIP | MEMF_CLEAR)) == NULL)
			Sys_Error ("Couldn't allocate chip memory for pointer");
		SetPointer (video_window, emptypointer, 1, 16, 0, 0);

		InitRastPort (&rp);
		rp.BitMap = video_screen->ViewPort.RasInfo->BitMap;

		if (wpa8)
		{
			InitBitMap (&tmp_bm, 8, width, 1);
			for (d = 0; d < 8; d++)
				if ((tmp_bm.Planes[d] = (PLANEPTR)AllocRaster (width, 1)) == NULL)
					Sys_Error ("AllocRaster() failed");
			temprp = *video_window->RPort;
			temprp.Layer = NULL;
			temprp.BitMap = &tmp_bm;
		}

		if ((!nodb) && (!wpa8))
		{
			if (((sbuffer[0] = AllocScreenBuffer (video_screen, NULL, SB_SCREEN_BITMAP)) == NULL) |
        	((sbuffer[1] = AllocScreenBuffer (video_screen, NULL, NULL)) == NULL ||
        	(sbuffer[2] = AllocScreenBuffer (video_screen, NULL, NULL)) == NULL))
      	Sys_Error ("AllocScreenBuffer() failed");
    	nextsbuffer = sbuffer[1];
			rp.BitMap = nextsbuffer->sb_BitMap;
		}
	}
	else
	{
		w = COM_CheckParm ("-width");
		if (w)
		{
			if (w < com_argc-1)
				ms.SCREENWIDTH = Q_atoi (com_argv[w+1]);
			else
				Sys_Error ("You must specify a size after -width");
		}
		else
			ms.SCREENWIDTH = BASEWIDTH;

		h = COM_CheckParm ("-height");
		if (h)
		{
			if (h < com_argc-1)
				ms.SCREENHEIGHT = Q_atoi (com_argv[h+1]);
			else
				Sys_Error ("You must specify a size after -height");
		}
		else
			ms.SCREENHEIGHT = BASEHEIGHT;

		ms.MS_MAXWIDTH = 1280;
		ms.MS_MAXHEIGHT = 1024;
		ms.MAXDEPTH = 8;
		ms.MINDEPTH = 8;
		ms.video_screen = 0;
		ms.video_window = 0;

		if (force)
			msptr = OpenGraphics ("WarpQuake", &ms, 1);
		else
			msptr = OpenGraphics ("WarpQuake", &ms, 0);

		if (!msptr)
			Sys_Error ("OpenGraphics() failed\n");

		if (!(ChunkyInit(&ms,PIXFMT_LUT8)))
			Sys_Error ("ChunkyInit() failed\n");

		if (ms.video_is_native_mode)
		{
			is_native_mode = TRUE;
			if (ms.numbuffers == 1)
				next_bm = ms.bitmapa;
			else
				next_bm = ms.bitmapb;
		}
		else
		{
			if (ms.video_is_cyber_mode)
				is_cyber_mode = TRUE;
			if (ms.numbuffers == 1)
				next_sbuf = ms.screen;
			else
				next_sbuf = ms.screenb;
		}

  	mode = ms.mode;
  	width = ms.SCREENWIDTH;
  	height = ms.SCREENHEIGHT;
		video_window = ms.video_window;

#ifdef DEBUG
		printf("ms.SCREENWIDTH: %i\n", ms.SCREENWIDTH);
		printf("ms.SCREENHEIGHT: %i\n", ms.SCREENHEIGHT);
#endif

  	Con_Printf ("Screen Mode $%08x is", mode);
  	if (is_native_mode)
    	Con_Printf (" NATIVE-PLANAR");
  	else
    	Con_Printf (" FOREIGN");
  	Con_Printf (" 8-BIT");
  	if (is_cyber_mode)
    	Con_Printf (" CYBERGRAPHX");
  	Con_Printf (", using size %d x %d\n", width, height);

		if (!using_mouse)
		{
			if(!(ModifyIDCMP(ms.video_window, IDCMP_RAWKEY)))
				Sys_Error ("ModifyIDCMP() failed\n");
		}
	}

  if ((vid_buffer = (pixel_t *)malloc(sizeof(pixel_t) *
                                      width * height)) == NULL ||
      (zbuffer = (short *)malloc(sizeof(short) * width * height)) == NULL ||
      (surfcache = (byte *)malloc(sizeof(byte) *
                                  (width*height/(320*200))*256*1024*2)) == NULL)
			Sys_Error ("Out of memory");

  vid.width = vid.conwidth = width;
  vid.height = vid.conheight = height;
  vid.maxwarpwidth = WARP_WIDTH;
  vid.maxwarpheight = WARP_HEIGHT;
  vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0);
	if (wpa8)
		vid.numpages = 1;
	else if (fastaga)
		if (nodb)
			vid.numpages = 1;
		else
			vid.numpages = 3;
	else
  	vid.numpages = ms.numbuffers;
  vid.colormap = host_colormap;
  vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
  vid.buffer = vid.conbuffer = vid_buffer;
  vid.rowbytes = vid.conrowbytes = width;
  vid.direct = NULL;

  d_pzbuffer = zbuffer;
  D_InitCaches (surfcache, sizeof(byte) *
                           (width*height/(320*200))*256*1024*2);

  VID_SetPalette (palette);

}

/**********************************************************************/
void	VID_Shutdown (void)
{
	int d;

//  printf ("VID_Shutdown\n");
  if (surfcache != NULL)
	{
    free (surfcache);
    surfcache = NULL;
  }

  if (zbuffer != NULL)
	{
    free (zbuffer);
    zbuffer = NULL;
  }

  if (vid_buffer != NULL)
	{
    free (vid_buffer);
    vid_buffer = NULL;
  }

	if (sbuffer[0] != NULL)
	{
		ChangeScreenBuffer (video_screen, sbuffer[0]);
    WaitTOF ();
    WaitTOF ();
		FreeScreenBuffer (video_screen, sbuffer[0]);
		sbuffer[0] = NULL;
	}

	if (sbuffer[1] != NULL)
	{
		FreeScreenBuffer (video_screen, sbuffer[1]);
		sbuffer[1] = NULL;
	}

	if (sbuffer[2] != NULL)
	{
		FreeScreenBuffer (video_screen, sbuffer[2]);
		sbuffer[2] = NULL;
	}

	if ((fastaga) || (wpa8))
	{
		if (video_window != NULL)
		{
			ClearPointer (video_window);
			CloseWindow (video_window);
			video_window = NULL;
		}
	}

	if (emptypointer != NULL)
	{
		FreeVec (emptypointer);
		emptypointer = NULL;
	}

	if (video_screen != NULL)
	{
		CloseScreen (video_screen);
		video_screen = NULL;
	}

	for (d = 0; d < 8; d++)
	{
  	if (tmp_bm.Planes[d] != NULL)
		{
    	FreeRaster (tmp_bm.Planes[d], vid.width, 1);
			tmp_bm.Planes[d] = NULL;
		}
	}

	if (smr != NULL)
	{
		FreeAslRequest (smr);
		smr = NULL;
	}

	if (msptr != NULL)
	{
		CloseGraphics (msptr, 1);
		msptr = NULL;
	}

	if (AslBase != NULL)
	{
		CloseLibrary (AslBase);
		AslBase = NULL;
	}

	if (ChunkyPPCBase != NULL)
	{
		CloseLibrary (ChunkyPPCBase);
		ChunkyPPCBase = NULL;
	}

	if (GfxBase != NULL)
	{
		CloseLibrary ((struct Library *)GfxBase);
		GfxBase = NULL;
	}

	if (IntuitionBase != NULL)
	{
		CloseLibrary ((struct Library *)IntuitionBase);
		IntuitionBase = NULL;
	}
}

/**********************************************************************/
void	VID_Update (vrect_t *rects)
{
  int i, j;

	if (is_native_mode)
	{
		if (fastaga)
		{
			while (rects != NULL)
			{
				for (i = rects->y, j = ((int)(vid_buffer)) + rects->y * vid.width;
      									i < rects->y + rects->height; i++, j += vid.width)
        	ppc_c2p_line (i, j, rp.BitMap, (vid.width + 31) >> 5);
				rects = rects->pnext;
			}
			if (!nodb)
			{
				if (ChangeScreenBuffer (video_screen, nextsbuffer))
				{
      		if (nextsbuffer == sbuffer[0])
        		nextsbuffer = sbuffer[1];
      		else if (nextsbuffer == sbuffer[1])
        		nextsbuffer = sbuffer[2];
					else
						nextsbuffer = sbuffer[0];
					rp.BitMap = nextsbuffer->sb_BitMap;
				}
			}
		}
		else
		{
			while (rects != NULL)
			{
				CallChunkyCopy (&ms, (unsigned char *) next_bm, (void *) vid_buffer,
												PIXFMT_LUT8, 0, 0);
				if (ms.numbuffers == 2)
				{
					if (next_bm == ms.bitmapa)
						next_bm = ms.bitmapb;
					else
						next_bm = ms.bitmapa;
				}
				else if (ms.numbuffers == 3)
				{
					if (next_bm == ms.bitmapa)
						next_bm = ms.bitmapb;
					else if (next_bm == ms.bitmapb)
						next_bm = ms.bitmapc;
					else
						next_bm = ms.bitmapa;
				}
				rects = rects->pnext;
			}
			DoubleBuffer (&ms);
		}
	}
	else if (wpa8)
	{
		while (rects != NULL)
		{
			WritePixelArray8 (video_window->RPort, rects->x, rects->y,
                      	rects->x + rects->width - 1,
                      	rects->y + rects->height - 1,
                      	vid_buffer, &temprp);
			rects = rects->pnext;
		}
	}
	else
	{
		while (rects != NULL)
		{
			CallChunkyCopy (&ms, next_sbuf, (void *) vid_buffer, PIXFMT_LUT8, 0, 0);
			if (ms.numbuffers == 2)
			{
				if (next_sbuf == ms.screen)
					next_sbuf = ms.screenb;
				else
					next_sbuf = ms.screen;
			}
			else if (ms.numbuffers == 3)
			{
				if (next_sbuf == ms.screen)
					next_sbuf = ms.screenb;
				else if (next_sbuf == ms.screenb)
					next_sbuf = ms.screenc;
				else
					next_sbuf = ms.screen;
			}
			rects = rects->pnext;
		}
		DoubleBuffer (&ms);
	}
}

/**********************************************************************/
/*
================
D_BeginDirectRect
================
*/

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

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

/**********************************************************************/
void Sys_SendKeyEvents(void)
{
  UWORD code;
	ULONG class;
	WORD mousex, mousey;
  struct IntuiMessage *msg;
  static int xlate[0x68] = {
    '`', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', '0', '-', '=', '\\', 0, '0',
    'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
    'o', 'p', K_F11, K_F12, 0, '0', '2', '3',
    'a', 's', 'd', 'f', 'g', 'h', 'j', 'k',
    'l', ';', '\'', K_ENTER, 0, '4', '5', '6',
    K_SHIFT, 'z', 'x', 'c', 'v', 'b', 'n', 'm',
    ',', '.', '/', 0, '.', '7', '8', '9',
    K_SPACE, K_BACKSPACE, K_TAB, K_ENTER, K_ENTER, K_ESCAPE, K_F11,
    0, 0, 0, '-', 0, K_UPARROW, K_DOWNARROW, K_RIGHTARROW, K_LEFTARROW,
    K_F1, K_F2, K_F3, K_F4, K_F5, K_F6, K_F7, K_F8,
    K_F9, K_F10, '(', ')', '/', '*', '=', K_PAUSE,
    K_SHIFT, K_SHIFT, 0, K_CTRL, K_ALT, K_ALT, 0, K_CTRL
  };

	if (video_window !=NULL )
	{
		while ((msg = (struct IntuiMessage *)GetMsg (video_window->UserPort)) != NULL)
		{
      class = msg->Class;
      code = msg->Code;
      mousex = msg->MouseX;
      mousey = msg->MouseY;
      ReplyMsg ((struct Message *)msg);
      switch (class)
			{
        case IDCMP_RAWKEY:
          if ((code & 0x80) != 0)
					{
            code &= ~0x80;
            if (code < 0x68)
              Key_Event (xlate[code], false);
          }
					else 
					{
            if (code < 0x68)
              Key_Event (xlate[code], true);
          }
          break;
        case IDCMP_MOUSEBUTTONS:
					switch (code)
					{
  					case IECODE_LBUTTON:
    					Key_Event (K_MOUSE1, true);
							break;
    				case IECODE_LBUTTON + IECODE_UP_PREFIX:
      				Key_Event (K_MOUSE1, false);
      				break;
    				case IECODE_MBUTTON:
      				Key_Event (K_MOUSE2, true);
      				break;
    				case IECODE_MBUTTON + IECODE_UP_PREFIX:
      				Key_Event (K_MOUSE2, false);
      				break;
    				case IECODE_RBUTTON:
      				Key_Event (K_MOUSE3, true);
      				break;
    				case IECODE_RBUTTON + IECODE_UP_PREFIX:
      				Key_Event (K_MOUSE3, false);
      				break;
						default:
							break;
					}
        case IDCMP_MOUSEMOVE:									// Handled in in_amiga.c
          last_mouse[0] = mousex;
          last_mouse[1] = mousey;
					mousemove = true;
          break;
        default:
          break;
      }
    }
  }
}

/**********************************************************************/
