#include <fstream>
#include <itk/bit_count.h>
#include <ocr/dictionary.h>


#ifndef DO_OCR_INLINE
#include <ocr/dictionary.inl>
#endif


Dictionary::Dictionary()
{
}


bool
Dictionary::load( const char *filename)
{
  unsigned char buf[ 5];
  Dict_Key      key;
  Dict_Token    token;

  std::ifstream is( filename);
  if (!is) {
    return false;
  }

/*
  while (is.read( buf, 5)) {
    key   = (buf[ 0] << 24)
          | (buf[ 1] << 16)
          | (buf[ 2] <<  8)
          | (buf[ 3] <<  0);
    token = buf[ 4];
*/
  while (is >> token >> key) {
    add_key( key, token);
  }

  return true;
}


bool
Dictionary::save( const char *filename) const
{
  unsigned char buf[ 5];
  Dict_Key      key;
  Dict_Token    token;

  std::ofstream os( filename);
  if (!os) {
    return false;
  }

  Dict_Search::const_iterator i = m_search.begin();
  while (i != m_search.end()) {
    key   = i->key;
    token = i->token;

/*
    buf[ 0] = (key >> 24) & 0xff;
    buf[ 1] = (key >> 16) & 0xff;
    buf[ 2] = (key >>  8) & 0xff;
    buf[ 3] = (key >>  0) & 0xff;
    buf[ 4] = token;
    if (!os.write( buf, 5)) {
      return false;
    }
*/
    os << token << " " << key << "\n";
    ++i;
  }
  return true;
}


void
Dictionary::add_key( const Dict_Key &key, const Dict_Token &token)
{
  if (m_lookup.find( key) == m_lookup.end()) {
    m_lookup[ key] = token;
    m_search.push_back( Dict_Key_Token( key, token));
  }
}


bool
Dictionary::find_key( const Dict_Key &key, Dict_Token &token)
{
  {
    Dict_Lookup::const_iterator i = m_lookup.find( key);
    if (i != m_lookup.end()) {
      token = i->second;
      return true;
    }
  }

  {
    Dict_Search::const_iterator i = m_search.begin();
    unsigned best_count = 32;
    unsigned count;
    unsigned bit_diff;

    while (i != m_search.end()) {
      bit_diff = i->key ^ key;
      count = bit_count( reinterpret_cast<unsigned char *>( &bit_diff), 32);
      if (count < best_count) {
        token      = i->token;
        best_count = count;
      }
      ++i;
    }
    if (best_count <= 2) {
      add_key( key, token);
      return true;
    }
  }
  return false;
}
