/*******************************************************************************
+
+  LEDA 3.5
+
+  _vga.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/
#include <LEDA/impl/x_window.h>
#include <stdio.h>
#include "vga.h"

/*
extern void  init_graphics(int mode, int& width, int& height, int& depth);
extern void  define_color(int i, int r, int g, int b);
extern void  draw_scan_segment(unsigned char* p, int x, int y, int len);
extern void  draw_pixel(int x, int y, unsigned char c);
extern int   check_next_event(int& val, int& x, int& y, int block);
*/

#if defined(unix)

#if defined(linux)
#include "_svgalib.c"
#else
#include "_x11.c"
#endif

#else

#if defined(__GNUC__)
#include "_dos_emx.c"
#elif defined(__WATCOMC__)
#include "_dos_wcc.c"
#else
#include "_dos_vga.c"
#endif

#endif





typedef unsigned char pixtype;


// RGB values    wi bl  re  gr  bl  ye  vi  or  cy  br  pi  gr  bl  g1  g2  g3
int _R_[16] = {255, 0,255,  0,  0,224,160,255,  0,192,255,  0,128,212,184, 96};
int _G_[16] = {255, 0,  0,192,  0,224,  0,160,192,112,  0,160,128,212,184, 96};
int _B_[16] = {255, 0,  0,  0,255,  0,208,  0,192, 56,255,128,192,212,184, 96};


static pixtype COLOR;
static int     MODE;

static int disp_width;
static int disp_height;
static int disp_depth;
static int disp_max_x;
static int disp_max_y;
static pixtype* disp_buffer;
static int buffering;



struct Event {
  int ev;
  int val;
  int x;
  int y;
  Event() { ev = -1; val = x = y = 0; }
};

static Event event_buf;
static Event last_event;


void vga_init(int mode)
{

  init_graphics(mode, disp_width, disp_height, disp_depth);

  if (mode == 1) // graphics mode
    { 
      for(int i=0; i<16; i++) 
        define_color(i,_R_[i]/4,_G_[i]/4,_B_[i]/4);

      disp_max_x = disp_width - 1;
      disp_max_y = disp_height - 1;
      disp_buffer = new pixtype[disp_width*disp_height];
     }
  else
     delete[] disp_buffer;
}



void vga_clear(int)           { }
int  vga_width()              { return disp_width;  }
int  vga_height()             { return disp_height; }
int  vga_depth()              { return disp_depth;  }
void vga_set_color(int col)   { COLOR = (pixtype)col; }
void vga_set_mode(int mode)   { MODE = mode; }
void vga_set_buffering(int b) { buffering = b; }


void vga_setpal(int index, int red, int green, int blue)
{ _R_[index] = red;
  _G_[index] = green;
  _B_[index] = blue;
  define_color(index,red/4,green/4,blue/4);
}

void vga_getpal(int index, int* red, int* green, int* blue)
{ *red   = _R_[index];
  *green = _G_[index];
  *blue  = _B_[index];
 }


void vga_flush(int x0, int y0, int x1, int y1, int always)
{ 
  if (buffering && !always) return;

  if (x1 < 0 || x0 > disp_max_x || y1 < 0 || y0 > disp_max_y) return;

  if (y0 < 0) y0 = 0;
  if (y1 > disp_max_y) y1 = disp_max_y;

  if (x0 == x1)
    { pixtype* p = disp_buffer + y0*disp_width + x0;
      for (int y = y0; y <= y1; y++)
      { draw_pixel(x0,y,*p);
        p += disp_width;
       }
     }
  else
    { x0 -= (x0%8); 
      x1 = 8*(x1+7)/8; 
    
      if (x0 < 0) x0 = 0;
      if (x1 > disp_max_x) x1 = disp_max_x;
    
      int len = x1 - x0 + 1;
    
      pixtype* p = disp_buffer + y0*disp_width + x0;
      for (int y = y0; y <= y1; y++)
      { draw_scan_segment(p,x0,y,len);
         p += disp_width;
       }
     }

  int mx = last_event.x;
  int my = last_event.y;

  if (mx <= x1 && mx+16 >= x0 && my <= y1 && my+14 >= y0) 
    vga_draw_pointer(mx,my,mx,my);
}



