/*
   GRAPHICS DEMO FOR Borland C++

   Copyright (c) 1987,88,91 Borland International. All rights reserved.

   From the command line, use:

	bcc bgidemo graphics.lib

*/
/* Partially copyrighted (c) 1993-96 by Hartmut Schirmer */

#ifdef __TINY__
#error BGIDEMO will not run in the tiny model.
#endif

#ifdef __linux__
#  include "bcclnx.h"
#else
#  include <dos.h>
#endif
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#ifdef __GNUC__
#  define BGI_PATH "..\\chr"
#  ifndef __linux__
#    include <pc.h>
#  endif
#  include "libbcc.h"
#  define Random(r) (random() % (r))
#  define Seed(s) srandom(s)
#  define itoa(value,str,radix) sprintf((str),"%d",(value))
#  define getch() getkey()
#else
#  define BGI_PATH "e:\\comp\\tp\\bgi"
#  define Random(r) random(r)
#  define Seed(s) srand(s)
#  include <conio.h>
#  include <graphics.h>
#endif

#include "bgiext.h"

#define ESC     0x1b                    /* Define the escape key        */
#ifndef TRUE
#  define TRUE  1                       /* Define some handy constants  */
#endif
#ifndef FALSE
#  define FALSE 0                       /* Define some handy constants  */
#endif
#ifndef PI
#  define PI    3.14159                 /* Define a value for PI        */
#endif
#define ON      1                       /* Define some handy constants  */
#define OFF     0                       /* Define some handy constants  */

#define NFONTS 11

char *Fonts[] = {
  "DefaultFont",   "TriplexFont",   "SmallFont",
  "SansSerifFont", "GothicFont", "ScriptFont", "SimplexFont", "TriplexScriptFont",
  "ComplexFont", "EuropeanFont", "BoldFont"
};

char *LineStyles[] = {
  "SolidLn",  "DottedLn",  "CenterLn",  "DashedLn",  "UserBitLn"
};

char *FillStyles[] = {
  "EmptyFill",  "SolidFill",      "LineFill",      "LtSlashFill",
  "SlashFill",  "BkSlashFill",    "LtBkSlashFill", "HatchFill",
  "XHatchFill", "InterleaveFill", "WideDotFill",   "CloseDotFill"
};

char *TextDirect[] = {
  "HorizDir",  "VertDir"
};

char *HorizJust[] = {
  "LeftText",   "CenterText",   "RightText"
};

char *VertJust[] = {
  "BottomText",  "CenterText",  "TopText"
};

struct PTS {
  int x, y;
};      /* Structure to hold vertex points      */

int    GraphDriver;             /* The Graphics device driver           */
int    GraphMode;               /* The Graphics mode value              */
double AspectRatio;             /* Aspect ratio of a pixel on the screen*/
int    MaxX, MaxY;              /* The maximum resolution of the screen */
int    MaxColors;               /* The maximum # of colors available    */
int    ErrorCode;               /* Reports any graphics errors          */
struct palettetype palette;             /* Used to read palette info    */
static char PauseMsg[] = "Esc aborts or press a key...";
static char StopMsg[]  = "ESC Aborts - Press a Key to stop";

/*                                                                      */
/*      GPRINTF: Used like PRINTF except the output is sent to the      */
/*      screen in graphics mode at the specified co-ordinate.           */
/*                                                                      */

int gprintf( int *xloc, int *yloc, char *fmt, ... )
{
  va_list  argptr;                      /* Argument list pointer        */
  char str[140];                        /* Buffer to build sting into   */
  int cnt;                              /* Result of SPRINTF for return */

  va_start( argptr, fmt );              /* Initialize va_ functions     */

  cnt = vsprintf( str, fmt, argptr );   /* prints string to buffer      */
  outtextxy( *xloc, *yloc, str );       /* Send string in graphics mode */
  *yloc += textheight( "H" ) + 2;       /* Advance to next line         */

  va_end( argptr );                     /* Close va_ functions          */

  return( cnt );                        /* Return the conversion count  */

}

