/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2004  by Diether Knof and Borg Enders
 *
 *   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 can find this license in the file 'gpl.txt'.
 *
 *   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
 *
 *  Contact:
 *    Diether Knof dknof@gmx.de
 *    Borg Enders  borg@borgsoft.de
 *
 **********************************************************************/

#ifndef CLASS_RULE_HEADER
#define CLASS_RULE_HEADER

#include "../basistypes.h"
#include "../game/specialpoint.h"
#include "../card/card.h"

namespace COUNTING_NS {
  enum Counting {
    PLUS,
    MINUS,
    PLUSMINUS
  };
} // namespace COUNTING_NS 
using COUNTING_NS::Counting;

/**
 **
 ** @brief      controls the rules of the game/party
 **
 ** @author     Diether Knof
 **
 **/
class Rule {
  public:
    static unsigned const MIN_NUMBER_OF_THROWING_NINES;
    static unsigned const MIN_NUMBER_OF_THROWING_KINGS;
    static unsigned const MAX_NUMBER_OF_POVERTY_TRUMPS;
  public:
    enum TypeBool {
      FIRST,
      BOOL_FIRST = FIRST,

      // if it is show, if a card is valid
      // default: yes
      SHOW_IF_VALID = BOOL_FIRST,
      // if the hands of the other players can be seen
      // default: no
      SHOW_ALL_HANDS,
      // the number of tricks in the trickpiles are shown
      // default: yes
      SHOW_TRICKS_IN_TRICKPILES,
      // if the points of the trickpiles are shown
      // default: yes
      SHOW_TRICKPILES_POINTS,
      // whether the (known) teams are shown in the game (the team icons)
      // default: yes
      SHOW_KNOWN_TEAMS_IN_GAME,
      // whether the soloplayer is shown in the game
      // (that is the team icon like the gametype in solo games,
      // the marriage- and poverty icon an so on)
      // default: yes
      SHOW_SOLOPLAYER_IN_GAME,

      // if the player can get a hint for the card to be played
      // default: yes
      CARD_HINT,
      // if the player can take back the last trick
      // default: no
      TAKE_BACK_TRICKS,

      // with or without 9
      // default: yes
      WITH_NINES,
      // throw with five 9
      // default: yes
      THROW_WITH_FIVE_NINES,
      // throw with six kings
      // default: no
      THROW_WITH_SIX_KINGS,

      // poverty (<= 3 trump)
      // default: yes
      POVERTY,
      // to shift the trump (or give the cards again)
      // default: yes
      POVERTY_SHIFT,
      // to shift only the trump
      // default: no
      POVERTY_SHIFT_ONLY_TRUMP,
      // to throw with at max one trump
      // default: no
      THROW_WITH_ONE_TRUMP,

      // to throw when the diamond ace is the highest trump
      // default: no
      THROW_WHEN_FOX_HIGHEST_TRUMP,

      // whether you can make the announcement
      // default: yes
      MARRIAGE_FIRST_FOREIGN,
      // whether you can make the announcement
      // default: yes
      MARRIAGE_FIRST_COLOR,
      // whether you can make the announcement
      // default: yes
      MARRIAGE_FIRST_SINGLE_COLOR,
      // whether you can make the announcement
      // default: yes
      MARRIAGE_FIRST_TRUMP,

      // if a player has both diamond kings, he can decide, with whom he builds
      // a team, when he is playing the first diamond king
      // default: no
      GENSCHER,

      // if the solo games are allowed
      // default: yes
      SOLO,
      // default: yes
      SOLO_JACK,
      // default: yes
      SOLO_QUEEN,
      // default: yes
      SOLO_KING,
      // default: yes
      SOLO_QUEEN_JACK,
      // default: yes
      SOLO_KING_JACK,
      // default: yes
      SOLO_KING_QUEEN,
      // default: yes
      SOLO_KOEHLER,
      // default: yes
      SOLO_COLOR,
      // default: yes
      SOLO_MEATLESS,

      // the hearts tens are trump
      // default: yes
      DOLLEN,
      // in the same trick is the second hearts ten is greater than the first
      // default: yes
      DOLLEN_SECOND_OVER_FIRST,
      // ... but not in the last trick
      // default: no
      DOLLEN_CONTRARY_IN_LAST_TRICK,