void vga_pixel(int x, int y, int flush)  
{ 
  // clip into display
  if (x < 0 || x > disp_max_x || y < 0 || y > disp_max_y) return;

  unsigned char* p = disp_buffer + x + y*disp_width;

  if (MODE == 3)
     *p ^= COLOR;
  else
     *p  = COLOR;

  if (flush) draw_pixel(x,y,*p);
}



void vga_hline(int x0,int x1, int y)      
{ 
  // clip into display
  if (y < 0 || y > disp_max_y || x1 < 0 || x0 > disp_max_x) return;
  if (x0 < 0) x0 = 0;
  if (x1 > disp_max_x) x1 = disp_max_x;

  pixtype* p = disp_buffer+(y*disp_width+x0);
  pixtype* stop =  p + (x1-x0+1);

  if (MODE == 3)
     while (p < stop) *p++ ^= COLOR;
  else
     while (p < stop) *p++  = COLOR;
 }


void vga_vline(int x,int y0, int y1)      
{ 
  // clip into display
  if (x < 0 || x > disp_max_x || y1 < 0 || y0 > disp_max_y) return;
  if (y0 < 0) y0 = 0;
  if (y1 > disp_max_y) y1 = disp_max_y;

  pixtype* p = disp_buffer+(y0*disp_width+x);

  int len = y1-y0+1;

  if (MODE == 3)
     while (len--) { *p ^= COLOR; p += disp_width; }
  else
     while (len--) { *p  = COLOR; p += disp_width; }

 }



void vga_box(int x0,int y0,int x1,int y1) 
{ for(int y=y0; y<=y1; y++) vga_hline(x0,x1,y); }



static void vga_bytes(int x, int y, unsigned char* lp, int len)
{ 
  if (len < 1) return;

  // clip into display
  int x1 = x + 8*len - 1;
  if (y < 0 || y > disp_max_y || x1 < 0 || x > disp_max_x) return;
  if (x < 0) { lp -= x/8; x = 0; }
  if (x1 > disp_max_x) x1 = disp_max_x;
  len = (x1 - x + 1)/8;

  pixtype* p = disp_buffer+(y*disp_width+x);

  unsigned char* stop = lp+len;

 if (MODE == 3)
      while (lp < stop)
      { unsigned char c = *lp++;
        if (c & 0x80) *p ^= COLOR; p++;
        if (c & 0x40) *p ^= COLOR; p++;
        if (c & 0x20) *p ^= COLOR; p++;
        if (c & 0x10) *p ^= COLOR; p++;
        if (c & 0x08) *p ^= COLOR; p++;
        if (c & 0x04) *p ^= COLOR; p++;
        if (c & 0x02) *p ^= COLOR; p++;
        if (c & 0x01) *p ^= COLOR; p++;
       }
  else
      while (lp < stop)
      { unsigned char c = *lp++;
        if (c & 0x80) *p = COLOR; p++;
        if (c & 0x40) *p = COLOR; p++;
        if (c & 0x20) *p = COLOR; p++;
        if (c & 0x10) *p = COLOR; p++;
        if (c & 0x08) *p = COLOR; p++;
        if (c & 0x04) *p = COLOR; p++;
        if (c & 0x02) *p = COLOR; p++;
        if (c & 0x01) *p = COLOR; p++;
       }
}


void vga_bitmap(int x, int y, unsigned char* pm, int width, int height)
{ for(int i=0; i<height; i++)
  { vga_bytes(x,y+i,pm,width);
    pm += width;
   }
 }


unsigned char* vga_getimage(int left, int top, int w, int h )
{ 
  if (left < 0 || left+w > disp_width ||
      top  < 0 || top+h  > disp_height) return 0;

  //unsigned char* buf = new unsigned char[w*h];
  unsigned char* buf = new unsigned char[(w+1)*(h+1)];
  int right = left + w - 1;
  int bottom = top + h - 1;
  unsigned char* p = buf;
  for(int y = top; y <= bottom; y++)
    for(int x = left; x <= right; x++)
      *p++ = disp_buffer[x + y*disp_width];
  return buf;
}

void  vga_putimage(int x, int y, int w, int h, unsigned char* p)
{ if (p == 0) return;
  for(int i=0; i<h; i++)
  {  for(int j=0; j<w; j++)
        disp_buffer[(x+j)+(y+i)*disp_width] = *p++;
    }
  vga_flush(x,y,x+w,y+h);
 }