/*                                                                      */
/*      CHANGETEXTSTYLE: similar to settextstyle, but checks for        */
/*      errors that might occur whil loading the font file.             */
/*                                                                      */

void changetextstyle(int font, int direction, int charsize)
{
  int ErrorCode;

  graphresult();                        /* clear error code             */
  settextstyle(font, direction, charsize);
  ErrorCode = graphresult();            /* check result                 */
  if( ErrorCode != grOk ){              /* if error occured             */
    closegraph();
    printf(" Graphics System Error: %s\n", grapherrormsg( ErrorCode ) );
    exit( 1 );
  }
}

/*                                                                      */
/*      DRAWBORDER: Draw a solid single line around the current         */
/*      viewport.                                                       */
/*                                                                      */

void DrawBorder(int color)
{
  struct viewporttype vp;

  setcolor( color);

  setlinestyle( SOLID_LINE, 0, NORM_WIDTH );

  getviewsettings( &vp );
  rectangle( 0, 0, vp.right-vp.left, vp.bottom-vp.top);

}

/*                                                                      */
/*      STATUSLINE: Display a status line at the bottom of the screen.  */
/*                                                                      */

void StatusLineColor( char *msg, int color )
{
  int height;

  setviewport( 0, 0, MaxX, MaxY, 1 );   /* Open port to full screen     */
  setcolor( color);                     /* Set requested color          */

  changetextstyle( DEFAULT_FONT, HORIZ_DIR, 1 );
  settextjustify( CENTER_TEXT, TOP_TEXT );
  setlinestyle( SOLID_LINE, 0, NORM_WIDTH );
  setfillstyle( EMPTY_FILL, 0 );

  height = textheight( "H" );           /* Detemine current height      */
  bar( 0, MaxY-(height+4), MaxX, MaxY );
  rectangle( 0, MaxY-(height+4), MaxX, MaxY );
  outtextxy( MaxX/2, MaxY-(height+2), msg );
  setviewport( 1, height+5, MaxX-1, MaxY-(height+5), 1 );
}

void StatusLine( char *msg )
{
  StatusLineColor(msg, WHITE);
}

/*                                                                      */
/*      MAINWINDOW: Establish the main window for the demo and set      */
/*      a viewport for the demo code.                                   */
/*                                                                      */

void DisplayTitle(char *header, int color) {
  struct viewporttype vp;

  getviewsettings( &vp );
  setcolor( color);                     /* Set current requested color  */
  setviewport( 0, 0, MaxX, MaxY, 1 );   /* Open port to full screen     */
  changetextstyle( DEFAULT_FONT, HORIZ_DIR, 1 );
  settextjustify( CENTER_TEXT, TOP_TEXT );
  outtextxy( MaxX/2, 2, header );
  setviewport(vp.left, vp.top, vp.right, vp.bottom, vp.clip);
}

void MainWindowColor( char *header, int color)
{
  int height;

  cleardevice();                        /* Clear graphics screen        */
  setcolor( color);                     /* Set current requested color  */
  setviewport( 0, 0, MaxX, MaxY, 1 );   /* Open port to full screen     */

  height = textheight( "H" );           /* Get basic text height        */

  DisplayTitle(header, color);
  setviewport( 0, height+4, MaxX, MaxY-(height+4), 1 );
  DrawBorder(color);
  setviewport( 1, height+5, MaxX-1, MaxY-(height+5), 1 );

}

void MainWindow( char *header )
{
  MainWindowColor( header, WHITE);
}

/*                                                                      */
/*      PAUSE: Pause until the user enters a keystroke. If the          */
/*      key is an ESC, then exit program, else simply return.           */
/*                                                                      */


void NewPause(int clear)
{
  int c;

  StatusLine( PauseMsg );               /* Put msg at bottom of screen  */

  c = getch();                          /* Read a character from kbd    */

  if( ESC == c ){                       /* Does user wish to leave?     */
    closegraph();                       /* Change to text mode          */
    exit( 1 );                          /* Return to OS                 */
  }

  if( 0 == c ){                         /* Did use hit a non-ASCII key? */
    c = getch();                        /* Read scan code for keyboard  */
  }

  if (clear)
    cleardevice();                      /* Clear the screen             */
}

