/* 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_dictionary.cc

#include "cardwords_charcombination.hh"
#include "cardwords_dictionary.hh"
#include "cardwords_iostream.hh"
#include "cardwords_dicbot_cardtablerequirements.hh"
#include "cardwords_dicbot_bestordergenerator.hh"

// file cardwords_dictionary.cc

// add a dictionary

void
CardWords_Dictionary::insert_file (const string & filename)
                                                   
{
  ifstream dictfile (filename.c_str());

  if (! dictfile) {
    cerr << "File " << filename << " not found.\n" << flush;
  }

  CardWords_String word;
  while (dictfile) {
    dictfile >> word;
    if ( word.length() <= maxWordLength && word.length() >= minWordLength ) {
      insert_word (word);
    }
  }
}

// remove words in that file

void
CardWords_Dictionary::remove_file (const string & filename)
{
  ifstream dictfile(filename.c_str());

  if (! dictfile) {
    cerr << "File " << filename << " not found.\n" << flush;
  }

  CardWords_String word;
  while (dictfile) {
    dictfile >> word;
    if ( word.length() <= maxWordLength && word.length() >= minWordLength ) {
      erase_word (word);
    }
  }
}

// This function will save the result in a mamber variable. It will be valid
// until the next call to find_possible_words.



const vector<CardWords_CardSelection> *
CardWords_Dictionary::
find_possible_words (size_t min_word_length,
                     size_t max_word_length,
                     CardWords_DicBot_CardSelectionContents & tsc,
                     CardWords_DicBot_CardTableRequirements * reqp,
		     CardWords_DicBot_BestOrderGenerator * bogp
                     )
{
  CardWords_DicBot_CardTableRequirements & req = *reqp;
  CardWords_DicBot_BestOrderGenerator & bog = *bogp;
  
  founds.erase (founds.begin(), founds.end());

  if (max_word_length > maxWordLength) {
    max_word_length = maxWordLength;
  }
  if (min_word_length < minWordLength) {
    min_word_length = minWordLength;
  }

  size_t altLength;
  /* This check has already been performed before calling this func:
  // We will calculate an alternative min_word_length here and choose
  // the greater one in the end:
  size_t altLength = req.min_word_length();

  if (altLength == 0) {
    // return value 0 means _no_ word possible at all:
    return &founds;
  }
  if (altLength > min_word_length) {
    min_word_length = altLength;
  }
  */

  
  if (req.size() < max_word_length) {
    max_word_length = req.size();
  }
  // We will calculate an alternative max_word_length here and choose
  // the smaller one in the end:
  altLength = req.max_word_length(tsc.get_no_of_cards());

  if (altLength < max_word_length) {
    max_word_length = altLength;
  }

  if (min_word_length > max_word_length) {
    return &founds;
  }
  
  // This var means: Currently we're searching words of length currentLength
  size_t currentLength;

  // An iterator for the table requirements:
  CardWords_DicBot_CardTableRequirements::const_iterator req_iter;
  CardWords_DicBot_CardTableRequirements::const_iterator req_lurker;
  const CardWords_DicBot_CardTableRequirements::const_iterator req_end =
    req.end();
  
  // The hash value of only the musts. They are not contained in the hash_keys
  // in the charcombinations and need to be added. Only used if we have hash
  // keys in the charcombinations
  CardWords_FastHashKey musts_hash;

  // The hash value to be looked up in the hash table:
  CardWords_FastHashKey lookup_hash;

  // A pointer to words in question:
  const CardWords_String * quest_word = 0;

  // The number of wildcards not necessarily used by quest_word:
  int unused_wilds;
  
  // The estimated score of the word in question:
  int quest_score;

  // The best estimated score for the current Wordlength:
  int best_score;

  // How many chars from pool will actualy be used:
  size_t nrUsedChars = 0;

  // adding the musts inside the borders of a word with minWordLength to the
  // activeMusts:
  for (req_iter = req.begin(), currentLength = 1;
       currentLength < min_word_length;
       ++req_iter,++currentLength) {
    assert (req_iter != req_end);
    
    if ((*req_iter).is_must()) {
      musts_hash += (*req_iter).get_must();
    } else {
      ++nrUsedChars;
    }
  }

  // Count the must at pos [minwordlength -1] if there is one:
  if ((*req_iter).is_must()) {
    musts_hash += (*req_iter).get_must();
  }
  
  // Iterating over all possible wordlengths:
  // currentLength has now the value minWordLength and req_iter points to the
  // corresponding CardWords_DicBot_CardTableRequirement.
  // This for loop does not need an initialisation.
  for (assert(currentLength == min_word_length);
       currentLength <= max_word_length;
       ++currentLength, ++req_iter) {
    assert(req_iter != req_end);
    
    // lurker lurks one pos forward:
    req_lurker = req_iter; ++req_lurker;

    // Do we have to use a card here?
    if ((*req_iter).is_must() == false) {
      // Yes
      ++nrUsedChars;
    }
    if ( req_lurker != req_end && (*req_lurker).is_must()) {
      // This check means we can not have a word of length  (1-based)
      // currentLength, because there is already a char on the table at
      // position (0-based) currentLength. (The next position.)
      // We can use the knowledge that there is this must at position
      // currentlength by adding it now to the activeMusts. This must will be
      // the last char of a word with length = currentLength+1, if such a word
      // exists.
      musts_hash += (*req_iter).get_meaning();
      continue;
    }
    
    if (nrUsedChars == 0) {
      // this will be true when it is impossible to lay a word with length
      // currentLength because at every table-position is already a card.
      continue;
    }

    // set the best reached score to INT_MIN:
    best_score = INT_MIN;
    
    CardWords_HashTable<CardWords_String> & hash_table =
      dict [currentLength - minWordLength];
	
    // Test if we use the hash keys or search the whole dictionary:
    if (tsc.get_no_of_wildcards() < nrUsedChars && tsc.have_hash_keys())
      { // We are using the hash keys
	const CardWords_CharCombination<CardWords_FastHashKey>::value_type &
          combination =

	  (*tsc.get_charcomb())[nrUsedChars - 1];
	
	const size_t count = combination.size();
        size_t index;

	for ( index = 0; index < count; ++index) {
	  lookup_hash = combination[index];
	  lookup_hash += musts_hash; // as these will always be contained

	  while (( quest_word = hash_table.get_next( lookup_hash.hash() ) )
		 != 0) {
	    if (req.test_allowed(*quest_word) == true) {
              unused_wilds = tsc.remaining_wildcards(req.get_needed_cards());
              if (unused_wilds >= 0) {
                // unused_wilds >= 0 means: we can lay quest_words using the
                // cards we have. Otherwise, unused_wilds would be -1 to
                // indicate that we have too few wildcards to lay that word.
		req.new_length(currentLength);
		quest_score = bog(*quest_word, unused_wilds, tsc, req);
		if (quest_score > best_score) {
		  if (best_score == INT_MIN) {
		    // first word for this length:
		    founds.push_back(bog);
		  } else {
		    // not the first, replace earlier word:
		    founds.back() = bog;
		  }
		  best_score = quest_score;
		}
	      }
	    }
	  }
	}
      }
    else { //We are not using the hash keys: search the whole dictionary.
      hash_table.reset_global_next();
      while (( quest_word = hash_table.get_global_next() ) != 0) {
	if (req.test_allowed(*quest_word) == true) {
          unused_wilds = tsc.remaining_wildcards(req.get_needed_cards());
          if (unused_wilds >= 0) {
	    req.new_length(currentLength);
	    quest_score = bog(*quest_word, unused_wilds, tsc, req);
	    if (quest_score > best_score) {
	      if (best_score == INT_MIN) {
		// first word for this length:
		founds.push_back(bog);
	      } else {
		// not the first, replace earlier word:
		founds.back() = bog;
	      }
	      best_score = quest_score;
	    }
          }
        }
      }
    }
  }
  return &founds;
}
    
    

