/**
 * TrueReality - Display/local/svgalib/svgalib.c
 * Copyright (C) 1998 Niki W. Waibel
 *
 * This program is free software; you can redistribute it and/
 * or modify it under the terms of the GNU General Public Li-
 * cence as published by the Free Software Foundation; either
 * version 2 of the Licence, or any later version.
 *
 * This program is distributed in the hope that it will be use-
 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public Licence for more details.
 *
 * You should have received a copy of the GNU General Public
 * Licence along with this program; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 * USA.
 *
 * Information about me (the author):
 *   Niki W. Waibel, Reichenau 20, 6890 Lustenau, Austria - EUROPE
 *   niki.waibel@gmx.net
**/





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

#include <signal.h>
#include <unistd.h>


#include "../../../config.h"
#include "../../../parser_extern.h"
#include "../../../n64/type_sizes.h"
#include "../../../n64/memory_extern.h"
#include "../../../dispatch.h"

#ifdef DEBUG
#  include "../../../debug.h"
#endif


#define VGA_MODE 	G640x480x64K   /* defined in vga.h */
#define DISPLAYNAME 	"SVGA"
#include <vga.h>
#include <vgagl.h>
#include <vgakeyboard.h>








static unsigned long      white, black;

// static char               title[] = "TrueReality from \"Niki W. Waibel\" and many others";
static WORD               width  =   0;   /* initial VI_WIDTH_REG (0x4400008) value */
static WORD               height =   0;   /* width*3/4 */
char *Buf;
char *MyFont;
//static GraphicsContext   *gc[2]; // stores info of whole screen
#undef DEPTH
static int                DEPTH;
static int RefreshVisualPage=0;

 static WORD               flags  = 0;

 static unsigned long      keystate = 0;
 static unsigned long      analog_stick = 0;


static  int     InitDisplay();
static  int     DestroyDisplay();
void		RefreshVisual();

static  void    PrintText(char *text, int x, int y);

#ifdef DEBUG_DISPLAY
        static  void PrintDisplayMode(WORD flags);
#endif

static  void    PutImage(int x, int y, int w, int h);
        
static  void    AllocDisplayMem();
static  void    FreeDisplayMem();










/*******************************************************************************
*                                                                              *
* int OpenVisual()                                                             *
* int CloseVisual()                                                            *
*                                                                              *
********************************************************************************
*                                                                              *
* These routines are used by the emulator to open and to close a visual.       *
*                                                                              *
* Both return a negative int on error and zero on success.                     *
*                                                                              *
*******************************************************************************/



int OpenVisual()
{
        if(InitDisplay() < 0)
                return(-1);

        prefs.display = LOCAL_DISPLAY;

        return(0);

} /* int OpenVisual() */





int CloseVisual()
{
        DestroyDisplay();

        prefs.display = NO_DISPLAY;

        return(0);

} /* int CloseVisual() */










/*******************************************************************************
*                                                                              *
* static int InitDisplay()                                                     *
*                                                                              *
********************************************************************************
*                                                                              *
* This routine is used to initialize the visual.                               *
* It should print serious information of the current status.                   *
* It must NOT be called by the emulator. It's called from 'int OpenVisual()'.  *
* The name is not standard but it should be used!                              *
*                                                                              *
* The routine returns a negative int on error and zero on success.             *
*                                                                              *
*******************************************************************************/





static int InitDisplay()
{
        char palvec[192];
        int i;
        printf("Initializing svga display: (most stuff by \"Guenther Sohler\")\n");
   


        printf("Opening display ...\n"); fflush(stdout);

        puts("trying svga\n");fflush(stdout);
        if (!vga_hasmode(VGA_MODE))
        {
         puts("required mode not available\n");
         return -1;
        }
        vga_setmode(VGA_MODE);
        if (!gl_setcontextvga(VGA_MODE) == 0)
        {
         puts("gl_setcontextvga failed!\n");
         vga_setmode(TEXT);
         return -1;
        }
        DEPTH=-1;
        if (vga_getcolors() == 256)
        {
         DEPTH=8;
         white=63;
         black=0;
        } 
        if (vga_getcolors() == 65536)
        {
         DEPTH=16;
         white=0xffff;
         black=0;
        }
        if (DEPTH == -1)
        {
         vga_setmode(TEXT);
         puts("required depth not supported!\n");
         return -1;
        } 
        Buf=(char *)malloc(BYTESPERPIXEL*HEIGHT*WIDTH);      
        if (Buf == NULL)
        {
         printf("Too less memory!\n");
         return -1;
        }
        MyFont = (char *) malloc(8*8*256*BYTESPERPIXEL*sizeof(char));
        if (MyFont == NULL)
        {
         printf("Too less memory!\n");
         return -1;
        }
        gl_expandfont(8,8,white,gl_font8x8,MyFont);
        gl_setfont(8,8,MyFont);
        gl_setfontcolors(black,white);
        if (DEPTH == 8)
        {
         for (i=0;i<64;i++)
         {
          palvec[3*i+2]=((i&0x03) >> 0)*21;
          palvec[3*i+1]=((i&0x0C) >> 2)*21;
          palvec[3*i+0]=((i&0x30) >> 4)*21;
         }
         gl_setpalettecolors(0,64,palvec);
        }
        gl_clearscreen(0);        
//        keyboard_init();
        RefreshVisual();

        return(0);
   
} /* static int InitDisplay() */