void  vga_delimage(unsigned char* p) { if (p) delete[] p; }



void vga_copyimage(int x1, int y1, int w, int h, int x, int y)
{ unsigned char* p = vga_getimage(x1,y1,w,h);
  vga_putimage(x,y,w,h,p);
  vga_delimage(p);
 }



//------------------------------------------------------------------------------
// events
//------------------------------------------------------------------------------


void vga_back_event() 
{ event_buf.ev  = last_event.ev;  
  event_buf.val = last_event.val; 
  event_buf.x = last_event.x; 
  event_buf.y = last_event.y; 
}


int vga_next_event(int* val, int* x, int* y, int block)
{ 
  int e = no_event;
  *x = last_event.x;
  *y = last_event.y;

  if (event_buf.ev != -1)
  { e = event_buf.ev;
    *val = event_buf.val;
    *x   = event_buf.x;
    *y   = event_buf.y;
    event_buf.ev   = -1;
   }
  else
    e = check_next_event(*val,*x,*y,block);

  if (e != no_event)
  { last_event.ev  = e;
    last_event.val = *val;
    last_event.x   = *x;
    last_event.y   = *y;
   }

  return e;
}



//------------------------------------------------------------------------------
// mouse cursor
//------------------------------------------------------------------------------

static unsigned char c_map[4][28] = {

{0xc0,0x00, 0xf0,0x00, 0x7c,0x00, 0x7f,0x00, 0x3f,0xc0, 0x3f,0xc0, 0x1f,0x00, 
 0x1f,0x80, 0x0d,0xc0, 0x0c,0xe0, 0x00,0x70, 0x00,0x38, 0x00,0x1c, 0x00,0x0c},

{0x00,0x03, 0x00,0x0f, 0x00,0x3e, 0x00,0xfe, 0x03,0xfc, 0x03,0xfc, 0x00,0xf8,
 0x01,0xf8, 0x03,0xb0, 0x07,0x30, 0x0e,0x00, 0x1c,0x00, 0x38,0x00, 0x30,0x00},

{0x00,0x0c, 0x00,0x1c, 0x00,0x38, 0x00,0x70, 0x0c,0xe0, 0x0d,0xc0, 0x1f,0x80, 
 0x1f,0x00, 0x3f,0xc0, 0x3f,0xc0, 0x7f,0x00, 0x7c,0x00, 0xf0,0x00, 0xc0,0x00}, 

{0x30,0x00, 0x38,0x00, 0x1c,0x00, 0x0e,0x00, 0x07,0x30, 0x03,0xb0, 0x01,0xf8, 
 0x00,0xf8, 0x03,0xfc, 0x03,0xfc, 0x00,0xfe, 0x00,0x3e, 0x00,0x0f, 0x00,0x03},

};



void vga_draw_pointer(int x, int y, int x_old, int y_old)
{ 
  int k = 0;

  if (x+16 > disp_width)  { x -= 16; k += 1; }
  if (y+14 > disp_height) { y -= 14; k += 2; }

  if (x_old+16 > disp_width)  x_old -= 16;
  if (y_old+14 > disp_height) y_old -= 14;
 
  int x0 = (x < x_old) ? x : x_old;
  int y0 = (y < y_old) ? y : y_old;
  int x1 = (x > x_old) ? x : x_old;
  int y1 = (y > y_old) ? y : y_old;
 
  x0 -= x0%8;
  x1 += (8-x1%8)%8;
 
  int wi = x1-x0+16;
  int he = y1-y0+14;
 
  pixtype* save_buf = disp_buffer;
  int save_width = disp_width;
  int save_mode  = MODE;
  int save_color = COLOR;

  disp_buffer = vga_getimage(x0,y0,wi,he);
  disp_width = wi;

  COLOR = 1;
  MODE  = 1;
  vga_bitmap(x-x0,y-y0,c_map[k],2,14);

  pixtype* p = disp_buffer;
  for (int i = 0; i < he; i++)
  { draw_scan_segment(p,x0,y0+i,wi);
    p += wi;
   }

  vga_delimage(disp_buffer);

  disp_buffer = save_buf;
  disp_width = save_width;
  MODE  = save_mode;
  COLOR = save_color;
}

