#define INCL_DOS
#define INCL_VIO
#define INCL_KBD
#include <os2.h>

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

// Color attributes for foreground text

#define FG_BLACK                   0
#define FG_BLUE                    1
#define FG_GREEN                   2
#define FG_CYAN                    3
#define FG_RED                     4
#define FG_MAGENTA                 5
#define FG_BROWN                   6
#define FG_LIGHTGREY               7
#define FG_DARKGREY                8
#define FG_LIGHTBLUE               9
#define FG_LIGHTGREEN             10
#define FG_LIGHTCYAN              11
#define FG_LIGHTRED               12
#define FG_LIGHTMAGENTA           13
#define FG_YELLOW                 14
#define FG_WHITE                  15
#define FG_BLINK                0x80

// Color attributes for text background

#define BG_BLACK                0x00
#define BG_BLUE                 0x10
#define BG_GREEN                0x20
#define BG_CYAN                 0x30
#define BG_RED                  0x40
#define BG_MAGENTA              0x50
#define BG_BROWN                0x60
#define BG_LIGHTGREY            0x70

// Monochrome attributes for foreground text

#define FG_UNDERLINE            0x01
#define FG_INTENSE              0x08

// Monochrome attributes for text background

#define BG_INTENSE              0x10

// ----------------------------------------------------------------------------

#define DAEMON_MAX_WIDTH	32
#define DAEMON_MAX_HEIGHT	19

static char *daemon_pic[] = {
        "             ,        ,",
	"            /(        )`",
	"            \\ \\___   / |",
	"            /- _  `-/  '",
	"           (/\\/ \\ \\   /\\",
	"           / /   | `    \\",
	"           O O   ) /    |",
	"           `-^--'`<     '",
	"          (_.)  _  )   /",
	"           `.___/`    /",
	"             `-----' /",
	"<----.     __ / __   \\",
	"<----|====O)))==) \\) /====",
	"<----'    `--' `.__,' \\",
	"             |        |",
	"              \\       /       /\\",
	"         ______( (_  / \\______/",
	"       ,'  ,-----'   |",
	"       `--{__________)",
	NULL
};

static char *daemon_attr[] = {
        "             R        R",
	"            RR        RR",
	"            R RRRR   R R",
	"            RR W  RRR  R",
	"           RWWW W R   RR",
	"           W W   W R    R",
	"           B B   W R    R",
	"           WWWWWWRR     R",
	"          RRRR  R  R   R",
	"           RRRRRRR    R",
	"             RRRRRRR R",
	"YYYYYY     RR R RR   R",
	"YYYYYYYYYYRRRRYYR RR RYYYY",
	"YYYYYY    RRRR RRRRRR R",
	"             R        R",
	"              R       R       RR",
	"         CCCCCCR RR  R RRRRRRRR",
	"       CC  CCCCCCC   C",
	"       CCCCCCCCCCCCCCC",
	NULL
};

static char *current_time()
{
  static char buf[32];
  DATETIME t;

  DosGetDateTime( &t );
  sprintf( buf, "%02d/%02d/%02d %02d:%02d:%02d",
           int(t.day), int(t.month), int(t.year),
           int(t.hours), int(t.minutes), int(t.seconds) );

  return buf;
}

static char xflip_symbol( char symbol )
{
  static const char lchars[]( "`'(){}[]\\/<>" );
  static const char rchars[]( "'`)(}{][/\\><" );

  for( int pos( 0 ); lchars[pos]; pos++ )
    if( lchars[pos] == symbol )
      return rchars[pos];

  return symbol;
}

static void set( int x, int y, char ch, unsigned short attr )
{
  unsigned short out( (attr<<8) | ch );

  Vio16WrtCellStr( (PCH)&out, 2, y, x, 0 );
}

static void fillw( unsigned short attr, char ch, int x, int y, int len )
{
  unsigned short out( (attr<<8) | ch );

  Vio16WrtNCell( (PCH)&out, len, y, x, 0 );
}

