/***************************************************************************
 liblcd                                                                1.0.3
 Copyright 1999, 2000 by Nathan Anderson, ALL RIGHTS RESERVED

 This library is free software; you can redistribute it and/or modify it
 under the terms of the GNU Library General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 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.  See the GNU Library General Public
 License for more details.

 You should have received a copy of the GNU Library General Public License
 along with this library (in a file called "COPYING"); if not, visit
 <http://www.fsf.org/> or write to:

    Free Software Foundation, Inc.
    59 Temple Place, Suite 330
    Boston, MA  02111-1307  USA

 ***************************************************************************

 Please see README for information on how to use this library.

      -- Nathan Anderson

 ***************************************************************************/

#define   LIBLCD_C
#include "liblcd.h"

int lcd_open( const char * device, const speed_t speed )
{
  char    fnname[ ] = "lcd_open";
  termios newtio;
  int     rc;

  if( speed != B2400 && speed != B9600 )
    return lcd_err( ELCDBAUD, fnname );

  rc = open( device, O_WRONLY | O_NOCTTY );
  if( rc < 0 )
  {
    perror( device );
    return lcd_err( ELCDOPEN, fnname );
  }
  fd_com = rc;

  tcgetattr( fd_com, &oldtio );
  bzero( &newtio, sizeof( newtio ) );
  newtio.c_cflag = speed | CS8 | CLOCAL;
  newtio.c_oflag = 0;
  tcflush( fd_com, TCOFLUSH );
  tcsetattr( fd_com, TCSANOW, &newtio );

  status = SLCDOPEN;

  return 0;
}

int liblcd_init( char initopts )
{
  char fnname[ ] = "lcd_init";
  char lcdinitstr[ 4 ] = "\0";
  char localbuf[ 2 ] = "\0\0";
  int  rc;

  if( status < SLCDOPEN )
    return lcd_err( ELCDNOPN, fnname );

  memset( buf, '\0', BUFSIZE );

  /* 14 == turn on backlight,
     15 == turn off backlight,
      2 == inverse color,
      3 == normal color,
     12 == clear LCD screen   */

  if( ( initopts & BACKLIGHT ) == BACKLIGHT )
    localbuf[ 0 ] = 14;
  else
    localbuf[ 0 ] = 15;
  strcat( lcdinitstr, ( const char * ) localbuf );
  if( ( initopts & INVERSETXT ) == INVERSETXT )
    localbuf[ 0 ] = 2;
  else
    localbuf[ 0 ] = 3;
  strcat( lcdinitstr, ( const char * ) localbuf );
  if( ( initopts & CLEARSCR ) == CLEARSCR )
  {
    localbuf[ 0 ] = 12;
    strcat( lcdinitstr, ( const char * ) localbuf );
  }

  rc = ( int ) write( fd_com, lcdinitstr, strlen( lcdinitstr ) );
  if( rc < 0 )
    return lcd_err( ELCDINIT, fnname );

  status = SLCDINIT;

  return 0;
}

int lcd_reopen( const char * device )
{
  char fnname[ ] = "lcd_reopen";
  int  rc;

  rc = open( device, O_WRONLY | O_NOCTTY );
  if( rc < 0 )
  {
    perror( device );
    return lcd_err( ELCDOPEN, fnname );
  }
  fd_com = rc;

  tcgetattr( fd_com, &oldtio );

  status = SLCDINIT;

  return 0;
}

int lcd_close( )
{
  char fnname[ ] = "lcd_close";

  if( status < SLCDOPEN )
    return lcd_err( ELCDNOPN, fnname );

  tcsetattr( fd_com, TCSAFLUSH, &oldtio );
  close( fd_com );

  status = 0;

  return 0;
}

int lcd_err( int errcode, const char * callingfn )
{
  if( errcode > NUM_ERR )
    errcode = NUM_ERR + 1; /* false error */

  fprintf( stderr, "%s( ): %s.\n", callingfn, err_str[ errcode - 1 ] );

  return errcode;
}

int lcd_clear( )
{
  char fnname[ ] = "lcd_clear";
  char code[ 2 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );

  sprintf( code, "%c", 12 );
  lcd_write( code );

  return 0;
}

int lcd_gotoxy( int x, int y )
{
  char fnname[ ] = "lcd_gotoxy";
  char code[ 5 ];
  int  pos;

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( x > 19 || y > 3 )
    return lcd_err( ELCDVOOB, fnname );

  pos = x + ( y * 20 );
  sprintf( code, "%c%d ", 16, pos );
  lcd_write( code );

  return 0;
}

