/* This file is part of cardwords
   (c) 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_actionselection.hh

#ifndef CARDWORDS_ACTIONSELECTION_HH
#define CARDWORDS_ACTIONSELECTION_HH

/* The class CardWords_ActionSelection is a tool for the clients:
   It takes single chars representing key strikes in an input
   method and forms them to a user request. This Request is either
   a move, a trade or a pass.
*/

#include "cardwords_hand.hh"
#include "cardwords_cardtable.hh"
#include <map>

class CardWords_ActionSelection : public CardWords_CardContainer1Dim {
public:
  // take_key_strike() returns an int.
  // It returns 0 if everything went normal.
  // It returns 1 if the user finished his request.
  // It returns -1 if the user made an invalid key strike.
  int
  take_key_strike(char);

  // reset() causes the current user interaction process to be terminated,
  // eg because the server assumes that the player wants to pass because of
  // a timeout.
  void
  reset(void);
  
  CardWords_ActionSelection(CardWords_Hand *, // The hand from where to get
                            // the cards. The hand will be modified, but will
                            // be restored when reset() is called.
                            const CardWords_CardTable *, // With this pointer,
                            //   invalid geometric moves will be detected.

                            bool use_digits_for_the_columns,
                            // and chars for the rows, otherwise the other way
                            // round
                          
                            bool column_indices_grow_rightwards,
                            // if true, the rightmost column has the greatest
                            // index
                          
                            bool row_indices_grow_upwards);
  
  // These functions are for access to CardWords_ActionSelection for GUIs.
  // This way they don't have to bother about the current state the object
  // is in.
  void
  change_start_location(CardWords_CardTableLocation);

  void
  change_direction(CardWords_Direction);
  void
  toggle_direction(void);

  void
  make_trade(void); // Deletes the start location.


  // info methods. They do always work, not only after the input finished:
  // If there is no move or trade, thez return 0. If both return 0, the
  // current action is a pass.
  const CardWords_Move *
  get_move(void) const;

  const CardWords_CardSelection *
  get_trade(void) const;

  // Will sometimes return a start location even if get_move returns 0. This
  // is the case if the user seems to want to make a move, but the move does
  // not fit on the table at the current start location
  const CardWords_CardTableLocation *
  get_start_location(void) const;

  // Will sometimes return a direction even if get_move returns 0. This
  // is the case if the user seems to want to make a move, but the move does
  // not fit on the table at the current start location
  const CardWords_Direction *
  get_move_direction(void) const;
  
  // print*() returns the location of the cursor. -1 means cursor invisible.
  int
  print(ostream &) const;
  int
  print_user_start_coordinates(ostream &) const; // depending on direction

  virtual void
  add_card(CardWords_Card *);

  virtual void
  remove_card(CardWords_Card *);

  // returns the actual card that is described in (*get_move())[n] or
  // (*get_trade())[n]:
  CardWords_Card *
  get_card(size_t n) const;
  
private:

  void
  set_start_location_from_user_coordinates(void);

  CardWords_Hand * hand;

  const CardWords_CardTable * table;
  
  enum Input_State {
    TABLE_LOCATION_EXPECTED,
    TABLE_LOCATION_CHAR_GIVEN_NUMBER_EXPECTED,
    TABLE_LOCATION_CHAR_GIVEN_NUMBER_MAYBE_NOT_FINISHED_YET,
    TABLE_LOCATION_NUMBER_GIVEN_CHAR_EXPECTED,
    TABLE_LOCATION_NUMBER_BEGUN_MAYBE_NOT_FINISHED_YET,

    CARD_INPUT_MODE,
    WILDCARD_MEANING_SPECIFICATION_NEEDED,
    CARD_POINTS_NEEDED,
    CARD_POINTS_NOT_FINISHED_YET,
    CARD_POINTS_MAYBE_NOT_FINISHED_YET,
    WILDCARD_POINTS_NEEDED,
    WILDCARD_POINTS_NOT_FINISHED_YET,
    WILDCARD_POINTS_MAYBE_NOT_FINISHED_YET,
  } input_state;

  bool is_move;
  CardWords_Move move;

  bool is_trade;
  CardWords_CardSelection cards;

  bool user_wants_move;
  
  CardWords_CardTableLocation start_location;
  CardWords_Direction     move_direction;

  size_t user_digit_coordinate;
  char user_char_coordinate;
  
  bool use_digits_for_the_columns;
  bool column_indices_grow_rightwards;
  bool row_indices_grow_upwards;
  
  char last_char;
  CardWords_Char card_meaning;
  int          card_points;
  int          absolute_card_points;
  bool         card_points_negative;

  // After this class was basically working with take_key_strike()-calls, I
  // thought that it would be difficult using this class for GUI interaction
  // with take_key_strike() as the only interaction method.
  // The existing CardWords_CardContainer1Dim::add_card() and
  // CardWords_CardContainer1Dim::remove_card() would be more convenient.
  // There must however be indications wether the currend add or remove was
  // initiated by the take_key_strike() method which cares to update the
  // internal data itself; or if it comes from outside, in which case the
  // cards, move, is_move, is_trade, input_state, card_meaning, card_points
  // and maybe other data members need to be updated from within the
  // add_card() or remove_card() methods.
  // The indication that the call comes from take_key_strike() is stored here:
  bool initiated_add_myself;
  bool initiated_remove_myself;

  // I also add a map that binds a specific card to a specific position. Since
  // CardWords_ActionSelection is a CardWords_CardContainer1Dim and therefor a
  // set, removing the second L from a move creating the word FILL might
  // really remove the L-card that was added first to this container.
  // This is almost always irrelevant, and I implemented the
  // CardWords_CardContainer1Dim as a set because of this irrelevancy.
  // It may however become slightly relevant inside a GUI where the user moves
  // a card from the hand to the actionselectionarea and wants that to be
  // undone afterwards, and the card does not move back to the place where it
  // came from but to the original place of the other L-card, because the GUI
  // checks where the card with this cardpointer was last.
  // This map helps to remove the right card in this case:
  map<CardWords_Card *, size_t> card_pos;

  // If the outside world wants to add a wildcard directly, this will work
  // only if the meaning has already been set inside the card. If this is not
  // possible or desirable with the UI, the wildcard that is to be added
  // can be kept in memory and is added when the meaning is specified via a
  // take_key_strike call. This data member holds the selected wildcard or 0:
  CardWords_Card * selected_wildcard;
};

inline
ostream &
operator << (ostream & ostr, const CardWords_ActionSelection & as)
{
  as.print(ostr);
  return ostr;
}
#endif
