// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: i_video_ggi.c 817 2011-03-13 01:52:15Z wesleyjohnson $
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
//
// 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.
//
//
// $Log: i_video_ggi.c,v $
// Revision 1.7  2004/05/16 19:11:53  hurdler
// that should fix issues some people were having in 1280x1024 mode (and now support up to 1600x1200)
//
// Revision 1.6  2001/04/27 13:32:14  bpereira
// no message
//
// Revision 1.5  2001/01/25 22:15:45  bpereira
// added heretic support
//
// Revision 1.4  2000/11/02 19:49:40  bpereira
// no message
//
// Revision 1.3  2000/03/06 15:19:58  hurdler
// Add Bell Kin's changes
//
// Revision 1.2  2000/02/27 00:42:12  hurdler
// fix CR+LF problem
//
// Revision 1.1.1.1  2000/02/22 20:32:33  hurdler
// Initial import into CVS (v1.29 pr3)
//
//
// DESCRIPTION:
//      Inspired from l_video.ggi.c by Colin Phipps (cph@lxdoom.linuxgames.com)
//      GGI target code
//
//-----------------------------------------------------------------------------


// checked mouse 19990314 by Kin
#define CHECKEDMOUSE

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

#include <ggi/ggi.h>

// it must be here 19990117 by Kin
#include "doomdef.h"
#ifdef USEASM
// added 19990125 by Kin
#include "../vid_copy.h"
#endif

#include "doomstat.h"
#include "i_system.h"
#include "v_video.h"
#include "m_argv.h"
#include "m_menu.h"
#include "d_main.h"
#include "s_sound.h"
// added for 1.27 19990220 by Kin
#include "g_input.h"
#include "st_stuff.h"
#include "g_game.h"
#include "i_video.h"

boolean showkey=0; // force to no 19990118 by Kin
int vid_modenum = 0; // index for vid mode list 19990119 by Kin
// added for 1.27 19990220 by Kin
rendermode_t    rendermode=render_soft;
byte graphics_started = 0;
boolean highcolor = false;

boolean expand_buffer = false;
static ggi_visual_t screen;
static const ggi_pixelformat* pixelformat;
static int multiply = 1;
//static byte* out_buffer;

static int ggi_screenwidth, ggi_screenheight;

// Mask of events that we are interested in
static const ggi_event_mask ev_mask = \
emKeyPress | emKeyRelease | emPtrRelative | emPtrButtonPress | \
emPtrButtonRelease;

// Unused vars to preserve in config file
int leds_always_off;
int use_vsync;

////////////////////////////////////////////////////////////////
// Input handling utility functions

// Null keyboard translation to satisfy m_misc.c
int I_DoomCode2ScanCode(int c)
{
  return c;
}

int I_ScanCode2DoomCode(int c)
{
  return c;
}

//
// I_GIITranslateKey
//
// Translate keys from LibGII
// Adapted from Linux Heretic v0.9.2

