/* 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_cardtablecell.hh

/* Classes containing _DicBot_ and files containing _dicbot_ indicate
   classes and files for dicbot. dicbot will be the dictionary and
   robot process started by the cardwords server.

   The server delegates the following tasks to dicbot:

   - Check if a move is valid. This means:
   . * Check if there is enough space on the card-table to actually lay the
   .   cards.
   . * Check if all words created by that move are valid. This can be
   .   disabled.
   - Calculate the score of a move. This contains a validity check as above.
   - Calculate the best possible move for a given set of cards. The best
   . possible move means the one that gets the most points.
   . This contains several score calculations as above.
*/

#ifndef CARDWORDS_DICBOT_CARDTABLECELL_HH
#define CARDWORDS_DICBOT_CARDTABLECELL_HH

// This file defines The class CardWords_DicBot_CardTableCell.
// This class contains all information dicbot wants to have about a
// special cell of the card-table.

#include "cardwords_carddescription.hh"
#include "cardwords_direction.hh"
#include "cardwords_dicbot_cardtablemustnot.hh"
#include "cardwords_exceptions.hh"
#include "config.hh"

class CardWords_DicBot_CardTableCell {
public:
  // everything is modifyable through accessing functions
  CardWords_DicBot_CardTableCell ();

  // Each cell remembers if it is possible to start or end a word here, de-
  // pending on the direction. E.g. it is not possible to start a horizontal
  // word on a cell whose left neighbour carries already a card.
  bool is_startPossible (CardWords_Direction) const;
  bool is_endPossible (CardWords_Direction) const;

  // Because a cell has no clue what happens with its neighbours, this info
  // has to come from the outside:
  void set_startPossible (CardWords_Direction, bool = true);
  void unset_startPossible (CardWords_Direction);
  void set_endPossible (CardWords_Direction, bool = true);
  void unset_endPossible (CardWords_Direction);

  // if this cell carries a card:
  bool get_containsCard(void) const;

  // use remove_card and change_card instead of this:
  //  void set_containsCard(bool);

  // return the CardDescription:
  const CardWords_CardDescription *
  get_card(void) const;

  // remove the card:
  void remove_card(void);

  // change the card, also used for adding:
  void change_card (const CardWords_CardDescription &);

  // get and set the letter and word factors:
  int get_letter_factor(void) const;
  int get_word_factor  (void) const;
  int get_cell_points  (void) const;
  bool is_cell_blocked (void) const;
  
  void set_letter_factor(int);
  void set_word_factor  (int);
  void set_cell_points  (int);
  void set_cell_blocked (bool);
  
  // Methods to modify the must_not information:
  // The direction parameter that every method takes is equivalent to the
  // direction of the words that must not contain these must_not characters.
  CardWords_DicBot_CardTableMustNot *
  get_must_not(CardWords_Direction);
  const CardWords_DicBot_CardTableMustNot *
  get_must_not(CardWords_Direction) const;

  // Check if the must_not allows char c, or if there is a card in this
  // cell, if that card's meaning is the same as c:
  inline bool test_allowed(CardWords_Char c, CardWords_Direction) const;

  // Add a forbidden char to the must nots:
  void add_must_not(CardWords_Char c, CardWords_Direction);

  // Clear all must nots because of a new card-table situation that has an
  // impact on this field:
  void clear_must_nots(CardWords_Direction);
private:
  bool startPossible[2];
  bool endPossible[2];

  bool containsCard;

  CardWords_CardDescription card;

  int letter_factor;
  int word_factor;
  int cell_points;
  bool cell_blocked;
  
  CardWords_DicBot_CardTableMustNot * mustNot[2];
};

// everything is modifyable through accessing functions
inline
CardWords_DicBot_CardTableCell::CardWords_DicBot_CardTableCell()
  : card()
{
  startPossible[0] = true;
  startPossible[1] = 
    endPossible[0] = 
    endPossible[1] = true;

  containsCard = false;

  letter_factor = DEFAULT_LETTER_FACTOR;
  word_factor   = DEFAULT_WORD_FACTOR;
  cell_points   = DEFAULT_CELL_POINTS;
  cell_blocked  = false;
  
  mustNot[horizontal] = mustNot[vertikal] = 0;
}

// Each cell remembers if it is possible to start or end a word here, de-
// pending on the direction. E.g. it is not possible to start a horizontal
// word on a cell whose left neighbour carries already a card.
inline
bool
CardWords_DicBot_CardTableCell::
is_startPossible(CardWords_Direction direction) const
{
  return startPossible[int(direction)];
}

