/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2006  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_AICONFIG_HEADER
#define CLASS_AICONFIG_HEADER

#include "../card/card.h"
#include "heuristicsmap.h"
#include "rating/rating.h"

#include "../misc/translator.h"

#include <map>

namespace AITYPE {
  enum AiType {
    AITYPE_FIRST = 0,

    NO_CHOOSEBESTCARD = AITYPE_FIRST,
    RANDOM,
    VIRTUAL_GAMES,
    GAMETREE,
    GAMETREE_WITH_HEURISTICS,
    GAMETREE_FOR_TEAM,
    MONTE_CARLO,

    AITYPE_LAST = MONTE_CARLO
  }; // enum AiType
  extern unsigned const NUMBER;
} // namespace AITYPE
using AITYPE::AiType;
extern AiType aitype;

string name(const AiType& aitype);
WRITE_NAME2(AITYPE, AiType)


class Aiconfig {
  public:
    enum Difficulty {
      DIFFICULTY_FIRST = 0,

      CUSTOM = DIFFICULTY_FIRST,
      NOVICE,
      STANDARD_DEFENSIVE,
      STANDARD_OFFENSIVE,
      PROFI,
      PROFI_UNFAIR,

      DIFFICULTY_LAST = PROFI_UNFAIR
    }; // enum Difficulty

    enum Heuristic {
      HEURISTIC_FIRST,

      NO_HEURISTIC = HEURISTIC_FIRST,
      MANUAL,
      BUG_REPORT,
      NETWORK,
      ONLY_VALID_CARD,
      PLAY_TO_MARRY,           // Heuristics::play_to_marry
      CHOOSE_ACE,              // Heuristics::choose_ace
      CHOOSE_FOR_COLOR_TRICK,  // Heuristics::choose_for_color_trick
      RETRY_COLOR,
      SERVE_COLOR_TRICK,
      SERVE_TRUMP_TRICK,
      CHOOSE_PFUND,            // Heuristics::choose_pfund 
      CHOOSE_PFUND_POVERTY,    // Heuristics::choose_pfund_poverty
      JAB_FOR_ACE,             // Heuristics::jab_for-ace
      CREATE_FEHL,             // Heuristics::create_fehl
      BEST_WINNING,            // Heuristics::best_winning_card
      LOW_HIGH,                // Heuristics::play_low_high    
      PLAY_FOR_TEAM,           // Heuristics::play_for_team
      JAB_FOX,                 // Heuristics::jab_fox
      TRY_FOR_DOPPELKOPF,                 // Heuristics::try_for_doppelkopf
      DRAW_TRUMP,             
      GET_TRICK_FOR_ANNOUNCEMENT,

      HEURISTIC_POVERTY_FIRST,
      POVERTY_SPECIAL_PLAY_PFUND = HEURISTIC_POVERTY_FIRST,
      POVERTY_SPECIAL_GIVE_NO_POINTS,
      POVERTY_RE_TRUMP_COLOR_TRICK_HIGH,
      POVERTY_RE_PLAY_TRUMP,
      POVERTY_CONTRA_PLAY_COLOR,
      POVERTY_CONTRA_TRUMP_COLOR_TRICK_HIGH,
      POVERTY_LEAVE_TO_PARTNER,
      POVERTY_OVERJAB_RE,
      POVERTY_BEST_WINNING_CARD,
      HEURISTIC_POVERTY_LAST = POVERTY_BEST_WINNING_CARD,

      MEATLESS_PLAYHIGHESTCOLOR,        // Meatless: Heuristics::play_highest_color
      PICTURE_SECONDBESTTRUMP,

      COLOR_CHOOSE_ACE,
      COLOR_BEST_WINNING,
      COLOR_JAB_FOR_ACE,
      COLOR_LOW_HIGH,

      CHOOSE_BEST_CARD, // for the database

      HEURISTIC_LAST = CHOOSE_BEST_CARD
    }; // enum Heuristic
    static unsigned const HEURISTIC_NUMBER;
    static Heuristic Heuristic_from_name(string const& name);
    static bool is_real(Heuristic const h);
    static bool is_general(Heuristic const h);
    static bool is_poverty(Heuristic const h);
    static bool is_solo(Heuristic const h);
    static bool is_solo_color(Heuristic const h);
    static bool is_solo_picture(Heuristic const h);
    static bool is_solo_meatless(Heuristic const h);

    struct HeuristicState {
      HeuristicState(Heuristic const heuristic, bool const active) :
	heuristic(heuristic), active(active) { }

      Heuristic heuristic;
      bool active;
    }; // struct HeuristicState

    enum TypeBool {
      BOOL_FIRST,

      TRUSTING = BOOL_FIRST, // whether the ai is trusting

      FAIRPLAYHANDS, // whether the ai takes the current hands of the players
      FAIRPLAYTEAMS, // whether the ai knows the team of the other players

      FEHLCREATIONONFIRSTCARD,

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

    enum TypeUnsigned {
      UNSIGNED_FIRST = BOOL_LAST + 1,

      REMEMBERTRICKS = UNSIGNED_FIRST, // how many tricks are remembered

