/***************************************************************************
 liblcd                                                                1.1.0
 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, int type )
{
  char    fnname[ ] = "lcd_open";
  termios newtio;
  int     rc;

  lcd_type = type;
  if( lcd_type < FRSTTYPE || lcd_type > LASTTYPE )
    return lcd_err( ELCDTYPE, fnname );

  /* currently only 2400 and 9600 baud are supported */
  if( speed != B2400 && speed != B9600 )
    return lcd_err( ELCDBAUD, fnname );

  /* users of seetron.com "LCD Backpack"-style displays *
   * are having trouble with 2400 baud, so we will      *
   * temporarily disable it for backpack displays       */
  /* contrib: rjune */
  if( lcd_type == BACKPACK && 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[ 5 ] = "\0";
  char localbuf[ 2 ] = "\0\0";
  int  rc;

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

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

  if( lcd_type == TERMINAL )
  {
    /* For displays with terminal-like protocols: *
     * ASCII 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 );
    }
  }
  else if( lcd_type == BACKPACK )
  {
    /* contrib: rjune */
    /* modified slightly from original sources by nathana */
    if( ( initopts & CLEARSCR ) == CLEARSCR )
      sprintf( lcdinitstr, "%c%c%c%c", 254, 1, 254, 128 );
    if( ( ( initopts & BACKLIGHT ) == BACKLIGHT ) ||
        ( ( initopts & INVERSETXT ) == INVERSETXT ) )
      /* just print error & don't exit function...non-fatal */
      lcd_err( ELCDUNSP, fnname );
  }

  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[ 5 ];

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

  if( lcd_type == TERMINAL )
    sprintf( code, "%c", 12 );
  /* contrib: rjune */
  /* modified slightly from original sources by nathana */
  else if( lcd_type == BACKPACK )
    sprintf( code, "%c%c%c%c", 254, 1, 254, 128 );

  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( lcd_type == TERMINAL )
  {
    if( x < 0 || x > 19 || y < 0 || y > 3 )
      return lcd_err( ELCDVOOB, fnname );

    pos = x + ( y * 20 );
    sprintf( code, "%c%d ", 16, pos );
  }
  /* contrib: rjune */
  else if( lcd_type == BACKPACK )
  {
    if( x < 0 || x > 39 || y < 0 || y > 1 )
      return lcd_err( ELCDVOOB, fnname );

    pos = x + ( y * 40 );
    sprintf( code, "%c%c", 254, 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 );
  /* looks like the backlit backpacks (like BPI-216L) don't have soft on/off */
  if( lcd_type == BACKPACK )
    return lcd_err( ELCDUNSP, 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( lcd_type == BACKPACK )
    return lcd_err( ELCDUNSP, 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( lcd_type == BACKPACK )
    return lcd_err( ELCDUNSP, 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( lcd_type == BACKPACK )
    return lcd_err( ELCDUNSP, 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( lcd_type == BACKPACK )
    return lcd_err( ELCDUNSP, 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( lcd_type == BACKPACK )
    return lcd_err( ELCDUNSP, 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( lcd_type == BACKPACK )
    return lcd_err( ELCDUNSP, 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( lcd_type == BACKPACK )
    return lcd_err( ELCDUNSP, 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( lcd_type == BACKPACK )
    return lcd_err( ELCDUNSP, fnname );
  if( row < 0 || row > 15 )
    return lcd_err( ELCDVOOB, fnname );

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

  lcd_write( code );

  return 0;
}
