/**
 ** BCC2GRX  -  Interfacing Borland based graphics programs to LIBGRX
 ** Copyright (C) 1993-97 by Hartmut Schirmer
 **
 **
 ** Contact :                Hartmut Schirmer
 **                          Feldstrasse 118
 **                  D-24105 Kiel
 **                          Germany
 **
 ** e-mail : hsc@techfak.uni-kiel.de
 **
 ** This file is part of the GRX graphics library.
 **
 ** The GRX graphics library is free software; you can redistribute it
 ** and/or modify it under some conditions; see the "copying.grx" file
 ** for details.
 **
 ** This library 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.
 **
 **/

#define __BCCGRX_C

#include "bccgrx00.h"

#define MAX_MODES 256

/* ----------------------------------------------------------------- */

static char copyright[]="Copyright (C) 1993-1994  Hartmut Schirmer";
int           __gr_Mode = 0;                    /* actual graphics mode     */
int           __gr_INIT = FALSE;                /* TRUE after initgraph()   */
char          __gr_BGICHR[128];                 /* Path to .chr files       */
int           __gr_MaxMode = 0;                 /* Last available mode      */
int           __gr_Result = grOk;               /* stores error code        */
int           __gr_X, __gr_Y;                   /* graphics cursor pos      */
int           __gr_vpl, __gr_vpt,               /* actual viewport          */
	      __gr_vpr, __gr_vpb;
int           __gr_color;                       /* drawing color            */
int           __gr_colorbg;                     /* background color         */
int           __gr_colorfill;                   /* fill color               */
int           __gr_WR = GrWRITE;                /* Write mode               */
int           __gr_lstyle = SOLID_LINE;         /* Actual line style        */
int           __gr_fpatno = SOLID_FILL;         /* Actual filling pattern   */
int           __gr_Xasp = 10000;                /* Aspect ratio             */
int           __gr_Yasp = 10000;
int           __gr_clip = TRUE;                 /* actual clipping state    */

int           __gr_Y_page_offs = 0;             /* Y offset for page simulation */

int           __gr_ADAPTER = GR_VGA;            /* Adapter used             */
int           __gr_TextLineStyle = 0;           /* use setlinestyle() while
						   plotting .chr fonts      */

int          (*__gr_initgraph_hook)(void)=NULL;  /* hook for overriding mode */
                                                /* setting in initgraph     */
int          (*__gr_closegraph_hook)(void)=NULL;

GrPattern      __gr_fillpattern;                /* GRX filling settings     */
GrLineOption   __gr_Line;                       /* GRX line settings        */