#define Pause() NewPause(TRUE)

/*                                                                      */
/*      INITIALIZE: Initializes the graphics system and reports         */
/*      any errors which occured.                                       */
/*                                                                      */

void Initialize(void)
{
  int xasp, yasp;

  GraphDriver = DETECT;                 /* Request auto-detection       */
#ifdef __GNUC__
  set_BGI_mode_pages(2);
#endif
  initgraph( &GraphDriver, &GraphMode, BGI_PATH );
  ErrorCode = graphresult();            /* Read result of initialization*/
  if( ErrorCode != grOk ){              /* Error occured during init    */
    printf(" Graphics System Error: %s\n", grapherrormsg( ErrorCode ) );
    exit( 1 );
  }

  getpalette( &palette );               /* Read the palette from board  */
  MaxColors = getmaxcolor() + 1;        /* Read maximum number of colors*/
  if (MaxColors == 256)
    setrgbdefaults();

  MaxX = getmaxx();
  MaxY = getmaxy();                     /* Read size of screen          */

  getaspectratio( &xasp, &yasp );       /* read the hardware aspect     */
  AspectRatio = (double)xasp / (double)yasp; /* Get correction factor   */

}


/*                                                                      */
/*      REPORTSTATUS: Report the current configuration of the system    */
/*      after the auto-detect initialization.                           */
/*                                                                      */

void ReportStatus(void)
{
  struct viewporttype     viewinfo;     /* Params for inquiry procedures*/
  struct linesettingstype lineinfo;
  struct fillsettingstype fillinfo;
  struct textsettingstype textinfo;
  struct palettetype      palette;

  char *driver, *mode, *fmt;            /* Strings for driver and mode  */
  int x, y, mno;

  getviewsettings( &viewinfo );
  getlinesettings( &lineinfo );
  getfillsettings( &fillinfo );
  gettextsettings( &textinfo );
  getpalette( &palette );

  x = 5;
  y = 4;

  MainWindow( "Status report after InitGraph" );
  settextjustify( LEFT_TEXT, TOP_TEXT );

  driver = getdrivername();
  mode = getmodename(GraphMode);        /* get current setting          */

  gprintf( &x, &y, "Graphics device    : %-20s (%d)", driver, GraphDriver );
  gprintf( &x, &y, "Graphics mode      : %-20s (%d)", mode, GraphMode );
#ifdef __GNUC__
  gprintf( &x, &y, "Available pages    : %d", get_BGI_mode_pages() );
#endif
  gprintf( &x, &y, "Screen resolution  : ( 0, 0, %d, %d )", getmaxx(), getmaxy() );
  gprintf( &x, &y, "Current view port  : ( %d, %d, %d, %d )",
  viewinfo.left, viewinfo.top, viewinfo.right, viewinfo.bottom );
  gprintf( &x, &y, "Clipping           : %s", viewinfo.clip ? "ON" : "OFF" );

  gprintf( &x, &y, "Current position   : ( %d, %d )", getx(), gety() );
  gprintf( &x, &y, "Colors available   : %d", MaxColors );
  gprintf( &x, &y, "Current color      : %d", getcolor() );

  gprintf( &x, &y, "Line style         : %s", LineStyles[ lineinfo.linestyle ] );
  gprintf( &x, &y, "Line thickness     : %d", lineinfo.thickness );

  gprintf( &x, &y, "Current fill style : %s", FillStyles[ fillinfo.pattern ] );
  gprintf( &x, &y, "Current fill color : %d", fillinfo.color );

  gprintf( &x, &y, "Current font       : %s", Fonts[ textinfo.font ] );
  gprintf( &x, &y, "Text direction     : %s", TextDirect[ textinfo.direction ] );
  gprintf( &x, &y, "Character size     : %d", textinfo.charsize );
  gprintf( &x, &y, "Horizontal justify : %s", HorizJust[ textinfo.horiz ] );
  gprintf( &x, &y, "Vertical justify   : %s", VertJust[ textinfo.vert ] );

  gprintf( &x, &y, "Aspect ratio       : %lf", AspectRatio);

  getviewsettings( &viewinfo );
  {
    int ybase;
    setfillstyle(SOLID_FILL, BLACK);
    if (MaxY<350-1) {
      x = 400;
      y = 4;
      fmt = "#%-3d: %s";
    } else {
      y += 5;
      fmt = " Mode #%-3d : %s";
    }
    gprintf( &x, &y, "Available modes :");
    y += 5;
    ybase = y;
    for (mno = 0; mno <= getmaxmode(); ++mno) {
      char sp[100];
      sprintf(sp, fmt, mno, getmodename(mno));
      bar(x-4, y + textheight(sp), x+textwidth(sp)+4, y);
      gprintf( &x, &y, "%s", sp);
      if (y+viewinfo.top>viewinfo.bottom-8) {
	y = ybase;
	x += (viewinfo.right-viewinfo.left) / 2;
      }
    }
  }

  Pause();                              /* Pause for user to read screen*/

}

