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

#include "cardwords_dicbot_bestordergenerator.hh"

int
CardWords_DicBot_BestOrderGenerator::operator ()
(const CardWords_String &word, // The word to lay
 size_t remaining_wildcards, //the number of wildcards not necessarily used.
 const CardWords_DicBot_CardSelectionContents & tsc,
 const CardWords_DicBot_CardTableRequirements & req)
{
  erase(begin(), end());

  // When this function returns, most of the vector creator will be copied to
  // *this.
  vector<CardWords_CardDescription> creator(word.length(),
                                          CardWords_CardDescription
                                          (CardWords_Char::blanko,
                                           CardWords_Char::blanko,
                                           INT_MIN) // mark invalid
                                          );
  const vector<pair<int, size_t> >& aim_factors_indices =
    req.get_aim_factors_indices();

  // These vectors contain information about the cards that have been chosen
  // to be used. Index is the internal number of the CardWords_Char,
  // accessible by CardWords_Char::internal_no(). Cards counted by
  // used_high_cards will be placed on fields with an aim factor >= 0 and
  // will start with the cards with the highest points.
  // With used_low_cards it's otherwayround.
  vector<size_t> used_high_cards (CardWords_Char::noOfChars(),0);
  vector<size_t> used_low_cards (CardWords_Char::noOfChars(),0);

  // first req-index and count of wildcards:
  pair<size_t /*first_index*/, size_t /*no*/> w_ind_and_no =
    tsc.get_index_and_no_of(CardWords_Char::blanko);
  // first req-index and count of current character:
  pair<size_t /*first_index*/, size_t /*no*/> c_ind_and_no;
  
  size_t high_aim_factors_count = req.get_high_aim_factors_count();
  size_t low_aim_factors_count = req.get_low_aim_factors_count();

  (null_is_good ? high_aim_factors_count : low_aim_factors_count) +=
    req.get_zero_aim_factors_count();

  // save the score here:
  int score = 0;
  
  size_t index_i;
  size_t word_index;
  bool   use_wild;

  // Choose the Cards for the high factors (high means >= 0):
  size_t lower_bound =  aim_factors_indices.size() - high_aim_factors_count;
  size_t upper_bound = aim_factors_indices.size(); // prepare for underflow
  for (index_i = upper_bound - 1;
       index_i >= lower_bound && index_i < upper_bound;
       --index_i) {
    word_index = aim_factors_indices[index_i].second;
    c_ind_and_no = tsc.get_index_and_no_of(word[word_index]);

    // choose: wildcard || normal card:
    if (w_ind_and_no.second ==
        used_high_cards[CardWords_Char::blanko.internal_no()]) {
      use_wild = false;
    } else {
      if (c_ind_and_no.second ==
          used_high_cards[word[word_index].internal_no()]) {
        use_wild = true;
      }
      else {
        if (remaining_wildcards == 0
            || never_use_wildcards_when_unnecessary) {
          use_wild = false;
        }
        else {
          int wild_score =
            tsc.get_points_of(w_ind_and_no.first + w_ind_and_no.second
                              - used_high_cards[CardWords_Char::
                                               blanko.internal_no()]
                              - 1);
          int c_score =
            tsc.get_points_of(c_ind_and_no.first + c_ind_and_no.second
                              - used_high_cards[word[word_index]
                                               .internal_no()]
                              -1);
          if (wild_score > c_score ||
              (prefer_wildcards_when_same_score && wild_score == c_score)) {
            use_wild = true;
            --remaining_wildcards;
          }
          else {
            use_wild = false;
          }
        }
      }
    }
    // use_wild has now been set to true || false;
    if (use_wild) {
      score += aim_factors_indices[index_i].first *
        tsc.get_points_of(w_ind_and_no.first + w_ind_and_no.second
                          - used_high_cards[CardWords_Char::
                                           blanko.internal_no()]
                          - 1);
      creator[word_index] =
        tsc[w_ind_and_no.first + w_ind_and_no.second
           - used_high_cards[CardWords_Char::blanko.internal_no()]
           - 1];
      // set the meaning of the wildcard:
      creator[word_index].set_meaning(word[word_index]);
      
      ++used_high_cards[CardWords_Char::blanko.internal_no()];
    }
    else {
      score += aim_factors_indices[index_i].first *
        tsc.get_points_of(c_ind_and_no.first + c_ind_and_no.second
                          - used_high_cards[word[word_index].internal_no()]
                          -1);
      creator[word_index] =
        tsc[c_ind_and_no.first + c_ind_and_no.second
           - used_high_cards[word[word_index].internal_no()]
           -1];
      ++used_high_cards[word[word_index].internal_no()];
    }
  }


  // now the same for the low aim factors:

  // Choose the Cards for the low factors (high means <= 0):
  for (index_i = 0; index_i < low_aim_factors_count; ++index_i) {
    word_index = aim_factors_indices[index_i].second;
    c_ind_and_no = tsc.get_index_and_no_of(word[word_index]);

    // choose: wildcard || normal card:
    if (w_ind_and_no.second ==
        used_high_cards[CardWords_Char::blanko.internal_no()]
        + used_low_cards[CardWords_Char::blanko.internal_no()]) {
      use_wild = false;
    } else {
      if (tsc.get_no_of(word[word_index]) ==
          used_high_cards[word[word_index].internal_no()]
          + used_low_cards[word[word_index].internal_no()]) {
        use_wild = true;
      }
      else {
        if (remaining_wildcards == 0
            || never_use_wildcards_when_unnecessary) {
          use_wild = false;
        }
        else {
          int wild_score =
            tsc.get_points_of(w_ind_and_no.first 
                              + used_low_cards[CardWords_Char
                                              ::blanko.internal_no()]
                              );
          int c_score =
            tsc.get_points_of(c_ind_and_no.first
                              + used_low_cards[word[word_index].internal_no()]
                              );
          if (wild_score < c_score ||
              (prefer_wildcards_when_same_score && wild_score == c_score)) {
            use_wild = true;
            --remaining_wildcards;
          }
          else {
            use_wild = false;
          }
        }
      }
    }
    // use_wild has now been set to true || false;
    if (use_wild) {
      score += aim_factors_indices[index_i].first *
        tsc.get_points_of(w_ind_and_no.first
                          + used_low_cards[CardWords_Char
                                          ::blanko.internal_no()]
                          );
      creator[word_index] =
        tsc[w_ind_and_no.first
           + used_low_cards[CardWords_Char::blanko.internal_no()] ];
           
      // set the meaning of the wildcard:
      creator[word_index].set_meaning(word[word_index]);

      ++used_low_cards[CardWords_Char::blanko.internal_no()];
    }
    else {
      score += aim_factors_indices[index_i].first *
        tsc.get_points_of(c_ind_and_no.first
                          + used_low_cards[word[word_index].internal_no()]
                          );
      creator[word_index] =
        tsc[c_ind_and_no.first
           + used_low_cards[word[word_index].internal_no()] ];
      ++used_low_cards[word[word_index].internal_no()];
    }
  }

  // copy the creator to *this:
  vector<CardWords_CardDescription>::const_iterator ci = creator.begin();
  vector<CardWords_CardDescription>::const_iterator ce = creator.end();
  for(;ci != ce; ++ci) {
    if ((*ci).get_points() != INT_MIN) {
      push_back(*ci);
    }
  }
  
  return score;
}
