/**
 * TrueReality - output/display/msdos/msdos.c
 * Copyright (C) 1998, 1999 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
**/





/**
 *
 * This msdos port comes from:
 * "Boca Juniors (Diego Rozensztejn)" <boca_jrs@hotmail.com>
 *
 * DJGPP and Allegro are needed!
 *
**/





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

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

#include <allegro.h>


#include <config.h>
#include <parser_extern.h>
#include <type_sizes.h>
#include <memory_extern.h>
#include <dispatch.h>

#include "../display.h"
#include "msdos.h"


#ifdef DEBUG
        #include <debug.h>
#endif

#ifdef FPS
        #include <sys/time.h>
        #include <unistd.h>
#endif





static BITMAP           *img;
static PALETTE          colormap;          
static RGB_MAP          rgbtable;

static WORD             width  =   0;   /* initial VI_WIDTH_REG (0x4400008) value */
static WORD             height =   0;   /* width*3/4 */
static int              WIDTH  = 640;
static int              HEIGHT = 480;

static int              DEPTH;            
static WORD             flags  = 0;


static  int     InitDisplay();
static  int     DestroyDisplay();

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

#ifdef DEBUG
        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()
{      
        
        int i;


        
        printf("Initializing MSDOS/Allegro display: (by \"Diego\")\n");

        i = allegro_init();                                 
        if(i)
        {
                puts(" FAILED\n"); fflush(stdout);
                return(-1);
        }
        

        install_keyboard();
                             
        DEPTH = prefs.depth;
        set_color_depth(DEPTH);
        
        i = set_gfx_mode(GFX_AUTODETECT, WIDTH, HEIGHT, 0, 0);               
        if(i)
        {               
                printf(" FAILED\n"); fflush(stdout);
                return(-1);
        }
             

        if(DEPTH == 8)
        {
                for (i=0; i<256; i++)
                {
                    colormap[i].r = ((i>>5)&7) * 63/7;
                    colormap[i].g = ((i>>2)&7) * 63/7;
                    colormap[i].b = (i&3) * 63/3;
                };

                set_palette(colormap);                                             

                create_rgb_table(&rgbtable, colormap, NULL);
                rgb_map = &rgbtable;


        } /* if(DEPTH == 8) */


        /* display window */

        clear_to_color(screen, makecol(255,255,255)); /* Clear screen to white */

        img = create_bitmap(WIDTH,HEIGHT);      
        clear(img);

        text_mode(makecol(255,255,255)); /* White Background for text */

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

        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()
{
    
        destroy_bitmap(img);

        set_gfx_mode(GFX_TEXT,80,40,0,0);  /* Back to text mode */

        remove_keyboard();      

        allegro_exit();

        return(0);

} /* static int DestroyDisplay() */









static void PrintText(char *text, int x, int y)
{
        text_mode(-1);
        textout(screen,font,text,x+1,y+1,makecol(0,0,0));
        textout(screen,font,text,x,y,makecol(255,255,255));

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





#ifdef DEBUG

static void PrintDisplayMode(WORD flags)
{
   
} /* static void PrintDisplayMode(WORD flags) */

#endif





void RefreshVisual()
{      
        WORD    new_width;
        int     i, j, k, l;
        BYTE    *Imsrc1;
        HWORD   *Imsrc2, ui;
        WORD    *Imsrc4, ul;

#ifdef FPS
        static char             fps_text[40];
        int                     fps_update_sec = 1;   /* min update frequency in sec */
                                                      /* otherwise you might not read it properly */
        static long             fps_update = 0;   /* stores the next time for update */
        static int              fps_count = 0;   /* to make fps average */
        static float            fps_rate = 0.0;
        static struct timeval   fps_tv_old  = { 0, 0 };
        struct timeval          fps_tv;
#endif





        /* video flags */
   
        flags = ((WORD *)mem.vi_reg)[0];


        /* 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;

                i = set_gfx_mode(GFX_AUTODETECT, width, height, 0, 0);

                if(i)
                    dispatch(SHUTDOWN, NULL); 
        
                if(DEPTH == 8)
                    set_palette(colormap);                                             

                clear(screen);

        }


      
        /* display it! */

        if(new_width >= 320 && new_width <= 640)
        {
                if(flags & 0x0003)
                {
                        if(WIDTH != width || HEIGHT != height)
                        {
                                WIDTH  = width;
                                HEIGHT = height;

                                /* free all display related memory */
                                FreeDisplayMem();
         

                                /* allocate a new portion of display related memory */
                                AllocDisplayMem();

                        } /* if(WIDTH != win_attrib.width || HEIGHT != win_attib.height) */

         
                        /* source video origin */
                        Imsrc4 = (WORD  *) (mem.rd_ram + ((WORD *)mem.vi_reg)[1]);
                        Imsrc2 = (HWORD *) Imsrc4;
                        Imsrc1 = (BYTE  *) Imsrc2;
         

                        switch(flags & 0x0003)
                        {
                            case 0:
                            case 1:
                                break;

                            case 2:   /* 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)^0x01];
                                                        for(; l*width<k*WIDTH; l++)   
                                                            if(DEPTH == 8)
                                                                /* putpixel(img,l,j,((ui & 0x30) >> 4) | ((ui & 0x600) >> 6) | ((ui & 0xc000) >> 8)); */
                                                                img->line[j][l] = ((ui & 0x30) >> 4) | ((ui & 0x600) >> 6) | ((ui & 0xc000) >> 8);
                                                            else
                                                                putpixel(img,l,j,makecol((ui & 0xF800) >> 8, (ui & 0x7C00) >> 3, (ui & 0x3E) << 2));
                                                }

                                        }
                                break;

                            case 3:   /* 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];
                                                        for(; l*width<k*WIDTH; l++)
                                                            if(DEPTH == 8)
                                                                /* putpixel(img,l,j,((ul & 0xc0) >> 6) | ((ul & 0xe000) >> 11) | (( ul & 0xe00000) >> 16)); */
                                                                img->line[j][l] = ((ul & 0xc0) >> 6) | ((ul & 0xe000) >> 11) | (( ul & 0xe00000) >> 16);
                                                            else
                                                                putpixel(img,l,j,makecol((ul & 0xFF0000)>>16, (ul & 0xFF00)>>8, ul & 0xFF));
                                                }
                                        }
                                        break;

                        } /* switch(flags & 0x0003) */
                        
                        PutImage(0, 0, WIDTH, HEIGHT);
                }
                else
                {
                        PrintText("DISPLAY: blank (no data, no sync)", 20, 40);
                }
        }
        else
        {
                if(!(flags & 0x0003))
                        PrintText("DISPLAY: blank (no data, no sync)", 20, 40);
         
                PrintText("DISPLAY: wrong width (640 will be used!)", 20, 60);
        }