/*                                                                      */
/*      TEXTDUMP: Display the all the characters in each of the         */
/*      available fonts.                                                */
/*                                                                      */

void TextDump(void)
{
  static int CGASizes[]  = {
    1, 3, 7, 3, 3, 2, 2, 2, 2, 2, 2  };
  static int NormSizes[] = {
    1, 4, 7, 4, 4, 2, 2, 2, 2, 2, 2  };

  char buffer[80];
  int font, ch, wwidth, lwidth, size;
  struct viewporttype vp;

  for( font=0 ; font<NFONTS ; ++font ){ /* For each available font      */
    sprintf( buffer, "%s Character Set", Fonts[font] );
    MainWindow( buffer );               /* Display fontname as banner   */
    getviewsettings( &vp );             /* read current viewport        */

    settextjustify( LEFT_TEXT, TOP_TEXT );
    moveto( 5, 3 );

    buffer[1] = '\0';                   /* Terminate string             */
    wwidth = vp.right - vp.left;        /* Determine the window width   */

    if( font == DEFAULT_FONT ){
      changetextstyle( font, HORIZ_DIR, 1 );
      lwidth = textwidth( "H" );        /* Get average letter width     */
      ch = 0;
      while( ch < 256 ){                /* For each possible character  */
	buffer[0] = ch;                 /* Put character into a string  */
	if( (getx() + lwidth) > wwidth-3)
	  moveto( 5, gety() + textheight("H") + 3 );
	outtext( buffer );              /* send string to screen        */
	++ch;                           /* Goto the next character      */
      }
    }
    else{

      size = (MaxY < 200) ? CGASizes[font] : NormSizes[font];
      changetextstyle( font, HORIZ_DIR, size );

      ch = '!';                         /* Begin at 1st printable       */
      while( ch < 256 ){                /* For each printable character */
	buffer[0] = ch;                 /* Put character into a string  */
	lwidth = textwidth( buffer);    /* Get letter width             */
	if( (lwidth+getx()) > wwidth-3) /* Are we still in window?      */
	  moveto( 5, gety()+textheight("H")+3 );
	outtext( buffer );              /* send string to screen        */
	++ch;                           /* Goto the next character      */
      }

    }

    Pause();                            /* Pause until user acks        */

  }                                     /* End of FONT loop             */

}

/*                                                                      */
/*      BAR3DDEMO: Display a 3-D bar chart on the screen.               */
/*                                                                      */