int I_GIITranslateKey(const ggi_key_event* key)
{
  int label = key->label, sym = key->sym;
  int rc=0;
  switch(label) {
  case GIIK_CtrlL:  case GIIK_CtrlR:  rc=KEY_CTRL;      break;
  case GIIK_ShiftL: case GIIK_ShiftR: rc=KEY_SHIFT;     break;
  case GIIK_MetaL:  case GIIK_MetaR:
  case GIIK_AltL:   case GIIK_AltR:   rc=KEY_ALT;       break;
  case GIIUC_BackSpace:   rc = KEY_BACKSPACE;     break;
  case GIIUC_Escape:      rc = KEY_ESCAPE;        break;
  case GIIK_Delete:       rc = KEY_DEL;        break;
  case GIIK_Insert:       rc = KEY_INS;        break;
  case GIIK_PageUp:       rc = KEY_PGUP;        break;
  case GIIK_PageDown:     rc = KEY_PGDN;      break;
  case GIIK_Home:         rc = KEY_HOME;          break;
  case GIIK_End:  rc = KEY_END;           break;
  case GIIUC_Tab:                     rc = KEY_TAB;     break;
  case GIIK_Up:                       rc = KEY_UPARROW; break;
  case GIIK_Down:                     rc = KEY_DOWNARROW;       break;
  case GIIK_Left:                     rc = KEY_LEFTARROW;       break;
  case GIIK_Right:                    rc = KEY_RIGHTARROW;      break;
  case GIIK_Enter:                    rc = KEY_ENTER;           break;
  case GIIK_F1: rc = KEY_F1;            break;
  case GIIK_F2: rc = KEY_F2;            break;
  case GIIK_F3: rc = KEY_F3;            break;
  case GIIK_F4: rc = KEY_F4;            break;
  case GIIK_F5: rc = KEY_F5;            break;
  case GIIK_F6: rc = KEY_F6;            break;
  case GIIK_F7: rc = KEY_F7;            break;
  case GIIK_F8: rc = KEY_F8;            break;
  case GIIK_F9: rc = KEY_F9;            break;
  case GIIK_F10:        rc = KEY_F10;           break;
  case GIIK_F11:        rc = KEY_F11;           break;
  case GIIK_F12:        rc = KEY_F12;           break;
  case GIIK_Pause:rc = KEY_PAUSE;               break;
  case GIIK_PSlash:rc = KEY_KPADSLASH;          break;
  case GIIK_P0:rc = KEY_KEYPAD0;break;
  case GIIK_P1:rc = KEY_KEYPAD1;break;
  case GIIK_P2:rc = KEY_KEYPAD2;break;
  case GIIK_P3:rc = KEY_KEYPAD3;break;
  case GIIK_P4:rc = KEY_KEYPAD4;break;
  case GIIK_P5:rc = KEY_KEYPAD5;break;
  case GIIK_P6:rc = KEY_KEYPAD6;break;
  case GIIK_P7:rc = KEY_KEYPAD7;break;
  case GIIK_P8:rc = KEY_KEYPAD8;break;
  case GIIK_P9:rc = KEY_KEYPAD9;break;
  case GIIK_PPlus:rc = KEY_PLUSPAD;break;
  case GIIK_PMinus:rc = KEY_MINUSPAD;break;
  /* Decimal???? why not Dot??? */
  case GIIK_PDecimal:rc = KEY_KPADDEL;break;
  case GIIK_CapsLock:rc = KEY_CAPSLOCK;break;
  case GIIK_ScrollLock:rc = KEY_SCROLLLOCK;break;
  case GIIK_NumLock:rc = KEY_NUMLOCK;break;
        default:
          if ((label > '0' && label < '9') ||
              label == '.' ||
              label == ',') {
              /* Must use label here, or it won't work when shift
                 is down */
            rc = label;
          } else if (sym < 256) {
              /* ASCII key - we want those */
            rc = sym;
              /* We want lowercase */
            if (rc >='A' && rc <='Z') rc -= ('A' - 'a');
            switch (sym) {
              /* Some special cases */
            case '+': rc = KEY_EQUALS;  break;
            case '-': rc = KEY_MINUS;
            default:                    break;
            }
          }
  }
  return rc;
}

unsigned short I_GIITranslateButtons(const ggi_pbutton_event* ev)
{
  return ev->button;
}

////////////////////////////////////////////////////////////////
// API

//
// I_StartFrame
//
void I_StartFrame (void)
{
  // If we reuse the blitting buffer in the next rendering,
  // make sure it is reusable now
  if (!expand_buffer)
    ggiFlush(screen);
}

void I_OsPolling(void) {
  I_GetEvent();
}

#ifdef CHECKEDMOUSE
int btmask = 0;
#endif

#ifdef LJOYSTICK
extern void I_GetJoyEvent();
#endif
#ifdef LMOUSE2
extern void I_GetMouse2Event();
#endif

