/* This file is part of cardwords
   (c) 1998 1999 Tobias Peters
   see file COPYING for the copyright terms.
   
   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 2 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, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// cardwords_char.cc

#include "cardwords_char.hh"
#include "cardwords_iostream.hh"
#include "cardwords_exceptions.hh"

#ifndef SPACE_STRING
#define SPACE_STRING " \n"
#endif

CardWords_Char::CardWords_CharNumber
CardWords_Char::nextFreeNumber = 0;

CardWords_Char::CardWords_CharNumber
CardWords_Char::cardwordsCharNumbers[CHAR_SET_LENGTH];

unsigned char  CardWords_Char::machineChars [CHAR_SET_LENGTH];
bool           CardWords_Char::definitionPossible = true;

CardWords_Char
CardWords_Char::define ( const string & mcp )
{
  assert (nextFreeNumber < CHAR_SET_LENGTH - 1);
  assert (mcp != "");
  assert (definitionPossible == true);

  if (definitionPossible == false) {
    return blanko;
  }
  size_t index;

  for (index = 0; index < mcp.length(); index++) {
    cardwordsCharNumbers[static_cast<unsigned char>(mcp[index])] =
      nextFreeNumber;
    // mcp[index] has to be converted to unsigned char since it could
    // be < 0 for characters > 127 ('\177') if char is equal to signed char.
  }
  machineChars[nextFreeNumber] = static_cast<unsigned char> (mcp[0]);

  ++nextFreeNumber;
  return CardWords_Char(mcp[0]);
}

// Discard all defined CardWords_Chars:
void
CardWords_Char::reset(void)
{
  definitionPossible = true;
  for (size_t index = 0; index < CHAR_SET_LENGTH; ++index) {
    cardwordsCharNumbers[index] = machineChars [index] = 0;
  }
  
  nextFreeNumber = 0;

  // redefine The blanko:
  CardWords_Char::blanko =
    CardWords_Char::define(SPACE_STRING);
}
  

void CardWords_Char::write_definitions_to_stream (ostream & ostr)
{
  ostr << noOfChars() - 1 /* w/o blanko */ << endl;
  size_t index1;
  unsigned char index2;
  for (index1 = 1; index1 < noOfChars(); ++index1) {
    ostr << machineChars[index1];
    // search for more representations of this CardWords_Char:
    for (index2 = 0; index2 < CHAR_SET_LENGTH - 1; ++index2) {
      if (cardwordsCharNumbers[index2] == index1
          && index2 != machineChars[index1]) {
        ostr << index2;
      }
    }
    ostr << endl;
  }
  ostr << endl;
}

void
CardWords_Char::define_from_file (const string & filename)
{
  ifstream stream (filename.c_str());
  if (stream.bad()) {
    throw CardWords_XAccessingFile(gettext("Charset definitions file"),
                                   filename);
  }
  define_from_stream(stream);
}

void CardWords_Char::define_from_stream (istream & stream) {
  size_t number_of_different_chars = 0, index;
  stream >> number_of_different_chars;
  if (stream.bad()) {
    throw CardWords_XBadInput(gettext("definitions stream for "\
                                      "CardWords_Chars"),
                              gettext("number"));
  }

  string str;
  
  for (index = 0;
       index < number_of_different_chars && stream.good();
       ++index) {
    stream >> str;
    define(str);
  }
  if (index < number_of_different_chars) {
    // The stream went bad too early:
    throw CardWords_XBadInput(gettext("definitions stream for "\
                                      "CardWords_Chars"),
                              gettext("another definitions string"));
  }
}

// How many different CardWords_Chars have been defined now?
// (After calling this method, no new CardWords_Char can be defined)
size_t
CardWords_Char::noOfChars (void)
{
  definitionPossible = false;
  return nextFreeNumber;
}

CardWords_Char  CardWords_Char::blanko =
                CardWords_Char::define(SPACE_STRING);