/*******************************************************************************
*                                                                              *
* static int DestroyDisplay()                                                  *
*                                                                              *
********************************************************************************
*                                                                              *
* This routine is used to destroy the visual.                                  *
* It should print serious information of the current status.                   *
* It must NOT be called by the emulator. It's called from 'int CloseVisual()'. *
* The name is not standard but it should be used!                              *
*                                                                              *
* The routine returns a negative int on error and zero on success.             *
*                                                                              *
*******************************************************************************/





static int DestroyDisplay()
{
        free(Buf);
        free(MyFont);
        vga_setmode(TEXT);
//        keyboard_clearstate();
//        keyboard_close();
        printf(" OK\n");

        printf("-------------------------------------------------------------------------------\n"); fflush(stdout);

        return(0);

} /* static int DestroyDisplay() */






static void PrintText(char *text, int x, int y)
{
   gl_write(x,y,text);
} /* static void PrintText(char *text, int x, int y) */





#ifdef DEBUG_DISPLAY

static void PrintDisplayMode(WORD flags)
{
        printf("Displaymode:\n");
   

   
        switch(flags & 0x0003)
        {
            case 0:
                printf("   blank (no data, no sync)\n");
                break;

            case 1:
                printf("   reserved\n");
                break;

            case 2:
                printf("   5/5/5/3 (\"16\" bit)\n");
                break;
         
            case 3:
                printf("   8/8/8/8 (32 bit)\n");
                break;
         
        } /* switch(flags & 0x0003) */
   

   
        if(flags & 0x0004)
                printf("   gamma_dither_enable\n");
        if(flags & 0x0008)
                printf("   gamma_enable\n");
        if(flags & 0x0010)
                printf("   divot_enable\n");
        if(flags & 0x0040)
                printf("   serrate\n");
   

   
        printf("   anti-alias (aa) mode\n");
   
        switch((flags & 0x0300) >> 8)
        {
            case 0:
                printf("      aa & resamp (always fetch extra lines)\n");
                break;

            case 1:
                printf("      aa & resamp (fetch extra lines if needed)\n");
                break;

            case 2:
                printf("      resamp only (treat as all fully covered)\n");
                break;
         
            case 3:
                printf("      neither (replicate pixels, no interpolate)\n");
                break;
         
        } /* switch((flags & 0x0300) >> 8) */
   
} /* static void PrintDisplayMode(WORD flags) */

#endif





void RefreshVisual()
{
 WORD    new_width;
 int     i, j, k, l;
 BYTE    *Imsrc1, *Imdst1, c;
 HWORD   *Imsrc2, *Imdst2, ui;
 WORD    *Imsrc4, *Imdst4, ul;
 flags = ((WORD *)mem.vi_reg)[0];
// keyboard_update();
// if (keyboard_keypressed(SCANCODE_1))
// {
//  vga_setmode(TEXT);
//  keyboard_clearstate();
//  keyboard_close();
//  exit(1); // this is very unnicely, but it might get you out without turning off your computer
// }
// if (keyboard_keypressed(SCANCODE_ESCAPE))
// {
//  dispatch(SHUTDOWN,0);
// }

#ifdef DEBUG_DISPLAY
        if(prefs.debug & DBG_DISP_REFRESH)
                puts("DBG_DISP_REFRESH: refresh");
        if(prefs.debug & DBG_DISP_MODE)
                PrintDisplayMode(flags);
#endif
   
   
        /* video width */
        /* if width in VI_WIDTH_REG (0x04400008) has changed set windows width */
   
        new_width = ((WORD *)mem.vi_reg)[2];
        if(new_width != width)
        {
                if(new_width < 320 || new_width > 640)
                        width = 640;
                else
                        width = new_width;

                height = (width * 3) >> 2;

        }


      
        /* display it! */

        if(new_width >= 320 && new_width <= 640)
        {
                if(flags & 0x0003)
                {
         
                        /* source video origin */
                        Imsrc4 = (WORD  *) (mem.rd_ram + ((WORD *)mem.vi_reg)[1]);
                        Imsrc2 = (HWORD *) Imsrc4;
                        Imsrc1 = (BYTE  *) Imsrc2;
         
                        /* destination video origin */
                        Imdst4 = (WORD  *) Buf;
                        Imdst2 = (HWORD *) Imdst4;
                        Imdst1 = (BYTE  *) Imdst2;

/* everything in the following switch comes from:  */
/* Guenther Sohler <guenther.sohler@newlogic.at>   */
/* HEY, GREAT WORK! (niki.waibel@gmx.net)          */
                        switch(DEPTH)
                        {
                            case 8:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;
                                    
                                    case 2:   /* local: 8bit   n64: 5/5/5/3 (16bit) */
                                        j=0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ui = Imsrc2[i*width+k];
                                                                c  = ((ui & 0x18) >> 3) | ((ui & 0x600) >> 7) | ((ui & 0xc000) >> 10);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst1[(j*BYTESPERPIXEL*WIDTH+l)^0x01] = c;
                                                        }
                                                }
                                        break;
                                    
                                    case 3:   /* local: 8bit   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                c  = ((ul & 0xc0) >> 6) | ((ul & 0xc000) >> 12) | (( ul & 0xc00000) >> 18);
                                                                for(; l*width<k*WIDTH;l++)
                                                                        Imdst1[j*BYTESPERPIXEL*WIDTH+l] = c;
                                                        }
                                                }
                                        break;

                                } /* switch(flags & 0x0003) */
                                break;
            
                            case 16:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 5/6/5 (16bit)   n64: 5/5/5/3 (16bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                for(; l*width<k*WIDTH; l++)
                                                                /* 
                                                                ** The following "for-body was modified by me
                                                                ** (Niki W. Waibel) to support correct colors
                                                                ** on big and little endians.
                                                                */
                                                                {
#ifdef ENDIAN_IS_LITTLE
                                                                        ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                        ui = Imsrc2[i*width+k];
#endif
                                                                        Imdst2[j*BYTESPERPIXEL*WIDTH/2+l] = ((ui >> 1) & 0x1f) + (ui & 0xffc0);
                                                                }
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 16bit   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                ui = ((ul & 0xF8) >> 3) | ((ul & 0xFC00) >> 5) | ((ul & 0xF80000) >> 8);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*BYTESPERPIXEL*WIDTH/2+l] = ui;
                                                        }
                                                }
                                                break;

                                } /* switch(flags & 0x0003) */
                                break;

                            default:
                                fprintf(stderr, "Display depth of %d bit is not supported! (yet ;) )\n", DEPTH);
                                
                        } /* switch(DEPTH) */
         
                        PutImage(0, 0, WIDTH, HEIGHT);
                }
                else
                {
                        PrintText("DISPLAY: blank (no data, no sync)", 20, 20);
                }
        }
        else
        {
                if(!(flags & 0x0003))
                        PrintText("DISPLAY: blank (no data, no sync)", 20, 20);
         
                PrintText("DISPLAY: wrong width (640 will be used!)", 20, 40);
        }
   
} /* void RefreshVisual() */