inline
bool
CardWords_DicBot_CardTableCell::
is_endPossible(CardWords_Direction direction) const
{
  return endPossible[int(direction)];
}

// Because a cell has no clue what happens with its neighbours, this info
// has to come from the outside:
inline
void
CardWords_DicBot_CardTableCell::
set_startPossible(CardWords_Direction direction, bool b = true)
{
  startPossible[int(direction)] = b;
}

inline
void
CardWords_DicBot_CardTableCell::
unset_startPossible(CardWords_Direction direction)
{
  startPossible[int(direction)] = false;
}

inline
void
CardWords_DicBot_CardTableCell::set_endPossible(CardWords_Direction direction,
                                                bool b = true)
{
  endPossible[int(direction)] = b;
}

inline
void
CardWords_DicBot_CardTableCell::
unset_endPossible(CardWords_Direction direction)
{
  endPossible[int(direction)] = false;
}

// if this cell carries a card:
inline
bool
CardWords_DicBot_CardTableCell::get_containsCard(void) const
{
  return containsCard;
}

// return the CardDescription:
inline
const CardWords_CardDescription *
CardWords_DicBot_CardTableCell::get_card(void) const
{
  return containsCard ? (& card) : (const CardWords_CardDescription *) 0;
}

// remove the card:
inline
void CardWords_DicBot_CardTableCell::remove_card(void)
{
  containsCard = false;

}

// change the card, also used for adding:
inline
void CardWords_DicBot_CardTableCell::
change_card(const CardWords_CardDescription & description)
{
  containsCard = true;
  card = description;
}

// get and set the letter and word factors:
inline
int
CardWords_DicBot_CardTableCell::get_letter_factor(void) const
{
  return letter_factor;
}

inline
int
CardWords_DicBot_CardTableCell::get_word_factor(void) const
{
  return word_factor;
}
inline
int
CardWords_DicBot_CardTableCell::get_cell_points  (void) const
{
  return cell_points;
}
inline
bool
CardWords_DicBot_CardTableCell::is_cell_blocked (void) const
{
  return cell_blocked;
}

inline
void
CardWords_DicBot_CardTableCell::set_letter_factor(int v)
{
  letter_factor = v;
}
inline
void
CardWords_DicBot_CardTableCell::set_word_factor(int v)
{
  word_factor = v;
}
inline
void
CardWords_DicBot_CardTableCell::set_cell_points(int v)
{
  cell_points = v;
}
inline
void
CardWords_DicBot_CardTableCell::set_cell_blocked (bool b)
{
  cell_blocked = b;
}

// Methods to modify the must_not information:
// The direction parameter that every method takes is equivalent to the
// direction of the words that must not contain these must_not characters.
inline
CardWords_DicBot_CardTableMustNot *
CardWords_DicBot_CardTableCell::get_must_not(CardWords_Direction dir)
{
  return mustNot[dir];
}

inline
const CardWords_DicBot_CardTableMustNot *
CardWords_DicBot_CardTableCell::get_must_not(CardWords_Direction dir) const
{
  return mustNot[dir];
}

// Check if the must_not allows char c, or if there is a card in this
// cell, if that card's meaning is the same as c:
inline bool
CardWords_DicBot_CardTableCell::
test_allowed(CardWords_Char c, CardWords_Direction dir) const
{
  if (get_containsCard() == true) {
    return c == get_card()->get_meaning();
  }
  if (mustNot[dir] == 0) {
    return true;
  }
  return mustNot[dir]->test_allowed(c);
}

// Add a forbidden char to the must nots:
inline void
CardWords_DicBot_CardTableCell::
add_must_not(CardWords_Char c, CardWords_Direction dir)
{
  assert (get_containsCard() == false);
  if (mustNot[dir] == 0) {
    mustNot[dir] = new CardWords_DicBot_CardTableMustNot();
    if (mustNot[dir] == 0) {
      throw CardWords_XLowResources(gettext("In CardWords_DicBot_"\
                                            "CardTableCell::"\
                                            "add_must_not: "\
                                            "Not enough memory "\
                                            "for CardWords_DIcBot_"\
                                            "TableMustNot"));
    }
  }
  mustNot[dir]->insert(c);
}

// Clear all must nots because of a new card-table situation that has an
// impact on this field:
inline void
CardWords_DicBot_CardTableCell::clear_must_nots(CardWords_Direction dir)
{
  if (mustNot[dir] != 0) {
    mustNot[dir]->clear();
  }
}

#endif