void Bar3DDemo(void)
{
  static int barheight[] = {
    1, 3, 5, 4, 3, 2, 1, 5, 4, 2, 3   };
  struct viewporttype vp;
  int xstep, ystep, deepth;
  int i, j, h, color, bheight;
  char buffer[10];

  MainWindow( "Bar 3-D / Rectangle Demonstration" );

  h = 3 * textheight( "H" );
  getviewsettings( &vp );
  settextjustify( CENTER_TEXT, TOP_TEXT );
  changetextstyle( TRIPLEX_FONT, HORIZ_DIR, 4 );
  outtextxy( MaxX/2, 6, "These are 3-D Bars" );
  changetextstyle( DEFAULT_FONT, HORIZ_DIR, 1 );
  setviewport( vp.left+50, vp.top+40, vp.right-50, vp.bottom-10, 1 );
  getviewsettings( &vp );

  line( h, h, h, vp.bottom-vp.top-h );
  line( h, (vp.bottom-vp.top)-h, (vp.right-vp.left)-h, (vp.bottom-vp.top)-h );
  xstep = ((vp.right-vp.left) - (2*h)) / 10;
  ystep = ((vp.bottom-vp.top) - (2*h)) / 5;
  j = (vp.bottom-vp.top) - h;
  deepth = (getmaxx() <= 400) ? 7 : 15;
  settextjustify( LEFT_TEXT, CENTER_TEXT );

  for( i=0 ; i<6 ; ++i ){
    line( h/2, j, h, j );
    itoa( i, buffer, 10 );
    outtextxy( 0, j, buffer );
    j -= ystep;
  }

  j = h;
  settextjustify( CENTER_TEXT, TOP_TEXT );

  for( i=0 ; i<11 ; ++i ){
    color = Random( MaxColors-1 ) + 1;
    setfillstyle( i+1, color );
    line( j, (vp.bottom-vp.top)-h, j, (vp.bottom-vp.top-3)-(h/2) );
    itoa( i, buffer, 10 );
    outtextxy( j, (vp.bottom-vp.top)-(h/2), buffer );
    if( i != 10 ){
      bheight = (vp.bottom-vp.top) - h - 1;
      bar3d( j, (vp.bottom-vp.top-h)-(barheight[i]*ystep), j+xstep-deepth, bheight, deepth, 1 );
    }
    j += xstep;
  }

  Pause();                              /* Pause for user's response    */

}

/*                                                                      */
/*      RandomBARS: Display random bars                                 */
/*                                                                      */

void RandomBars(void)
{
  int color;

  MainWindow( "Random Bars" );
  StatusLine( PauseMsg );               /* Put msg at bottom of screen   */
  while( !kbhit() ){                    /* Until user enters a key...   */
    color = Random( MaxColors-1 )+1;
    setcolor( color );
    setfillstyle( Random(11)+1, color );
    bar3d( Random( getmaxx() ), Random( getmaxy() ),
       Random( getmaxx() ), Random( getmaxy() ), 0, OFF);
  }

  Pause();                              /* Pause for user's response    */

}


/*                                                                      */
/*      TEXTDEMO: Show each font in several sizes to the user.          */
/*                                                                      */

void TextDemo(void)
{
  int charsize[] = {
    1, 3, 7, 3, 4, 2, 2, 2, 2, 2, 2   };
  int font, size;
  int h, x, y, i;
  struct viewporttype vp;
  char buffer[80];

  for( font=0 ; font<NFONTS ; ++font ){ /* For each of the avail. fonts */

    sprintf( buffer, "%s Demonstration", Fonts[font] );
    MainWindow( buffer );
    getviewsettings( &vp );

    changetextstyle( font, VERT_DIR, charsize[font] );
    settextjustify( CENTER_TEXT, BOTTOM_TEXT );
    outtextxy( 2*textwidth("M"), vp.bottom - 2*textheight("M"), "Vertical" );

    changetextstyle( font, HORIZ_DIR, charsize[font] );
    settextjustify( LEFT_TEXT, TOP_TEXT );
    outtextxy( 2*textwidth("M"), 2, "Horizontal" );

    settextjustify( CENTER_TEXT, CENTER_TEXT );
    x = (vp.right - vp.left) / 2;
    y = textheight( "H" );

    for( i=1 ; i<5 ; ++i ){             /* For each of the sizes */
      size = (font == SMALL_FONT) ? i+3 : i;
      changetextstyle( font, HORIZ_DIR, size );
      h = textheight( "H" );
      y += h;
      sprintf( buffer, "Size %d", size );
      outtextxy( x, y, buffer );

    }

    if( font != DEFAULT_FONT ){         /* Show user declared font size */
      y += h / 2;                       /* Move down the screen         */
      settextjustify( CENTER_TEXT, TOP_TEXT );
      setusercharsize( 5, 6, 3, 2 );
      changetextstyle( font, HORIZ_DIR, USER_CHAR_SIZE );
      outtextxy( (vp.right-vp.left)/2, y, "User Defined Size" );
    }

    Pause();                            /* Pause to let user look       */

  }                                     /* End of FONT loop             */

}