char __gr_fpatterns[][8] = {                    /* BGI fill patterns        */
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   /* EMPTY_FILL        */
  { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   /* SOLID_FILL        */
  { 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00},   /* LINE_FILL         */
  { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80},   /* LTSLASH_FILL      */
  { 0xE0, 0xC1, 0x83, 0x07, 0x0E, 0x1C, 0x38, 0x70},   /* SLASH_FILL        */
  { 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x87, 0xC3, 0xE1},   /* BKSLASH_FILL      */
  { 0xA5, 0xD2, 0x69, 0xB4, 0x5A, 0x2D, 0x96, 0x4B},   /* LTBKSLASH_FILL    */
  { 0xFF, 0x88, 0x88, 0x88, 0xFF, 0x88, 0x88, 0x88},   /* HATCH_FILL        */
  { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81},   /* XHATCH_FILL       */
  { 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33},   /* INTERLEAVE_FILL   */
  { 0x80, 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00},   /* WIDE_DOT_FILL     */
  { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00},   /* CLOSE_DOT_FILL    */
  { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}    /* USER_FILL         */
};

#ifdef GRX_VERSION
int __gr_BGI_p = 1;   /* requested / available # of pages  (valid: 1,2) */
#endif
int __gr_BGI_w = 640; /* resulution and colors needed to emulate        */
int __gr_BGI_h = 480; /* BGI driver modes                               */
int __gr_BGI_c = 16;  /* default : Standard VGA                         */


#define NULL_IS_EMPTY(s) ((s) ? : "")

/* ----------------------------------------------------------------- */

GraphicsMode *__gr_Modes = NULL;

static int ModeCompare(GraphicsMode *a, GraphicsMode *b) {
  int size_a, size_b;
  if (a->colors < b->colors) return -1;
  if (a->colors > b->colors) return +1;
  size_a = a->width * a->height;
  size_b = b->width * b->height;
  if (size_a < size_b) return -1;
  if (size_a > size_b) return +1;
  if (a->width < b->width) return -1;
  if (a->width > b->width) return +1;
  return 0;
}

static void NewMode(int w, int h, long c) {
  GraphicsMode *p, *q, *qn;
  p = malloc(sizeof(GraphicsMode));
  if (p != NULL) {
    p->width = w;
    p->height = h;
    p->colors = c;
    p->next = NULL;
    if (__gr_Modes == NULL) {
      __gr_Modes = p;
      return;
    }
    switch(ModeCompare(p,__gr_Modes)) {
      case -1 : /* p < __gr_Modes */
		p->next = __gr_Modes;
		__gr_Modes = p;
		return;
      case  0 : /* p == __gr_Modes */
		free(p);
		return;
    }
    q = __gr_Modes;
    while ((qn=q->next) != NULL) {
      switch (ModeCompare(p,qn)) {
	case -1 : /* p < q->next */
		  p->next = qn;
		  q->next = p;
		  return;
	case  0 : /* p == q->next */
		  free(p);
		  return;
      }
      q = q->next;
    }
    /* append to end */
    p->next = q->next;
    q->next = p;
  }
}

int __gr_getmode_whc(int mode, int *w, int *h, long *c) {
  GraphicsMode *p;
  p = __gr_Modes;
  mode -= __FIRST_DRIVER_SPECIFIC_MODE;
  if (mode < 0)
    return FALSE;
  while (p != NULL && mode-->0)
    p = p->next;
  if (p==NULL) return FALSE;
  *w = p->width;
  *h = p->height;
  *c = p->colors;
  return TRUE;
}

#ifdef GRX_VERSION

  /* ---- GRX v2.0 */
  static void build_mode_table(void) {
    GrFrameMode fmode;
    const GrVideoMode *mp;

    for (fmode = GR_firstGraphicsFrameMode;
	   fmode <= GR_lastGraphicsFrameMode;
	     ++fmode ) {
      mp = GrFirstVideoMode(fmode);
      while (mp != NULL) {
	NewMode(mp->width, mp->height, 1<<mp->bpp);
	mp = GrNextVideoMode(mp);
      }
    }
  }

  #define COLOR_NR2DRV(col) (col)

#else

  /* ---- GRX v1.0x */
  #ifdef GR_DRV_VER_GRD
  #define _GRD GR_DRV_VER_GRD
  #define _GRN GR_DRV_VER_GRN
  #define _VDR GR_DRV_VER_VDR
  #endif

  static long COLOR_NR2DRV(int col) {
    switch (col) {
      case 1<<16 : return 0xC010;
      case 1<<24 : return 0xC018;
    }
    return col;
  }

  static long COLOR_DRV2NR(int c) {
    if ( (c & 0xC000) == 0xC000)
      c = 1<<(c & 0x00ff);
    return c;
  }

  static int color_ok(long c) {
    /* Check if GRX supports requested color mode */
    int old_colors, res;
    extern int _GrNumColors;  /* happy hacking ... */
    c = COLOR_DRV2NR(c);
    switch (c) {
      case 1<<15 :
      case 1<<16 :
      case 1<<24 : old_colors = _GrNumColors;
		   _GrNumColors = c;
		   res = GrLineOffset(128) != 0;
		   _GrNumColors = old_colors;
		   return res;
    }
    return TRUE; /* No way to check other colors ... */
  }

  #ifdef _VDR
  static int driver = _GRD;
  #endif

  static int CheckTableEntry(GR_DRIVER_MODE_ENTRY *gm) {
    if (gm == NULL) return FALSE;
    if (!color_ok(gm->number_of_colors) )
      return FALSE;
    #ifdef _VDR
      if (driver == _VDR)
	return gm->mode.vdr.BIOS_mode != 0xFFF;
      if (driver == _GRN)
	return gm->mode.grn.BIOS_mode != 0xFF;
      return FALSE;
    #else
      return gm->BIOS_mode != 0xFF;
    #endif
  }

  static void build_mode_table(void) {
    GR_DRIVER_MODE_ENTRY *tm, *gm;
    #ifdef _VDR
    driver = 0xFFFF0000 &
    #endif
    GrGetDriverModes(&tm, &gm);

    while (gm->width != 0 && gm->height != 0 && gm->number_of_colors != 0) {
      if (CheckTableEntry(gm))
	NewMode(gm->width, gm->height, COLOR_DRV2NR(gm->number_of_colors));
      ++gm;
    }
  }

#endif

/* ----------------------------------------------------------------- */

void __gr_set_up_modes(void)
{
  int mode;
  GraphicsMode *p;
  static int DidInit = FALSE;

  if (DidInit) return;
  if (strlen(copyright) != sizeof(copyright)-1)
    exit(1);
# ifdef __linux__
   /* vga_init(); */
# endif
  MM = 1;
  GrSetMode( GrCurrentMode());                       /* Init grx */
  __gr_ADAPTER = GrAdapterType();
  if (__gr_ADAPTER == GR_S3)
    __gr_ADAPTER = GR_VGA;
  build_mode_table();
  p = __gr_Modes;
  mode = __FIRST_DRIVER_SPECIFIC_MODE-1;
  while (p != NULL) {
    ++mode;
    p = p->next;
  }
  MM = mode;
  DidInit = TRUE;
}

/* ----------------------------------------------------------------- */

int __gr_White(void) {
  return GrNumColors() > 256 ? GrWhite() : 15;
}

int getmaxx(void) {
  return GrScreenX()-1;
}

int getmaxy(void) {
  return GrScreenY()-1;
}

/* ----------------------------------------------------------------- */
void graphdefaults(void)
{
  ERR          = grOk;
  moveto( 0, 0);
  COL = FILL   = WHITE;
  COLBG        = BLACK;
  __gr_WR      = GrWRITE;

  _DO_INIT_CHECK;
  GrSetContext( NULL);         /* ViewPort == Full screen */
#ifdef GRX_VERSION
  GrSetViewport(0,0);
  PY = 0;
#endif
  VL = VT = 0;
  VR = getmaxx();
  VB = getmaxy();
  __gr_clip = TRUE;

  __gr_Xasp = 10000;
  __gr_Yasp = __gr_Xasp * ((VR+1)*3) / ((VB+1)*4);

  __gr_lstyle     = SOLID_LINE;
  LNE.lno_pattlen = 0;
  LNE.lno_dashpat = NULL;
  LNE.lno_width   = 1;

  FPATT = SOLID_FILL;
  FILLP.gp_ispixmap    = 0;    /* Bitmap fill pattern */
  FILLP.gp_bmp_data    = (unsigned char *)&__gr_fpatterns[FPATT];
  FILLP.gp_bmp_height  = 8;
  FILLP.gp_bmp_fgcolor = COL;
  FILLP.gp_bmp_bgcolor = COLBG;

  __gr_TextLineStyle = 0;
}

/* ----------------------------------------------------------------- */

int __gr_adaptcnv(int grx) {
  switch (grx) {
    case GR_VGA   : return VGA;
    case GR_EGA   : return EGA;
    case GR_8514A : return IBM8514;
    case GR_HERC  : return HERCMONO;
  }
  return grx;
}

/* ----------------------------------------------------------------- */
void setgraphmode(int mode)
{
  int w, h;
  long c;

  _DO_INIT_CHECK;
  switch (mode) {
    case GRX_DEFAULT_GRAPHICS:
#ifdef GRX_VERSION
      w = GrDriverInfo->defgw;
      h = GrDriverInfo->defgh;
      c = GrDriverInfo->defgc;
      goto Default;
#else
      GrSetMode( GR_default_graphics);
      break;
#endif
    case GRX_BIGGEST_NONINTERLACED_GRAPHICS:
      GrSetMode( GR_biggest_noninterlaced_graphics);
      break;
    case GRX_BIGGEST_GRAPHICS:
      GrSetMode( GR_biggest_graphics);
      break;
    case GRX_BGI_EMULATION:
#ifdef GRX_VERSION
      if (__gr_BGI_p > 1) {
	__gr_BGI_p = 2;
	GrSetMode( GR_custom_graphics , __gr_BGI_w, __gr_BGI_h,
		   COLOR_NR2DRV(__gr_BGI_c), __gr_BGI_w, 2*__gr_BGI_h);
	if (GrVirtualY() < 2*GrScreenY()) __gr_BGI_p = 1;
      } else
#endif
	GrSetMode( GR_width_height_color_graphics, __gr_BGI_w,
		   __gr_BGI_h, COLOR_NR2DRV(__gr_BGI_c));
      break;
    default:
      if (mode < __FIRST_DRIVER_SPECIFIC_MODE || mode > MM) {
	ERR = grInvalidMode;
	return;
      }
      if (!__gr_getmode_whc(mode, &w, &h, &c)) {
	ERR = grInvalidMode;
	return;
      }
#ifdef GRX_VERSION
Default:
      if (__gr_BGI_p > 1) {
	__gr_BGI_p = 2;
	GrSetMode( GR_custom_graphics , w, h, COLOR_NR2DRV(c), w, 2*h);
	if (GrVirtualY() < 2*GrScreenY()) __gr_BGI_p = 1;
      } else
#endif
	GrSetMode( GR_width_height_color_graphics, w, h, COLOR_NR2DRV(c));
      break;
  }
  __gr_Mode = mode;
  graphdefaults();
  GrClearScreen(BLACK);
}

/* ----------------------------------------------------------------- */
void __gr_initgraph(int *graphdriver, int *graphmode) {
  ERR = grOk;
  if (__gr_initgraph_hook) {
    (*__gr_initgraph_hook) ();
    if (ERR != grOk) {
      __gr_INIT = FALSE;
      return;
    }
  } else if (!__gr_INIT) {
    __gr_set_up_modes();
    if (ERR != grOk) return;
    if ( *graphdriver != NATIVE_GRX || *graphmode < 0 || *graphmode > MM)
      *graphmode = 0;
    __gr_INIT = TRUE;
    setgraphmode(*graphmode);
    if (ERR != grOk) {
      __gr_INIT = FALSE;
      return;
    }
  }
  if (*graphmode == 0) *graphdriver = __gr_adaptcnv(__gr_ADAPTER);
		  else *graphdriver = NATIVE_GRX;
}



void closegraph(void)
{
  if (__gr_closegraph_hook) {
    (*__gr_closegraph_hook) ();
    __gr_initgraph_hook = NULL;
    __gr_closegraph_hook = NULL;
  } else
  restorecrtmode();
  __gr_INIT = FALSE;
}



void  __gr_set_libbcc_init_hooks (
                      int (*init) (void) ,
                      int (*close) (void) )
{
   __gr_initgraph_hook  = init;
   __gr_closegraph_hook = close;
}



void initgraph(int *graphdriver, int *graphmode, char *pathtodriver) {
  __gr_initgraph(graphdriver, graphmode);
  strcpy(__gr_BGICHR, NULL_IS_EMPTY(pathtodriver));
}