      // both diamond ace are the highest
      // default: yes
      SWINES,
      // when to make the announcement
      // (start of the game, or when the first is played)
      // dafault: yes
      SWINES_ANNOUNCEMENT_BEGIN,
      // when the first diamond ace is home, the second becomes a Schwein
      // default: no
      SWINE_ONLY_SECOND,
      // when there are swines in a solo
      // default: yes
      SWINES_IN_SOLO,
      // both diamond nine are above the Schweine
      // default: no
      HYPERSWINES,
      // when to make the announcement
      // (start of the game, or when the first is played)
      // default: yes
      HYPERSWINES_ANNOUNCEMENT_BEGIN,
      // when there are hyperswines in a solo
      // default: no
      HYPERSWINES_IN_SOLO,

      // catching the diamond ace of the other team: +1 point
      // default: yes
      EXTRAPOINT_CATCH_FOX,
      // the fox gets the last trick: +1 point
      // default: no
      EXTRAPOINT_FOX_LAST_TRICK,
      // the double_fox gets the last trick: +1 points
      // (+ the point from 'fox_last_trick')
      // default: no
      EXTRAPOINT_DOUBLE_FOX_LAST_TRICK,

      // club jack gets the last trick: +1 point
      // default: yes
      EXTRAPOINT_CHARLIE,
      // club jack of the other team is catched in the last trick: +1 point
      // default: yes
      EXTRAPOINT_CATCH_CHARLIE,
      // both club jack of the same team get the last trick: +2 point
      // default: no
      EXTRAPOINT_DOUBLE_CHARLIE,
      // both club jack of the same team is catched in the last trick: +2 point
      // default: no
      EXTRAPOINT_CATCH_DOUBLE_CHARLIE,

      // the second dolle jabs the first
      // default: no
      EXTRAPOINT_DOLLE_JABS_DOLLE,
      // if there is a heart trick
      // default: no
      EXTRAPOINT_HEART_TRICK,

      // if the winner of the first trick has to make an announcement,
      // when the trick has at least thirty points
      ANNOUNCEMENT_FIRST_TRICK_THIRTY_POINTS,
      // whether you can reply with re/contra to an announcement
      // default: true
      ANNOUNCEMENT_REPLY,

      // whether the announcement re/contra doubles the points
      // default: true
      ANNOUNCEMENT_RE_DOUBLES,

      // whether the numbe of rounds is limited
      // default: true
      NUMBER_OF_ROUNDS_LIMITED,
      // whether the points (for the tournament) are limited
      // default: false
      POINTS_LIMITED,

      // whether the soloplayer of a lust-solo plays the first card
      // default: true
      LUSTSOLO_PLAYER_LEADS,

      BOOL_LAST = LUSTSOLO_PLAYER_LEADS
    }; // enum TypeBool
    static unsigned const BOOL_NUMBER;

    enum TypeUnsigned {
      UNSIGNED_FIRST = BOOL_LAST + 1,

      // the number of tricks, that are allowed to be seen
      // default: 1
      SHOW_TRICKS_NUMBER = UNSIGNED_FIRST,

      // the number of tricks, after which you have to play alone
      // if you have not found a partner
      MARRIAGE_DETERMINATION,

      // announcements:
      // -1 for not allowed,
      // 1-12: as long as you have at least so many cards on the hand
      // default: 10
      ANNOUNCEMENT_NO_120,
      // default: 9
      ANNOUNCEMENT_NO_90,
      // default: 6
      ANNOUNCEMENT_NO_60,
      // default: 3
      ANNOUNCEMENT_NO_30,
      // default: 3
      ANNOUNCEMENT_NO_0,
      // the last time, an announcement can be made
      // default: 3
      ANNOUNCEMENT_LAST,

      // the number of rounds to play
      // default: 20
      NUMBER_OF_ROUNDS,

      // the number of points in a tournament
      // default: 40
      POINTS,
      // the number of duty soli
      // default: 0
      NUMBER_OF_DUTY_SOLI,
      // the number of duty color soli
      // default: 0
      NUMBER_OF_DUTY_COLOR_SOLI,
      // the number of duty picture soli
      // default: 0
      NUMBER_OF_DUTY_PICTURE_SOLI,

      // number of players
      // default: 4
      NUMBER_OF_PLAYERS,

      // number of players in a game
      // default: 4
      NUMBER_OF_PLAYERS_IN_GAME,

      UNSIGNED_LAST = NUMBER_OF_PLAYERS_IN_GAME,
    }; // enum TypeUnsigned
    static unsigned const UNSIGNED_NUMBER;

    // these values are calculated, not set
    enum TypeUnsignedExtra {
      UNSIGNED_EXTRA_FIRST = UNSIGNED_LAST + 1,