static void clear_daemon( int xpos, int ypos, int dxdir, int xoff, int yoff, 
                          int xlen, int ylen )
{
  if( xlen<=0 )
    return;

  for( int y( yoff ); y<ylen; y++ )
    fillw( FG_LIGHTGREY|BG_BLACK, 0x20, xpos+xoff, ypos+y, xlen-xoff );
}

static void draw_daemon( int xpos, int ypos, int dxdir, int xoff, int yoff, 
                         int xlen, int ylen )
{
  for( int y( yoff ); y<ylen; y++ )
  {
    int px( dxdir<0 ? xoff : DAEMON_MAX_WIDTH - xlen );

    if( px>=strlen( daemon_pic[y] ) )
      continue;

    for( int x( xoff ); (x<xlen) && daemon_pic[y][px]; x++, px++)
    {
      int attr;

      switch( daemon_attr[y][px] )
      {
      case 'R': attr = FG_LIGHTRED|BG_BLACK;  break;
      case 'Y': attr = FG_YELLOW|BG_BLACK;    break;
      case 'B': attr = FG_LIGHTBLUE|BG_BLACK; break;
      case 'W': attr = FG_LIGHTGREY|BG_BLACK; break;
      case 'C': attr = FG_CYAN|BG_BLACK;      break;
      default:  attr = FG_WHITE|BG_BLACK;     break;
      }

      if( dxdir<0 )
        set( xpos+x, ypos+y, daemon_pic[y][px], attr ); 	//Moving left
      else
        set( xpos+DAEMON_MAX_WIDTH-px-1, ypos+y,		//Moving right
             xflip_symbol( daemon_pic[y][px] ), attr );
    }
  }
}

static void clear_string( int xpos, int ypos, int xoff, char *s, int len )
{
  if( len <= 0 )
    return;

  fillw( FG_LIGHTGREY|BG_BLACK, 0x20, xpos + xoff, ypos, len - xoff );
}

static void draw_string( int xpos, int ypos, int xoff, char *s, int len )
{
  int how( (len-xoff)*2 );
  unsigned short *tmp( (unsigned short *)_alloca( how ) );

  for( int x( xoff ); x < len; x++ )
    tmp[x-xoff] = ((FG_LIGHTGREEN|BG_BLACK)<<8) | s[x];

  Vio16WrtCellStr( (PCH)tmp, how, ypos, xpos, 0 );
}

struct scr_stat
{
  int xsize;
  int ysize;
};

static int scrn_blanked;
static scr_stat *scp;