void I_GetEvent(void)
{
  struct timeval nowait = { 0, 0 };
#ifdef LMOUSE2
  I_GetMouse2Event();
#endif
#ifdef LJOYSTICK
  I_GetJoyEvent();
#endif
  while (ggiEventPoll(screen, ev_mask, &nowait)!=evNothing) {
    // There is a desirable event
    ggi_event ggi_ev;
    event_t doom_ev;
    int m_x = 0, m_y = 0; // Mouse motion
    unsigned short buttons; // Buttons
    int i,j;
    // GII will return modified button "number"

    ggiEventRead(screen, &ggi_ev, ev_mask);

    switch(ggi_ev.any.type) {
    case evKeyPress:
      doom_ev.type = ev_keydown;
      if ((doom_ev.data1= I_GIITranslateKey(&ggi_ev.key)) >0 )
        D_PostEvent(&doom_ev);
      //fprintf(stderr,"p:%4x\n",doom_ev.data1);
      break;
    case evKeyRelease:
      doom_ev.type = ev_keyup;
      if ((doom_ev.data1= I_GIITranslateKey(&ggi_ev.key)) > 0 )
        D_PostEvent(&doom_ev);
      //fprintf(stderr,"r:%4x\n",doom_ev.data1);
      break;
    case evPtrRelative:
      m_x += ggi_ev.pmove.x;
      m_y += ggi_ev.pmove.y;
      break;
    case evPtrButtonPress:
      buttons = I_GIITranslateButtons(&ggi_ev.pbutton);
#ifdef CHECKEDMOUSE
      if(btmask&(1<<buttons)) {
         break;
      } else btmask |= (1<<buttons);
#endif
      doom_ev.type = ev_keydown;
      // translate button2 in ggi to MOUSE3 19990225 by Kin
      doom_ev.data1=KEY_MOUSE1+(buttons^(buttons>>1))-1;
      D_PostEvent(&doom_ev);
      break;
    case evPtrButtonRelease:
      buttons = I_GIITranslateButtons(&ggi_ev.pbutton);
#ifdef CHECKEDMOUSE
      if(!(btmask&(1<<buttons))) {
         break;
      } else btmask ^= (1<<buttons);
#endif
      doom_ev.type = ev_keyup;
      doom_ev.data1=KEY_MOUSE1+(buttons^(buttons>>1))-1;
      D_PostEvent(&doom_ev);
      break;
    default:
      //I_Error("I_OsPolling: Bad GGI event type");
    }

    if (m_x||m_y) {
      doom_ev.type = ev_mouse;
      // 4x mouse like xdoom 19990227 by Kin
      doom_ev.data2 = m_x << 2;
      doom_ev.data3 = -(m_y << 2);
      D_PostEvent(&doom_ev);
    }
  }
}

//
// I_UpdateNoBlit
//
void I_UpdateNoBlit (void)
{
  // Finish up any output
  ggiFlush(screen);
}

//
// I_FinishUpdate
//
void I_FinishUpdate (void)
{

  int           i;

  // draws little dots on the bottom of the screen
  if (devparm) {
    static int  lasttic;
    int         tics;

    i = I_GetTime();
    tics = i - lasttic;
    lasttic = i;
    if (tics > 20) tics = 20;

    for (i=0 ; i<tics*2 ; i+=2)
      screens[0][ (vid.height-1)*vid.width + i] = 0xff;
    for ( ; i<20*2 ; i+=2)
      screens[0][ (vid.height-1)*vid.width + i] = 0x0;
  }

  // scales the screen size before blitting it
  // disabled for now 19990221 by Kin
  //  if (expand_buffer)
  //  (*I_ExpandImage)(out_buffer, screens[0]);

  // Blit it
  ggiPutBox(screen, 0, 0, ggi_screenwidth,
            ggi_screenheight, vid.buffer);

}

//
// I_ReadScreen
//
void I_ReadScreen (byte* scr)
{
  memcpy(scr, vid.buffer, vid.width*vid.height*vid.bpp);
}

void I_SetPalette(RGBA_t* palette)
{

  if (!highcolor) {
    ggi_color ggi_pal[256];
    ggi_color* p;
    int i;
    // PseudoColor mode, so set the palette

    for (i=0, p=ggi_pal; i<256; i++) {
      // Translate palette entry to 16 bits per component
      p->r = palette->s.red   << 8;
      p->g = palette->s.green << 8;
      p->b = palette->s.blue  << 8;

      p++; palette++;
    }

    ggiSetPalette(screen, 0, 256, ggi_pal);
    //  } else {
    // TrueColor mode, so rewrite conversion table

    //    I_SetPaletteTranslation(palette);
  }
}

void I_ShutdownGraphics(void)
{

  ggiRemoveFlags(screen, GGIFLAG_ASYNC);

  if (vid.buffer)
    free(vid.buffer);

  ggiExit();
}

#define MAX_GGIMODES 18

const ggi_coord temp_res[MAX_GGIMODES]= {
        {320,200},
        {320,240},
        {320,350}, /* EGA text */
        {360,400}, /* VGA text */
        {400,300},
        {480,300},
        {512,384},
        {640,200}, /* EGA */
        {640,350}, /* EGA */
        {640,400},
        {640,480},
        {720,350}, /* MDA text */
        {720,400}, /* VGA text */
        {800,600},
        {1024,768},
        {1152,864},
        {1280,960},
        {1280,1024},
        {1600,1200}
};