void GetVisualName(char *name, int *n)
{
   strncpy(name, DISPLAYNAME, (size_t) *n);
   
   *n = strlen(DISPLAYNAME);
   
} /* void GetVisualName() */





unsigned long CheckVisual()
{
 int stick;

 /*
 keyboard_update();
 if (keyboard_keypressed(SCANCODE_F8))
 {
  RefreshVisual();
 }
 if (keyboard_keypressed(SCANCODE_K))
 {
  stick = (analog_stick & 0xff00) >> 8;
  if(stick < 120)  analog_stick += 0x0800UL;
 }
 if (keyboard_keypressed(SCANCODE_J))
 {
  stick = (analog_stick & 0xff00) >> 8;
  if(stick > -120)  analog_stick -= 0x0800UL;
 }
 if (keyboard_keypressed(SCANCODE_L))
 {
  stick = analog_stick & 0x00ff;
  if(stick < 120) analog_stick += 0x0008UL;
 }
 if (keyboard_keypressed(SCANCODE_H))
 {
  stick = analog_stick & 0x00ff;
  if(stick > -120) analog_stick -= 0x0008UL;
 }
 keystate = keyboard_keypressed(SCANCODE_Q          )?0x1000UL:0 |
            keyboard_keypressed(SCANCODE_W          )?0x8000UL:0 |
            keyboard_keypressed(SCANCODE_E          )?0x4000UL:0 |
            keyboard_keypressed(SCANCODE_HOME       )?0x0008UL:0 |
            keyboard_keypressed(SCANCODE_END        )?0x0004UL:0 |
            keyboard_keypressed(SCANCODE_INSERT     )?0x0001UL:0 |
            keyboard_keypressed(SCANCODE_CURSORUP   )?0x0800UL:0 |
            keyboard_keypressed(SCANCODE_CURSORDOWN )?0x0400UL:0 |
            keyboard_keypressed(SCANCODE_CURSORLEFT )?0x0200UL:0 |
            keyboard_keypressed(SCANCODE_A          )?0x0010UL:0 |
            keyboard_keypressed(SCANCODE_S          )?0x0020UL:0 |
            keyboard_keypressed(SCANCODE_D          )?0x2000UL:0 |                                                               keyboard_keypressed(SCANCODE_CURSORRIGHT)?0x0100UL:0 ;
*/
 return(keystate + analog_stick);
} /* unsigned long CheckVisual() */


static void PutImage(int X, int Y, int W, int H)
{
  gl_putbox(X,Y,W,H,Buf);
} /* static void PutImage(int X, int Y, int W, int H) */





static void FreeDisplayMem()
{
} /* static void FreeDisplayMem() */





static void AllocDisplayMem()
{
} /* static void AllocDisplayMem() */