      LIMIT_THROW_FEHL, // maximum points for playing a fehl
      LIMITQUEEN, // mininum points per trick for jabbing with a queen
      LIMITDOLLE, // minimum points per trick for jabbing with dollen or higher

      LASTFEHLCREATION, // last trick to create a fehl color
      LASTHEURISTICTRICK,
      FIRST_TRICK_FOR_TRUMP_POINTS_OPTIMIZATION,

      ANNOUNCELIMIT, // points of hand for first announcement 
      ANNOUNCELIMITDEC, // points of hand you can lose between two announcements
      ANNOUNCECONFIG, // Parameter for Playerconfiguration of announcements
      ANNOUNCELIMITREPLY, // points of hand for first Reply
      ANNOUNCECONFIGREPLY, // Decrement for each announcment made, when replying 

      TAKEPOVERTY,

      SINGLESOLO, // minimum cardvalue for a solo with only one picture trump
      DOUBLESOLO, // minimum cardvalue for a solo with two pictures trump
      TRIPLESOLO, // minimum cardvalue for a solo with three pictures trump
      COLORSOLO, // minimun cardvalue for playing a colorsolo
      MEATLESS, // minimun cardvalue for playing a fleischlosen

      UNSIGNED_LAST = MEATLESS
    }; // enum TypeUnsigned
    static unsigned const UNSIGNED_NUMBER;

    enum TypeCard {
      CARD_FIRST = UNSIGNED_LAST + 1,

      LIMITTHROWING = CARD_FIRST, // minimum team mate card for not over jabbing

      TRUMPLIMIT_SOLOCOLOR,
      TRUMPLIMIT_SOLOJACK,
      TRUMPLIMIT_SOLOQUEEN,
      TRUMPLIMIT_SOLOKING,
      TRUMPLIMIT_SOLOJACKKING,
      TRUMPLIMIT_SOLOJACKQUEEN,
      TRUMPLIMIT_SOLOQUEENKING,
      TRUMPLIMIT_SOLOKOEHLER,
      TRUMPLIMIT_MEATLESS,
      TRUMPLIMIT_NORMAL,

      LOWEST_TRUMPLIMIT_SOLOCOLOR,
      LOWEST_TRUMPLIMIT_SOLOJACK,
      LOWEST_TRUMPLIMIT_SOLOQUEEN,
      LOWEST_TRUMPLIMIT_SOLOKING,
      LOWEST_TRUMPLIMIT_SOLOJACKKING,
      LOWEST_TRUMPLIMIT_SOLOJACKQUEEN,
      LOWEST_TRUMPLIMIT_SOLOQUEENKING,
      LOWEST_TRUMPLIMIT_SOLOKOEHLER,
      LOWEST_TRUMPLIMIT_MEATLESS,
      LOWEST_TRUMPLIMIT_NORMAL,

      LIMITHIGH, // minimum team mate card for throwing a ten or ace into this trick

      CARD_LAST = LIMITHIGH
    }; // enum TypeCard
    static unsigned const CARD_NUMBER;

  public:
    // returns a preset ai
    static Aiconfig const& preset(Difficulty const difficulty);

  public:
    friend class Ai;

  public:
    Aiconfig();
    Aiconfig(istream& istr);
    Aiconfig(Aiconfig const& aiconfig);
    Aiconfig& operator=(Aiconfig const& aiconfig);
    ~Aiconfig();

    // set the aiconfig to the default values
    void reset_to_hardcoded();

    // (re)sets the aiconfig to the given difficulty
    void set_to_difficulty(Difficulty const difficulty);

    // whether the aiconfig is equal
    bool equal(Aiconfig const& aiconfig) const;

    Difficulty difficulty() const;
    AiType const& aitype(unsigned const trickno) const;
    void set_aitype(unsigned const trickno, AiType const& aitype);
    void set_aitype_for_all_tricks(AiType const& aitype);
    unsigned const future_limit(unsigned const trickno) const;
    void set_future_limit(unsigned const trickno, unsigned const future_limit);
    void set_future_limit_for_all_tricks(unsigned const future_limit);
    Rating::Type const& rating(unsigned const trickno) const;
    void set_rating(unsigned const trickno, Rating::Type const& rating);
    void set_rating_for_all_tricks(Rating::Type const& rating);

    // returns the heuristic states
    vector<HeuristicState> const&
      heuristic_states(HeuristicsMap::Key const& key) const;
    vector<HeuristicState> const&
      heuristic_states(HeuristicsMap::GametypeGroup const gametype_group,
		       HeuristicsMap::PlayerTypeGroup const playertype_group
		      ) const;
    vector<HeuristicState>&
      heuristic_states(HeuristicsMap::Key const& key);
    vector<HeuristicState>&
      heuristic_states(HeuristicsMap::GametypeGroup const gametype_group,
		       HeuristicsMap::PlayerTypeGroup const playertype_group
		      );
    // returns the heuristics
    vector<Heuristic> heuristics(HeuristicsMap::GametypeGroup const
				 gametype_group,
				 HeuristicsMap::PlayerTypeGroup const
				 playertype_group
				) const;
    // returns the heuristics
    vector<Heuristic> heuristics(Player const& player) const;

