/* vi: set sw=4 ts=4: */
/*
 * CCE - Console Chinese Environment -
 * Copyright (C) 1998-2003 Rui He (rhe@3eti.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 */

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

#include	"defs.h"
#include	"errors.h"
#include	"vc.h"
#include	"term.h"
#include	"vga.h"
#include	"fb.h"

#ifdef SUPPORT_GGILIB
#include	"cceggi.h"
#endif

#ifdef SUPPORT_SDLLIB
#include	"ccesdl.h"
#endif

#ifdef SUPPORT_FREETYPE_FONT
#include	"ftfont.h"
#endif

u_char	*gramMem;		/* dummy buffer for mmapping grahics memory */
int      gramFile = -1;      /* /dev/mem file descriptor */

u_int   writeAddr;      

bool    kanjiCursor;
u_char  cursorTop, cursorBtm;

u_char red8[] = {
    0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
    0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff
};

u_char green8[] = {
    0x00, 0x00, 0xaa, 0xaa, 0x00, 0x00, 0x55, 0xaa,
    0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};

u_char blue8[] = {
    0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
    0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

u_short red16[] = {
    0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,
    0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff
};

u_short green16[] = {
    0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa,
    0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff
};

u_short blue16[] = {
    0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa,
    0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff
};

void CheckSuidRoot(void)
{
#if !defined(__WINDOWS__) && !defined(__MSDOS__)
    if (geteuid() != 0)  /* MS Windows's geteuid() is not 0 ? FIXME: necessary for GGI/SDL? */
    {
        fatal("Can't get I/O permission. CCE in VGA mode needs suid-root.\n");
    }
#endif
}

VideoInfo *InitVideoInfo(char *display)
{
	VideoInfo *pInfo = NULL;

#if defined(SUPPORT_GGILIB)
	if (GGIAvail)
	{
		if (GGIInfo.init() == SUCCESS)
		{
			pInfo = &GGIInfo;
			return pInfo;
		}
	}
#endif

#if defined(SUPPORT_SDLLIB)
	if (SDLAvail && SDLInfo.init() == SUCCESS)
	{
		pInfo = &SDLInfo;
		return pInfo;
	}
#endif

#if defined(SUPPORT_FRAMEBUF)

	if (VgaFBInfo.init() == SUCCESS) /* try frame buffer first */
	{
		pInfo= &VgaFBInfo;
     	return pInfo;
	}

#endif  /* SUPPORT_FRAMEBUF */

#ifdef SUPPORT_VGA

	CheckSuidRoot();

#if defined(linux) && !defined(USE_LRMI)
    if (display && !strcasecmp(display,"S3"))   /* no-case compare */
        pInfo = &VgaS3Info;
    else
#endif 
		pInfo = &VgaInfo;  
		
   	if (fontwidth != 8 || fontheight != 16)        
		fatal("Only 16x16 font is supported in VGA mode\r\n");

	if (pInfo->init() != SUCCESS)
		return NULL;
#endif

    return pInfo; /* SUCCESS or NULL for failure */
}

void InitDispInfo(void)
{
	dispInfo.input = INPUT_HEIGHT;

    dispInfo.tx_avail = dispInfo.gxdim / fontwidth;
    if (dispInfo.linegap < 0 || dispInfo.linegap > 10)
         dispInfo.linegap = 2;
    dispInfo.glineChar = fontheight + dispInfo.linegap;
    dispInfo.ty_avail = (dispInfo.gydim - dispInfo.input) / dispInfo.glineChar;

    if (dispInfo.txmax <= 0 || dispInfo.txmax > dispInfo.tx_avail)
           dispInfo.txmax = dispInfo.tx_avail;
    if (dispInfo.tymax <= 0 || dispInfo.tymax > dispInfo.ty_avail)
           dispInfo.tymax = dispInfo.ty_avail;

    dispInfo.gsize = dispInfo.glineByte * dispInfo.gydim;
    dispInfo.tlineByte = dispInfo.glineChar * dispInfo.glineByte;
    dispInfo.tsize = dispInfo.tlineByte * dispInfo.tymax;

#ifdef DEBUG
	fprintf(stderr, "gLineByte=%d gSize=%d tLineByte=%d tsize=%d\r\n", dispInfo.glineByte, dispInfo.gsize, dispInfo.tlineByte, dispInfo.tsize);
#endif

	dispInfo.show_terminal = ( dispInfo.tx_avail > 99 ? 1 : 0);  
}

static void CCEOpDrawBitmap(int xpos, int ypos, u_char *bits, int fg, int bg, int width, int height)
{
	if (!bits) return; /* IMPORTANT: some fonts are not complete! */

	switch(dispInfo.type)
	{
#ifdef SUPPORT_VGA
	case TYPE_VGA:
		writeAddr = ypos * dispInfo.glineByte + xpos * dispInfo.bpp / 8;  
		VgaOpDraw(bits, fg, bg, width, height);
		break;
#endif
#ifdef SUPPORT_FRAMEBUF
	case TYPE_FB_VGAPLANES:
		writeAddr = ypos * dispInfo.glineByte + xpos * dispInfo.bpp / 8;  
		VgaOpDraw(bits, fg, bg, width, height);
		break;
	case TYPE_FB_LINEAR:
		writeAddr = ypos * dispInfo.glineByte + xpos * dispInfo.bpp / 8;  
		FBOpDraw(bits, fg, bg, width, height);
		break;
#endif
#ifdef SUPPORT_GGILIB
	case TYPE_GGI:
		GGIOpDraw(xpos, ypos, bits, fg, bg, width, height);
		break;
#endif
#ifdef SUPPORT_SDLLIB
	case TYPE_SDL:
		SDLOpDraw(xpos, ypos, bits, fg, bg, width, height);
		break;
#endif
	}
}

static void CCEOpDrawChar(int xpos, int ypos, int encoding, int ch1, int ch2, 
		int fg, int bg, int width, int height)
{
	u_char *code = NULL;
	
	if (encoding == CODE_SJIS)  /* we assume that ch1/ch2 has been converted to EUC-JP */
		encoding = CODE_EUC;

#ifdef SUPPORT_FREETYPE_FONT

	if (UseTTFont)
	{
		FT_GlyphSlot glyph;
		
		int err = FreetypeGetGlyph(encoding, ch1, ch2, &glyph); /* 0 for success */

		switch(dispInfo.type)
		{		
#ifdef SUPPORT_VGA
		case TYPE_VGA:
			fatal("TrueType font is not supported under VGA mode!\n");
			break;
#endif
#ifdef SUPPORT_FRAMEBUF
		case TYPE_FB_VGAPLANES:

			fatal("TrueType font is not supported in 16 colors mode!\n");
			break;
		case TYPE_FB_LINEAR:
			FBDrawFreeTypeChar(xpos, ypos, glyph, fg, bg, width, height);
			break;
#endif
#ifdef SUPPORT_GGILIB
		case TYPE_GGI:
			GGIDrawFreeTypeChar(xpos, ypos, glyph, fg, bg, width, height);
			break;
#endif
#ifdef SUPPORT_SDLLIB
		case TYPE_SDL:
			SDLDrawFreeTypeChar(xpos, ypos, glyph, fg, bg, width, height);
			break;	
#endif
		}
	}
	else 
#endif

	{  /* Bitmap fonts */
		FontRegs *reg;

		if (ch2 == 0)
			reg = &fSRegs[0]; /* Coding2FontRegSB[encoding]]; */
		else
			reg = &fDRegs[Coding2FontRegDB[encoding]];

#ifdef SUPPORT_X11FONT
    	if (reg->stat == FR_X11FONT)  /* bdf or pcf font */
    	{
			if (ch2 == 0) /* single byte */
				code = GetX11BitmapFontBits(reg->bitmap, ch1);
			else
				code = GetX11BitmapFontBits(reg->bitmap, (ch1 << 8) | ch2);
    	}
    	else /* mmaped */
#endif
    	{
			if (ch2 == 0)
			{
        		code = reg->bitmap + ch1 * fontbytes;
			}
			else
			{
        		u_int fnt = reg->addr(ch1, ch2);
        		if (fnt < reg->size) code = reg->bitmap + fnt;
			}
    	}

		CCEOpDrawBitmap(xpos, ypos, code, fg, bg, width, height);
	}
}

void CCEWput(int encoding, int x, int y, u_char ch1, u_char ch2, int fg, int bg)
{
	CCEOpDrawChar(x * fontwidth, y * dispInfo.glineChar, /*dbFReg*/encoding, ch1, ch2, 
		fg, bg, fontwidth*2, fontheight);
}

void CCESput(int x, int y, u_char ch, int fg, int bg)
{
	CCEOpDrawChar(x * fontwidth, y * dispInfo.glineChar, 0/*sbFReg*/, ch, 0,
		fg, bg, fontwidth, fontheight);
}

void CCEInputSput(int x, u_char ch, int fg, int bg)
{

	x %= dispInfo.tx_avail; /* necessary ? */

	CCEOpDrawChar(x * fontwidth, INPUT_YPOS, /*sbFReg*/0, ch, 0, fg, bg, fontwidth, fontheight);
}

void CCEInputWput(int encoding, int x, u_char ch1, u_char ch2, int fg, int bg)
{

	x %= dispInfo.tx_avail;  /* necessary ? */

	if (encoding == CODE_SJIS)
		ShiftJIS2JIS(ch1, ch2, &ch1, &ch2);

	CCEOpDrawChar(x * fontwidth, INPUT_YPOS, /*dbFReg*/encoding, ch1, ch2,
		fg, bg, fontwidth*2, fontheight);
}

void CCEInputDrawIcon(int x, u_char *bitmap, int width, int height, int fg, int bg)
{
	int xpos, ypos;

	xpos = (x % dispInfo.tx_avail) * fontwidth; 
	if (width < 2*fontwidth) xpos += (2*fontwidth - width)/2;
	ypos = INPUT_YPOS;
	if (height < fontheight) ypos += (fontheight - height)/2;

	CCEOpDrawBitmap(xpos, ypos, bitmap, fg, bg, width, height);
}

void CCEClear(int action, int color)
{
	int y, height;

	switch(action)
	{
	case CLEAR_DISPLAY:
		y = 0;
		height = dispInfo.gydim - dispInfo.input;
		break;
	case CLEAR_INPUT:
		y = dispInfo.gydim - dispInfo.input;
		height = dispInfo.input;
		break;
	default:  /* Unknown action */
		return;
	}

    switch(dispInfo.type)
    {
#if defined(SUPPORT_VGA)
	case TYPE_VGA:
		VgaOpClear(y, height, color);
        break;
#endif
#ifdef SUPPORT_FRAMEBUF
    case TYPE_FB_VGAPLANES:
        VgaOpClear(y, height, color);
        break;
    case TYPE_FB_LINEAR:
        FBOpClear(y, height, color);
        break;
#endif
#ifdef SUPPORT_GGILIB
	case TYPE_GGI:
		GGIOpClear(y, height, color);
		break;
#endif
#ifdef SUPPORT_SDLLIB	
	case TYPE_SDL:
		SDLOpClear(y, height, color);
		break;
#endif
    }
}

void CCECursor(CursorInfo *ci)
{
	int xpos, ypos;

	xpos = ci->x * fontwidth;
	ypos = ci->y * dispInfo.glineChar + cursorTop;

	switch(dispInfo.type)
    {
#ifdef SUPPORT_VGA
	case TYPE_VGA:
		VgaOpCursor(ci, xpos, ypos);
		break;
#endif
#ifdef SUPPORT_FRAMEBUF
    case TYPE_FB_VGAPLANES:
		VgaOpCursor(ci, xpos, ypos);
        break;
    case TYPE_FB_LINEAR:
        FBOpCursor(ci, xpos, ypos);
        break;
#endif
#ifdef SUPPORT_GGILIB
	case TYPE_GGI:
		GGIOpCursor(ci, xpos, ypos);
		break;
#endif
#ifdef SUPPORT_SDLLIB	
	case TYPE_SDL:
		SDLOpCursor(ci, xpos, ypos);
		break;
#endif
    }
}

#if defined(__FreeBSD__)
int fbwidth, fbheight, fbdepth;

int ConfigFreeBSDFrameBuffer(const char *confstr)
{

    sscanf(confstr, "%d %d %d", &fbwidth, &fbheight, &fbdepth);

	if (fbwidth <= 0 || fbwidth > 2048 || fbheight <= 0 || fbheight > 2048 ||
		fbdepth <= 0 || fbdepth > 32)  /* 2048 big enough for reasonable value? */
		fbwidth = fbheight = fbdepth = 0;
		
    return SUCCESS;
}

int GetOrigFreeBSDVideoMode(void)
{
	int mode;

	if (ioctl(0, FBIO_GETMODE, &mode) == -1)
		mode = M_VGA12; /* VGA12 is 640x480x16 colors VGA11 is 2 colors 640x480 */

	return VIDMODE_TO_IOCTL(mode);
}

int GetFreeBSDVideoMode(int width, int height, int depth, video_info_t *vmode)
{
	if (width == 0 || height == 0 || depth == 0 || vmode == NULL)
	{
		return -1;  

	}

	if (width >= 1024 && depth <= 4) depth = 8;
	
	memset(vmode, 0, sizeof(*vmode));
    vmode->vi_width = width;
    vmode->vi_height = height;
    vmode->vi_depth = depth;
    if (ioctl(0, FBIO_FINDMODE, vmode) == -1)
		return -1;

	if (vmode->vi_mode == M_VGA11 && vmode->vi_depth ==4)
		vmode->vi_mode = M_VGA12;

	return VIDMODE_TO_IOCTL(vmode->vi_mode);
}

int GetBestFreeBSDConsVidMode(video_info_t *vmode)
{
	int mode;
	
	mode = GetFreeBSDVideoMode(800, 600, 4, vmode);
	if (mode == -1) /* error */
		mode = GetFreeBSDVideoMode(640, 480, 4, vmode);

	return mode;
}
#endif  /* defined(__FreeBSD__) */