/*                                                                      */
/*      COLORDEMO: Display the current color palette on the screen.     */
/*                                                                      */

void ColorDemo(void)
{
  struct viewporttype vp;
  int color, height, width, ec;
  int x, y, i, j;
  char cnum[5];

  MainWindow( "Color Demonstration" );  /* Show demonstration name      */

  color = 1;
  getviewsettings( &vp );               /* Get the current window size  */
  width  = 2 * ( (vp.right+1) / 16 );      /* Get box dimensions           */
  height = 2 * ( (vp.bottom-10) / 10 );

  x = width / 2;
  y = height / 2;       /* Leave 1/2 box border         */

  for( j=0 ; j<3 ; ++j ){               /* Row loop                     */

    for( i=0 ; i<5 ; ++i ){             /* Column loop                  */

      ec = _ega_color(color);
      setfillstyle(SOLID_FILL, ec);     /* Set to solid fill in color   */
      setcolor( ec );                   /* Set the same border color    */

      bar( x, y, x+width, y+height );   /* Draw the rectangle           */
      rectangle( x, y, x+width, y+height );  /* outline the rectangle   */

      if( color == BLACK ){             /* If box was black...          */
	setcolor( _ega_color(WHITE) );  /* Set drawing color to white   */
	rectangle( x, y, x+width, y+height );  /* Outline black in white*/
      }

      itoa( color, cnum, 10 );          /* Convert # to ASCII           */
      outtextxy( x+(width/2), y+height+4, cnum );  /* Show color #      */

      color = ++color % MaxColors;      /* Advance to the next color    */
      x += (width / 2) * 3;             /* move the column base         */
    }                           /* End of Column loop           */

    y += (height / 2) * 3;              /* move the row base            */
    x = width / 2;                      /* reset column base            */
  }                                     /* End of Row loop              */

  Pause();                              /* Pause for user's response    */

}

/*                                                                      */
/*      ARCDEMO: Display a random pattern of arcs on the screen */
/*      until the user says enough.                                     */
/*                                                                      */

void ArcDemo(void)
{
  int mradius;                          /* Maximum radius allowed       */
  int eangle;                           /* Random end angle of Arc      */
  struct arccoordstype ai;              /* Used to read Arc Cord info   */

  MainWindow( "Arc Demonstration" );
  StatusLine( StopMsg);

  mradius = (int)(MaxY / (10.0*AspectRatio));  /* Determine the maximum radius */

  while( !kbhit() ){                    /* Repeat until a key is hit    */
    setcolor( Random( MaxColors - 1 ) + 1 );    /* randomly select a color      */
    eangle = Random( 358 ) + 1;         /* Select an end angle          */
    arc( Random(MaxX), Random(MaxY), Random(eangle), eangle, mradius );
    getarccoords( &ai );                /* Read Cord data               */
    line( ai.x, ai.y, ai.xstart, ai.ystart ); /* line from start to center */
    line( ai.x, ai.y, ai.xend,   ai.yend );   /* line from end to center   */
  }                                     /* End of WHILE not KBHIT       */

  Pause();                              /* Wait for user's response     */

}

/*                                                                      */
/*      CIRCLEDEMO: Display a random pattern of circles on the screen   */
/*      until the user says enough.                                     */
/*                                                                      */

void CircleDemo(void)
{
  int mradius;                          /* Maximum radius allowed       */

  MainWindow( "Circle Demonstration" );
  StatusLine( StopMsg);

  mradius = (int)(MaxY / (10.0*AspectRatio));  /* Determine the maximum radius */

  while( !kbhit() ){                    /* Repeat until a key is hit    */
    setcolor( Random( MaxColors - 1 ) + 1 );    /* Randomly select a color      */
    circle( Random(MaxX), Random(MaxY), Random(mradius) );
  }   