#ifdef FPS
        gettimeofday(&fps_tv, NULL);

        fps_rate = fps_count * fps_rate
                   + 1 / ( ((float)fps_tv.tv_sec     + (float)fps_tv.tv_usec     / 1000000)
                         - ((float)fps_tv_old.tv_sec + (float)fps_tv_old.tv_usec / 1000000) );

        fps_count++;

        fps_rate /= fps_count;

        if(fps_update <= fps_tv.tv_sec)
        {
                fps_update = fps_tv.tv_sec + fps_update_sec;
                fps_count  = 0;

                if(fps_rate > 99.9)
                        fps_rate = 99.99;

                sprintf(fps_text, "%05.2f", fps_rate);

                fps_rate = 0.0;
        }

        fps_tv_old.tv_sec = fps_tv.tv_sec;
        fps_tv_old.tv_usec = fps_tv.tv_usec;

        PrintText(fps_text, 01, 20);
#endif

} /* void RefreshVisual() */





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





void PutImage(int X, int Y, int W, int H)
{
    blit(img, screen, X, Y, X, Y, W, H);

} /* static void PutImage(int X, int Y, int W, int H) */





void FreeDisplayMem()
{
    destroy_bitmap(img);

} /* static void FreeDisplayMem() */





void AllocDisplayMem()
{                                           
    img = create_bitmap(WIDTH,HEIGHT);        
    clear(img);

} /* static void AllocDisplayMem() */

/* for RDP */

void SetVisualClipRect(int x, int y, int width, int height)
{}

void DrawVisualFillRect(int ulx, int uly, int lrx, int lry, int color)
{}