int lcd_write( const char * str )
{
  char   fnname[ ] = "lcd_write";
  char * localbuf;
  int    buffree;

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );

  buffree = BUFSIZE - strlen( buf );

  if( strlen( str ) <= buffree )
    strcat( buf, str );
  else
  {
    int leftover;

    leftover = strlen( str ) - buffree;
    localbuf = ( char * ) malloc( leftover * sizeof( char ) );
    strcpy( localbuf, str + ( strlen( str ) - leftover ) );
    strncat( buf, str, buffree );
    lcd_flush( );
    lcd_write( localbuf );
    free( localbuf );
  }

  return 0;
}

int lcd_flush( )
{
  char fnname[ ] = "lcd_flush";
  int  rc;

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );

  rc = ( int ) write( fd_com, buf, strlen( buf ) );
  if( rc < 0 )
    return lcd_err( ELCDWRIT, fnname );

  memset( buf, '\0', BUFSIZE );

  return 0;
}

int lcd_backlight( int state )
{
  char fnname[ ] = "lcd_backlight";
  char code[ 2 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( state < 0 || state > 1 )
    return lcd_err( ELCDVOOB, fnname );

  if( state == ON )
    sprintf( code, "%c", 14 );
  else
    sprintf( code, "%c", 15 );
  lcd_write( code );

  return 0;
}

int lcd_inversetxt( int state )
{
  char fnname[ ] = "lcd_inversetxt";
  char code[ 2 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( state < 0 || state > 1 )
    return lcd_err( ELCDVOOB, fnname );

  if( state == ON )
    sprintf( code, "%c", 2 );
  else
    sprintf( code, "%c", 3 );
  lcd_write( code );

  return 0;
}

int lcd_textsize( int size )
{
  char fnname[ ] = "lcd_textsize";
  char code[ 5 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( size < 0 || size > 3 )
    return lcd_err( ELCDVOOB, fnname );

  sprintf( code, "%cF%d ", 27, size );
  lcd_write( code );

  return 0;
}

int lcd_g_setcolor( int color )
{
  char fnname[ ] = "lcd_g_setcolor";
  char code[ 5 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( color != BLACK && color != WHITE )
    return lcd_err( ELCDVOOB, fnname );

  sprintf( code, "%cI%d ", 27, color );
  lcd_write( code );

  return 0;
}

int lcd_g_loadimg( int rompage )
{
  char fnname[ ] = "lcd_g_loadimg";
  char code[ 5 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( rompage < 0 || rompage > 7 )
    return lcd_err( ELCDVOOB, fnname );

  sprintf( code, "%cE%d ", 27, rompage );
  lcd_write( code );

  return 0;
}

int lcd_g_drawline( int x1, int y1, int x2, int y2 )
{
  char fnname[ ] = "lcd_g_drawline";
  char code[ 17 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( ( x1 < 0 || x1 > 119 ) || ( x2 < 0 || x2 > 119 ) ||
      ( y1 < 0 || y1 > 31 )  || ( y2 < 0 || y2 > 31 ) )
    return lcd_err( ELCDVOOB, fnname );

  sprintf( code, "%cL%d %d %d %d ", 27, x1, y1, x2, y2 );
  lcd_write( code );

  return 0;
}

int lcd_g_drawbox( int x1, int y1, int x2, int y2 )
{
  char fnname[ ] = "lcd_g_drawbox";
  char code[ 17 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( ( x1 < 0 || x1 > 119 ) || ( x2 < 0 || x2 > 119 ) ||
      ( y1 < 0 || y1 > 31 )  || ( y2 < 0 || y2 > 31 ) )
    return lcd_err( ELCDVOOB, fnname );

  sprintf( code, "%cL%d %d %d %d ", 27, x1, y1, x2, y1 );
  lcd_write( code );
  sprintf( code, "%cL%d %d %d %d ", 27, x2, y1, x2, y2 );
  lcd_write( code );
  sprintf( code, "%cL%d %d %d %d ", 27, x2, y2, x1, y2 );
  lcd_write( code );
  sprintf( code, "%cL%d %d %d %d ", 27, x1, y2, x1, y1 );
  lcd_write( code );

  return 0;
}

int lcd_g_plotdot( int x, int y )
{
  char fnname[ ] = "lcd_g_plotdot";
  char code[ 10 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( ( x < 0 || x > 119 ) || ( y < 0 || y > 31 ) )
    return lcd_err( ELCDVOOB, fnname );

  sprintf( code, "%cP%d %d ", 27, x, y );
  lcd_write( code );

  return 0;
}

int lcd_g_revline( int row )
{
  char fnname[ ] = "lcd_g_revline";
  char code[ 6 ];

  if( status < SLCDINIT )
    return lcd_err( ELCDNINI, fnname );
  if( row < 0 || row > 15 )
    return lcd_err( ELCDVOOB, fnname );

  sprintf( code, "%cR%d ", 27, row );
  lcd_write( code );

  return 0;
}
