/*  GNU Moe - My Own Editor
    Copyright (C) 2005, 2006, 2007, 2008, 2009 Antonio Diaz Diaz.

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

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cctype>
#include <string>

#include "iso_8859.h"


namespace ISO_8859 {

enum {
     CSCARON = 0xA6,	// latin capital letter s with caron
     SSCARON = 0xA8,	// latin small letter s with caron
     CZCARON = 0xB4,	// latin capital letter z with caron
     SZCARON = 0xB8,	// latin small letter z with caron
     CAGRAVE = 0xC0,	// latin capital letter a with grave
     CAACUTE = 0xC1,	// latin capital letter a with acute
     CACIRCU = 0xC2,	// latin capital letter a with circumflex
     CATILDE = 0xC3,	// latin capital letter a with tilde
     CADIAER = 0xC4,	// latin capital letter a with diaeresis
     CARING  = 0xC5,	// latin capital letter a with ring above
     CCCEDI  = 0xC7,	// latin capital letter c with cedilla
     CEGRAVE = 0xC8,	// latin capital letter e with grave
     CEACUTE = 0xC9,	// latin capital letter e with acute
     CECIRCU = 0xCA,	// latin capital letter e with circumflex
     CEDIAER = 0xCB,	// latin capital letter e with diaeresis
     CIGRAVE = 0xCC,	// latin capital letter i with grave
     CIACUTE = 0xCD,	// latin capital letter i with acute
     CICIRCU = 0xCE,	// latin capital letter i with circumflex
     CIDIAER = 0xCF,	// latin capital letter i with diaeresis
     CNTILDE = 0xD1,	// latin capital letter n with tilde
     COGRAVE = 0xD2,	// latin capital letter o with grave
     COACUTE = 0xD3,	// latin capital letter o with acute
     COCIRCU = 0xD4,	// latin capital letter o with circumflex
     COTILDE = 0xD5,	// latin capital letter o with tilde
     CODIAER = 0xD6,	// latin capital letter o with diaeresis
     COSTROK = 0xD8,	// latin capital letter o with stroke
     CUGRAVE = 0xD9,	// latin capital letter u with grave
     CUACUTE = 0xDA,	// latin capital letter u with acute
     CUCIRCU = 0xDB,	// latin capital letter u with circumflex
     CUDIAER = 0xDC,	// latin capital letter u with diaeresis
     CYACUTE = 0xDD,	// latin capital letter y with acute
     SAGRAVE = 0xE0,	// latin small letter a with grave
     SAACUTE = 0xE1,	// latin small letter a with acute
     SACIRCU = 0xE2,	// latin small letter a with circumflex
     SATILDE = 0xE3,	// latin small letter a with tilde
     SADIAER = 0xE4,	// latin small letter a with diaeresis
     SARING  = 0xE5,	// latin small letter a with ring above
     SCCEDI  = 0xE7,	// latin small letter c with cedilla
     SEGRAVE = 0xE8,	// latin small letter e with grave
     SEACUTE = 0xE9,	// latin small letter e with acute
     SECIRCU = 0xEA,	// latin small letter e with circumflex
     SEDIAER = 0xEB,	// latin small letter e with diaeresis
     SIGRAVE = 0xEC,	// latin small letter i with grave
     SIACUTE = 0xED,	// latin small letter i with acute
     SICIRCU = 0xEE,	// latin small letter i with circumflex
     SIDIAER = 0xEF,	// latin small letter i with diaeresis
     SNTILDE = 0xF1,	// latin small letter n with tilde
     SOGRAVE = 0xF2,	// latin small letter o with grave
     SOACUTE = 0xF3,	// latin small letter o with acute
     SOCIRCU = 0xF4,	// latin small letter o with circumflex
     SOTILDE = 0xF5,	// latin small letter o with tilde
     SODIAER = 0xF6,	// latin small letter o with diaeresis
     SOSTROK = 0xF8,	// latin small letter o with stroke
     SUGRAVE = 0xF9,	// latin small letter u with grave
     SUACUTE = 0xFA,	// latin small letter u with acute
     SUCIRCU = 0xFB,	// latin small letter u with circumflex
     SUDIAER = 0xFC,	// latin small letter u with diaeresis
     SYACUTE = 0xFD,	// latin small letter y with acute
     SYDIAER = 0xFF	// latin small letter y with diaeresis
     };

} // end namespace ISO_8859


// 'seq[i]' begins a escape sequence (the characters following a '\').
// Returns the corresponding code and, in *lenp, the characters read.
// Returns -1 if error.
//
int ISO_8859::escape( const std::string & seq, const unsigned int i, int *lenp ) throw()
  {
  if( i >= seq.size() ) return -1;
  int len = 1;
  unsigned char ch = seq[i];

  switch( ch )
    {
    case 'a': ch = '\a'; break;
    case 'b': ch = '\b'; break;
    case 'e': ch = 27; break;
    case 'f': ch = '\f'; break;
    case 'n': ch = '\n'; break;
    case 'r': ch = '\r'; break;
    case 't': ch = '\t'; break;
    case 'v': ch = '\v'; break;
    case '0':
    case '1':
    case '2':
    case '3': if( i + 2 >= seq.size() || !ISO_8859::isodigit( seq[i+1] ) ||
                  !ISO_8859::isodigit( seq[i+2] ) ) return -1;
              ch = ((ch-'0') << 6) + ((seq[i+1]-'0') << 3) + (seq[i+2]-'0');
              len = 3;
              break;
    case 'x':
    case 'X': if( i + 2 >= seq.size() || ISO_8859::xtoi( seq[i+1] ) < 0 ||
                  ISO_8859::xtoi( seq[i+2] ) < 0 ) return -1;
              ch = (ISO_8859::xtoi( seq[i+1] ) << 4) + ISO_8859::xtoi( seq[i+2] );
              len = 3;
              break;
    }
  if( lenp ) *lenp = len;
  return ch;
  }


void ISO_8859::escapize( std::string & s ) throw()
  {
  for( unsigned int i = 0; i < s.size(); ++i )
    {
    if( !ISO_8859::iscntrl( s[i] ) && s[i] != '\\' ) continue;
    s.insert( i, 1, '\\' ); ++i;
    switch( s[i] )
      {
      case '\\': break;
      case '\a': s[i] = 'a'; break;
      case '\b': s[i] = 'b'; break;
      case '\f': s[i] = 'f'; break;
      case '\n': s[i] = 'n'; break;
      case '\r': s[i] = 'r'; break;
      case '\t': s[i] = 't'; break;
      case '\v': s[i] = 'v'; break;
      default:
        {
        s.insert( i, 1, 'x' ); ++i;
        const unsigned char ch = s[i];
        s.insert( i, 1, ISO_8859::xdigit( ch >> 4 ) ); ++i;
        s[i] = ISO_8859::xdigit( ch & 0x0f );
        }
      }
    }
  }


void ISO_8859::deescapize( std::string & s ) throw()
  {
  for( unsigned int i = 0; i + 1 < s.size(); ++i )
    {
    if( s[i] != '\\' ) continue;
    int len, c = ISO_8859::escape( s, i + 1, &len );
    if( c < 0 ) continue;
    const unsigned char ch = s[i+1];
    if( ( ch != c && ch != '\\' ) || len > 1 )
      { s[i] = c; s.erase( i + 1, len ); }
    else ++i;
    }
  }


bool ISO_8859::islower( const unsigned char ch ) throw()
  {
  if( ch < 128 ) return std::islower( ch );
  switch( ch )
    {
    case SSCARON:
    case SZCARON:
    case SAGRAVE:
    case SAACUTE:
    case SACIRCU:
    case SATILDE:
    case SADIAER:
    case SARING :
    case SCCEDI :
    case SEGRAVE:
    case SEACUTE:
    case SECIRCU:
    case SEDIAER:
    case SIGRAVE:
    case SIACUTE:
    case SICIRCU:
    case SIDIAER:
    case SNTILDE:
    case SOGRAVE:
    case SOACUTE:
    case SOCIRCU:
    case SOTILDE:
    case SODIAER:
    case SOSTROK:
    case SUGRAVE:
    case SUACUTE:
    case SUCIRCU:
    case SUDIAER:
    case SYACUTE:
    case SYDIAER: return true;
    default:      return false;
    }
  }


bool ISO_8859::isupper( const unsigned char ch ) throw()
  {
  if( ch < 128 ) return std::isupper( ch );
  switch( ch )
    {
    case CSCARON:
    case CZCARON:
    case CAGRAVE:
    case CAACUTE:
    case CACIRCU:
    case CATILDE:
    case CADIAER:
    case CARING :
    case CCCEDI :
    case CEGRAVE:
    case CEACUTE:
    case CECIRCU:
    case CEDIAER:
    case CIGRAVE:
    case CIACUTE:
    case CICIRCU:
    case CIDIAER:
    case CNTILDE:
    case COGRAVE:
    case COACUTE:
    case COCIRCU:
    case COTILDE:
    case CODIAER:
    case COSTROK:
    case CUGRAVE:
    case CUACUTE:
    case CUCIRCU:
    case CUDIAER:
    case CYACUTE: return true;
    default:      return false;
    }
  }


bool ISO_8859::isalpha_( const unsigned char ch ) throw()
  {
  return ( ch == '_' || ISO_8859::islower( ch ) || ISO_8859::isupper( ch ) );
  }


bool ISO_8859::isalnum_( const unsigned char ch ) throw()
  {
  return ( ( ch < 128 && std::isdigit( ch ) ) || ISO_8859::isalpha_( ch ) );
  }


bool ISO_8859::isodigit( const unsigned char ch ) throw()	// tests if 'ch' is a octal digit
  {
  switch( ch )
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7': return true;
    default: return false;
    }
  }


const char * ISO_8859::control_name( const unsigned char ch ) throw()  // '^A' --> "^A"
  {
  static char buf[3];
  if( ch < 32 ) { buf[0] = '^'; buf[1] = ch + '@'; buf[2] = 0; }
  else buf[0] = 0;
  return buf;
  }


int ISO_8859::controlize( int code ) throw()	// converts 'A' or 'a' into '^A'
  {
  if( code >= 0 && code < 128 )
    {
    if( std::isupper( code ) ) code = code - 'A' + 1;
    else if( std::islower( code ) ) code = code - 'a' + 1;
    }
  return code;
  }


int ISO_8859::decontrolize( int code ) throw()	// converts '^A' or 'a' into 'A'
  {
  if( code >= 0 && code < 128 )
    { if( code < 32 ) code += '@'; else code = std::toupper( code ); }
  return code;
  }


unsigned char ISO_8859::tolower( const unsigned char ch ) throw()
  {
  if( ch < 128 ) return std::tolower( ch );
  switch( ch )
    {
    case CSCARON: return SSCARON;
    case CZCARON: return SZCARON;
    case CAGRAVE: return SAGRAVE;
    case CAACUTE: return SAACUTE;
    case CACIRCU: return SACIRCU;
    case CATILDE: return SATILDE;
    case CADIAER: return SADIAER;
    case CARING : return SARING;
    case CCCEDI : return SCCEDI;
    case CEGRAVE: return SEGRAVE;
    case CEACUTE: return SEACUTE;
    case CECIRCU: return SECIRCU;
    case CEDIAER: return SEDIAER;
    case CIGRAVE: return SIGRAVE;
    case CIACUTE: return SIACUTE;
    case CICIRCU: return SICIRCU;
    case CIDIAER: return SIDIAER;
    case CNTILDE: return SNTILDE;
    case COGRAVE: return SOGRAVE;
    case COACUTE: return SOACUTE;
    case COCIRCU: return SOCIRCU;
    case COTILDE: return SOTILDE;
    case CODIAER: return SODIAER;
    case COSTROK: return SOSTROK;
    case CUGRAVE: return SUGRAVE;
    case CUACUTE: return SUACUTE;
    case CUCIRCU: return SUCIRCU;
    case CUDIAER: return SUDIAER;
    case CYACUTE: return SYACUTE;
    default     : return ch;
    }
  }


unsigned char ISO_8859::toupper( const unsigned char ch ) throw()
  {
  if( ch < 128 ) return std::toupper( ch );
  switch( ch )
    {
    case SSCARON: return CSCARON;
    case SZCARON: return CZCARON;
    case SAGRAVE: return CAGRAVE;
    case SAACUTE: return CAACUTE;
    case SACIRCU: return CACIRCU;
    case SATILDE: return CATILDE;
    case SADIAER: return CADIAER;
    case SARING : return CARING;
    case SCCEDI : return CCCEDI;
    case SEGRAVE: return CEGRAVE;
    case SEACUTE: return CEACUTE;
    case SECIRCU: return CECIRCU;
    case SEDIAER: return CEDIAER;
    case SIGRAVE: return CIGRAVE;
    case SIACUTE: return CIACUTE;
    case SICIRCU: return CICIRCU;
    case SIDIAER: return CIDIAER;
    case SNTILDE: return CNTILDE;
    case SOGRAVE: return COGRAVE;
    case SOACUTE: return COACUTE;
    case SOCIRCU: return COCIRCU;
    case SOTILDE: return COTILDE;
    case SODIAER: return CODIAER;
    case SOSTROK: return COSTROK;
    case SUGRAVE: return CUGRAVE;
    case SUACUTE: return CUACUTE;
    case SUCIRCU: return CUCIRCU;
    case SUDIAER: return CUDIAER;
    case SYACUTE: return CYACUTE;
    default     : return ch;
    }
  }


unsigned char ISO_8859::xdigit( const int value ) throw()
  {
  if( value >= 0 && value <= 9 ) return '0' + value;
  if( value >= 10 && value <= 15 ) return 'A' + value - 10;
  return 0;
  }


int ISO_8859::xtoi( const unsigned char ch ) throw()
  {
  switch( ch )
    {
    case '0': return 0;
    case '1': return 1;
    case '2': return 2;
    case '3': return 3;
    case '4': return 4;
    case '5': return 5;
    case '6': return 6;
    case '7': return 7;
    case '8': return 8;
    case '9': return 9;
    case 'a':
    case 'A': return 0x0A;
    case 'b':
    case 'B': return 0x0B;
    case 'c':
    case 'C': return 0x0C;
    case 'd':
    case 'D': return 0x0D;
    case 'e':
    case 'E': return 0x0E;
    case 'f':
    case 'F': return 0x0F;
    default: return -1;
    }
  }