      // number of cards colors, values and cards
      // how often a card is in the game
      NUMBER_OF_SAME_CARDS = UNSIGNED_EXTRA_FIRST,
      // the number of colors
      NUMBER_OF_CARD_COLORS,
      // the number of colors, with trump as an extra color
      NUMBER_OF_CARD_TCOLORS,
      // the number of card values
      NUMBER_OF_CARD_VALUES,
      // the number of cards (each (color, value) exists two times)
      NUMBER_OF_CARDS,

      // the number of teams (two)
      NUMBER_OF_TEAMS,
      // the number of players in a team
      // (NUMBER_OF_PLAYERS_IN_GAME / NUMBER_OF_TEAMS)
      NUMBER_OF_PLAYERS_PER_TEAM,

      MAX_NUMBER_OF_TRICKS_IN_GAME,
      // NUMBER_OF_CARDS / NUMBER_OF_PLAYERS
      NUMBER_OF_TRICKS_IN_GAME,
      NUMBER_OF_CARDS_ON_HAND = NUMBER_OF_TRICKS_IN_GAME,

      UNSIGNED_EXTRA_LAST = NUMBER_OF_TRICKS_IN_GAME
    }; // enum TypeUnsignedExtra

    enum TypeCounting {
      COUNTING = UNSIGNED_EXTRA_LAST + 1,
      LAST = COUNTING
    }; // enum TypeCounting

    // how to sum the points
    // default: plus
    // ***
    // PARTY_POINTS_SUMMING

  public:
    Rule();
    Rule(Rule const&);

    Rule& operator=(Rule const&);

    // returns, whether the rule is activ or not (or the value)
    bool operator()(TypeBool const& type) const;
    unsigned operator()(TypeUnsigned const& type) const;
    unsigned operator()(TypeUnsignedExtra const& type) const;
    Counting operator()(TypeCounting const& type) const;

    // returns, whether the gametype can be selected
    bool operator()(GameType const& game_type) const;
    // returns, whether the marriage selector can be selected
    bool operator()(MarriageSelector const& marriage_selector) const;
    // returns, whether the specialpoint is valid
    bool operator()(Specialpoint const& special_point) const;
    // returns the number of remaining tricks for the announcement
    unsigned remaining_tricks(Announcement const& announcement) const;

    // returns, whether the dependencies of the rule are met
    bool dependencies(TypeBool const& type) const;
    bool dependencies(TypeUnsigned const& type) const;

    // returns the setting of the rule
    bool value(TypeBool const& type) const;
    unsigned value(TypeUnsigned const& type) const;
    Counting value(TypeCounting const& type) const;
    Counting const& counting() const;

    // the minimum and maximum value
    unsigned min(TypeUnsigned const& type) const;
    unsigned max(TypeUnsigned const& type) const;

    // all valid cards
    vector<Card::Color> const& valid_card_colors() const;
    vector<Card::Value> const& valid_card_values() const;
    vector<Card> const& valid_cards() const;

    // sets the rule
    void set(TypeBool const& type, bool const& value);
    void set(TypeBool const& type, string const& value);
    void set(TypeUnsigned const& type, unsigned const& value);
    void set(TypeUnsigned const& type, string const& value);
    void set(TypeCounting const& type, Counting const& value);
    void set(TypeCounting const& type, string const& value);

    bool set(string const& type, string const& value);

    // load the rules
    bool load(string const& file);
    bool read(istream& istr);
    // save the rules in the given file
    bool save(string const& file) const;

    // output of the rules
    void write(ostream& ostr) const;

  private:
    vector<bool> bool_p;
    vector<unsigned> unsigned_p;
    Counting counting_;

}; // class Rule

// conversion in a string
string name(Rule::TypeBool const& type);
string name(Rule::TypeUnsigned const& type);
string name(Rule::TypeUnsignedExtra const& type);
string name(Rule::TypeCounting const& type);
string name(Counting const& counting);

// output of the name
ostream& operator<<(ostream& ostr, Rule::TypeBool const& type);
ostream& operator<<(ostream& ostr, Rule::TypeUnsigned const& type);
ostream& operator<<(ostream& ostr, Rule::TypeUnsignedExtra const& type);
ostream& operator<<(ostream& ostr, Rule::TypeCounting const& type);
ostream& operator<<(ostream& ostr, Counting const& counting);

// output of the Rule
ostream& operator<<(ostream& ostr, Rule const& rule);

// comparison of two rules (value)
bool operator==(Rule const& rule1, Rule const& rule2);
bool operator!=(Rule const& rule1, Rule const& rule2);

#endif // #ifndef CLASS_RULE_HEADER