ggi_coord real_res[MAX_GGIMODES+1];
char vidname[MAX_GGIMODES+1][10];
ggi_mode vidmodes[MAX_GGIMODES+1];
int rescount;

// dummy for test 19990221 by Kin
int   VID_NumModes(void) {
  return rescount;
}

char  *VID_GetModeName(int modenum) {
  return vidname[modenum];
}

int VID_GetModeForSize( int w, int h) {

  int i;

  for (i=0; i<rescount;i++) {
    if(real_res[i].x==w) {
      if(real_res[i].y==h) {
        return i;
      }
    }
  }

  for (i=0; i<rescount;i++) {
    if(real_res[i].x>=w) {
      if(real_res[i].y>=h) {
        break;
      }
    }
  }

  if(i<rescount) {
    return i;
  }

  return 0;

}

void I_StartupGraphics(void)
{
  int i;

  fprintf(stderr, "I_StartupGraphics:");

  if (ggiInit())
    I_Error("Failed to initialise GGI\n");

  if (!(screen = ggiOpen(NULL))) // Open default visual
    I_Error("Failed to get default visual\n");

  { // Check for screen enlargement
    char str[3] = { '-', 0, 0 };
    int n;

    for (n=1; n<4; n++) {
      str[1] = n + '0';
      if (M_CheckParm(str)) multiply = n;
    }
  }

  highcolor = M_CheckParm("-highcolor");

  for(i=0,rescount=0;i<MAX_GGIMODES;i++) {
    if(!ggiCheckSimpleMode(screen,temp_res[i].x,temp_res[i].y,2,
                           (highcolor?GT_15BIT:GT_8BIT),&vidmodes[rescount]))
      {
        memcpy(&real_res[rescount],&temp_res[i],sizeof(ggi_coord));
        sprintf(vidname[rescount],"%4dx%4d",temp_res[i].x,temp_res[i].y);
        fprintf(stderr,"mode %s\n",vidname[rescount]);
        rescount++;
      } else {
        if(GT_DEPTH(vidmodes[rescount].graphtype)==(highcolor?15:8)) {
          //if((vidmodes[rescount].visible.x>temp_res[i-1].x &&
          //    vidmodes[rescount].visible.x<temp_res[i+1].x &&
          //    vidmodes[rescount].visible.y>=temp_res[i-1].y) ||
          //   (vidmodes[rescount].visible.y>temp_res[i-1].y &&
          //    vidmodes[rescount].visible.y<temp_res[i+1].y &&
          //    vidmodes[rescount].visible.x>=temp_res[i-1].x)) {
          if(vidmodes[rescount].visible.x==temp_res[i].x &&
             vidmodes[rescount].visible.y==temp_res[i].y) {
            real_res[rescount].x = vidmodes[rescount].visible.x;
            real_res[rescount].y = vidmodes[rescount].visible.y;
            sprintf(vidname[rescount],"%4dx%4d",
                    real_res[rescount].x,real_res[rescount].y);
            fprintf(stderr,"suggested mode %s\n",vidname[rescount]);
            rescount++;
          }
        }
      }
  }
  if(!rescount) {
    I_Error("No video modes available!");
    return;
  }
  vid.buffer = NULL;
  VID_SetMode(0);
  // Go asynchronous
  ggiAddFlags(screen, GGIFLAG_ASYNC);

  // Mask events
  ggiSetEventMask(screen, ev_mask);
  // added for 1.27 19990220 by Kin
  graphics_started = 1;

}

int VID_SetMode(int modenum) {

  if(vid.buffer) free(vid.buffer);
  //if(expand_buffer) {
  //  if(out_buffer) free(out_buffer);
  //}

  vid.bpp = (highcolor?2:1);
  ggi_screenwidth = vid.width = real_res[modenum].x;
  ggi_screenheight = vid.height = real_res[modenum].y;
  vid.rowbytes = vid.width*vid.bpp;
  vid.recalc = 1;

  if (ggiSetMode(screen,&vidmodes[modenum])) {
    I_Error("Failed to set mode");
    return 0;
  }

  vid.buffer = vid.direct =
    (unsigned char *) malloc (vid.width * vid.height * NUMSCREENS * vid.bpp);

  // Allocate enlarement buffer if needed
  //if (expand_buffer)
  //  out_buffer = malloc(ggi_screenheight*ggi_screenwidth*vid.bpp);
  //else
  //out_buffer = (byte*)screens[0];

  return 1;

}