    // returns the value
    bool value(HeuristicsMap::Key const& key,
	       Heuristic const heuristic) const;
    bool value(HeuristicsMap::GametypeGroup const gametype_group,
	       HeuristicsMap::PlayerTypeGroup const playertype_group,
	       Heuristic const heuristic) const;
    bool value(TypeBool const type) const;
    unsigned value(TypeUnsigned const type) const;
    Card value(TypeCard const type) const;

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

    // the valid cards for the type
    list<Card> const& valid_cards(TypeCard const type) const;

    // sets the value
    bool set(string const& type, string const& value);

    void set(HeuristicsMap::GametypeGroup const gametype_group,
	     HeuristicsMap::PlayerTypeGroup const playertype_group,
	     Heuristic const heuristic,
	     string const& value);
    void move(HeuristicsMap::Key const& key,
	      Heuristic const heuristic,
	      unsigned const pos);
    void set(Heuristic const heuristic, string const& value);
    void set(TypeBool const type, string const& value);
    void set(TypeUnsigned const type, string const& value);
    void set(TypeCard const type, string const& value);

    void set(HeuristicsMap::Key const& key,
	     Heuristic const heuristic,
	     bool const value);
    void set(HeuristicsMap::GametypeGroup const gametype_group,
	     HeuristicsMap::PlayerTypeGroup const playertype_group,
	     Heuristic const heuristic,
	     bool const value);
    void set_to_default(HeuristicsMap::Key const& key);
    void set(Heuristic const heuristic, bool const value);
    void set(TypeBool const type, bool const value);
    void set(TypeUnsigned const type, unsigned const value);
    void set(TypeCard const type, Card const value);

    bool load(string const& filename);
    bool load(istream& istr);
    bool load_heuristics(istream& istr);
    bool save(string const& filename) const;

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

  private:
    // initializes the heuristic states
    void init_heuristic_states();
    // fills the heuristic states with all heuristics
    void fill_up_heuristic_states();

  private:
    vector<AiType> aitype_p;
    vector<Rating::Type> rating_p;
    vector<unsigned> future_limit_p;

    map<HeuristicsMap::Key, vector<HeuristicState> > heuristic_states_;

    vector<bool> bool_p;
    vector<unsigned> unsigned_p;
    vector<Card> card_p;
}; // class Aiconfig

namespace HeuristicsMap {
  typedef std::map<Key, vector<Aiconfig::Heuristic> > Map;
}; // namespace HeuristicsMap

// whether the aitype has support for rating
bool supports_rating(AiType const type);

// whether the two aiconfigs are equal
bool operator==(Aiconfig const& aiconfig_a, Aiconfig const& aiconfig_b);
// whether the two aiconfigs are different
bool operator!=(Aiconfig const& aiconfig_a, Aiconfig const& aiconfig_b);

// whether the two heuristic states are equal
bool operator==(Aiconfig::HeuristicState const& heuristic_state_a,
		Aiconfig::HeuristicState const& heuristic_state_b);

string name(Aiconfig::Difficulty const difficulty);
WRITE_NAME2(Aiconfig, Difficulty)

  // conversion in a string
  string name(Aiconfig::Heuristic const heuristic);

  string name(Aiconfig::TypeBool const type);
  string name(Aiconfig::TypeUnsigned const type);
  string name(Aiconfig::TypeCard const type);

  // output of the name
WRITE_NAME2(Aiconfig, Heuristic)
WRITE_NAME2(Aiconfig, TypeBool)
WRITE_NAME2(Aiconfig, TypeUnsigned)
WRITE_NAME2(Aiconfig, TypeCard)

  // output of the Aiconfig
  ostream& operator<<(ostream& ostr, Aiconfig const& aiconfig);


inline
Translator::Translation translation(Aiconfig::Difficulty const& difficulty)
{ return Translator::Translation(::translator,
				 "AiConfig::Difficulty::"
				 + ::name(difficulty)); }
inline
Translator::Translation translation(Aiconfig::Heuristic const& heuristic)
{ return Translator::Translation(::translator,
				 "AiConfig::Heuristic::"
				 + ::name(heuristic)); }
inline
Translator::Translation translation_description(Aiconfig::Heuristic const& heuristic)
{ return Translator::Translation(::translator,
				 "AiConfig::Heuristic::Description::"
				 + ::name(heuristic)); }
inline
Translator::Translation translation(Aiconfig::TypeBool const& type)
{ return Translator::Translation(::translator,
				 "AiConfig::"
				 + ::name(type)); }
inline
Translator::Translation translation(Aiconfig::TypeUnsigned const& type)
{ return Translator::Translation(::translator,
				 "AiConfig::"
				 + ::name(type)); }
inline
Translator::Translation translation(Aiconfig::TypeCard const& type)
{ return Translator::Translation(::translator,
				 "AiConfig::"
				 + ::name(type)); }
inline
Translator::Translation translation(AiType const& aitype)
{ return Translator::Translation(::translator,
				 "AiConfig::AiType::"
				 + ::name(aitype)); }
#endif // #ifndef CLASS_AICONFIG_HEADER