void __cdecl daemon_saver_step()
{
  static int txpos( 10 ),
             typos( 10 );
  static int txdir( -1 ),
             tydir( -1 );
  static int dxpos( 0 ),
             dypos( 0 );
  static int dxdir( 1 ),
             dydir( 1 );
  static int moved_daemon( 0 );
  static int xoff,
             yoff,
             toff;
  static int xlen,
             ylen,
             tlen;
  int min, max;

  static char m3[]( "The Brake!(tm) Mailer" ),
              m2[]( " - " );
  char *m1( current_time() ),
       *message( (char *)_alloca( strlen(m1)+(sizeof m2)+(sizeof m3) ) );

  strcpy( message, m1 );
  strcat( message, m2 );
  strcat( message, m3 );

  int messagelen( strlen( message ) );

  if( !scrn_blanked )
  {
    fillw( FG_LIGHTGREY|BG_BLACK, 0x20, 0, 0, scp->xsize * scp->ysize );
    xlen = ylen = tlen = 0;
    scrn_blanked = 1;
  }

  clear_daemon( dxpos, dypos, dxdir, xoff, yoff, xlen, ylen );
  clear_string( txpos, typos, toff, (char *)message, tlen );

  if( ++moved_daemon )
  {
                      /*
                       * The daemon picture may be off the screen, if
                       * screen size is chagened while the screen
                       * saver is inactive. Make sure the origin of
                       * the picture is between min and max.
                       */
    if( scp->xsize <= DAEMON_MAX_WIDTH )
    {
                      /*
                       * If the screen width is too narrow, we
                       * allow part of the picture go off
                       * the screen so that the daemon won't
                       * flip too often.
                       */
      min = scp->xsize - DAEMON_MAX_WIDTH - 10;
      max = 10;
    }
    else
    {
      min = 0;
      max = scp->xsize - DAEMON_MAX_WIDTH;
    }

    if( dxpos <= min )
    {
      dxpos = min;
      dxdir = 1;
    }
    else if( dxpos >= max )
    {
      dxpos = max;
      dxdir = -1;
    }

    if( scp->ysize <= DAEMON_MAX_HEIGHT )
    {
      min = scp->ysize - DAEMON_MAX_HEIGHT - 10;
      max = 10;
    }
    else
    {
      min = 0;
      max = scp->ysize - DAEMON_MAX_HEIGHT;
    }

    if( dypos <= min )
    {
      dypos = min;
      dydir = 1;
    }
    else if( dypos >= max )
    {
      dypos = max;
      dydir = -1;
    }

    moved_daemon = -1;
    dxpos += dxdir; dypos += dydir;

              // clip the picture

    xoff = 0;
    xlen = DAEMON_MAX_WIDTH;

    if( dxpos + xlen <= 0 )
      xlen = 0;
    else if( dxpos < 0 )
      xoff = -dxpos;

    if( dxpos >= scp->xsize )
      xlen = 0;
    else if( dxpos + xlen > scp->xsize )
      xlen = scp->xsize - dxpos;

    yoff = 0;
    ylen = DAEMON_MAX_HEIGHT;

    if( dypos + ylen <= 0 )
      ylen = 0;
    else if( dypos < 0 )
      yoff = -dypos;

    if( dypos >= scp->ysize )
      ylen = 0;
    else if( dypos + ylen > scp->ysize )
      ylen = scp->ysize - dypos;
  }

  if( scp->xsize <= messagelen )
  {
    min = scp->xsize - messagelen - 10;
    max = 10;
  }
  else
  {
    min = 0;
    max = scp->xsize - messagelen;
  }

  if( txpos <= min )
  {
    txpos = min;
    txdir = 1;
  }
  else if( txpos >= max )
  {
    txpos = max;
    txdir = -1;
  }

  if( typos <= 0 )
  {
    typos = 0;
    tydir = 1;
  }
  else if( typos >= scp->ysize - 1 )
  {
    typos = scp->ysize - 1;
    tydir = -1;
  }

  txpos += txdir;
  typos += tydir;

  toff = 0;
  tlen = messagelen;

  if( txpos + tlen <= 0 )
    tlen = 0;
  else if( txpos < 0 )
    toff = -txpos;

  if( txpos >= scp->xsize )
    tlen = 0;
  else if( txpos + tlen > scp->xsize )
    tlen = scp->xsize - txpos;

  draw_daemon( dxpos, dypos, dxdir, xoff, yoff, xlen, ylen );
  draw_string( txpos, typos, toff, (char *)message, tlen );
  DosSleep( 49 );
}

void __cdecl daemon_saver_load( scr_stat *sc )
{
  scrn_blanked = 0;
  scp = sc;
}

void __cdecl daemon_saver_unload()
{
  scrn_blanked = 0;
}

#ifdef TEST

static int kbhit( void )
{
  KBDKEYINFO k;
  APIRET16 ret = KbdPeek( &k, 0 );

  return ret ? 0 : k.fbStatus & KBDTRF_FINAL_CHAR_IN;;
}

void main()
{
  scr_stat scp = { 80, 25 };

  daemon_saver_load( &scp );

  while( !kbhit() )
  {
    daemon_saver_step();
    DosSleep( 1 );
  }

  daemon_saver_unload();
}

#endif