/**********************************************************************
 *
 *   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
 *
 *********************************************************************/

#include "constants.h"

#include "specialpoint.h"

#include "../party/party.h"
#include "../party/rule.h"
#include "../game/game.h"

/**
 ** -> return
 **
 ** @param	sp	the specialpoint type
 **
 ** @return	whether 'sp' was got by winning
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
SPECIALPOINT::is_winning(Specialpoint const sp)
{
  switch (sp) {
  case NO120:
  case NO90:
  case NO60:
  case NO30:
  case NO0:
  case NO90_WON:
  case NO60_WON:
  case NO30_WON:
  case NO0_WON:
  case CONTRA_WON:
  case SOLO:
    return true;
    break;
  default:
    return false;
    break;
  }; // switch(sp)
} // bool SPECIALPOINT::is_winning(Specialpoint const sp)

/**
 **
 ** -> return
 **
 ** @param	sp	the specialpoint type
 **
 ** @return	whether 'sp' was got by announcement
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **
 **/
bool
SPECIALPOINT::is_announcement(Specialpoint const sp)
{
  switch (sp) {
  case NO120_SAID:
  case NO90_SAID:
  case NO60_SAID:
  case NO30_SAID:
  case NO0_SAID:
  case NO120_REPLY:
  case NO90_REPLY:
  case NO60_REPLY:
  case NO30_REPLY:
  case NO0_REPLY:
    return true;
    break;
  default:
    return false;
    break;
  }; // switch(sp)
} // bool SPECIALPOINT::is_announcement(Specialpoint const sp)


/**
 ** -> result
 **
 ** @param	sp	special point
 **
 ** @result	points for this specialpoint
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
int
Value_of_Specialpoint(Specialpoint const sp)
{
  using namespace SPECIALPOINT;

  switch (sp) {
  case NOSPECIALPOINT:
    return 0;
  case CAUGHT_FOX:
  case FOX_LAST_TRICK:
  case CHARLIE:
  case CAUGHT_CHARLIE:
  case DOLLE_CAUGHT_DOLLE:
  case HEART_TRICK:
  case DOPPELKOPF:
  case NO120:
  case NO90:
  case NO60:
  case NO30:
  case NO0:
  case NO90_WON:
  case NO60_WON:
  case NO30_WON:
  case NO0_WON:
    return 1;
  case NO120_SAID:
    return (::party.rule()(Rule::ANNOUNCEMENT_RE_DOUBLES)
	    ? 0
	    : 2);
  case NO90_SAID:
  case NO60_SAID:
  case NO30_SAID:
  case NO0_SAID:
  case NO120_REPLY:
  case NO90_REPLY:
  case NO60_REPLY:
  case NO30_REPLY:
  case NO0_REPLY:
  case CONTRA_WON:
    return 1;
  case SOLO:
    return 0;
  } // switch(sp)

  return 0;
} // int Value_of_Specialpoint(Specialpoint sp)

/**
 ** -> result
 **
 ** @param	type	the type of the special point
 **
 ** @return	name of the specialpoints
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
string
name(Specialpoint const type)
{
  using namespace SPECIALPOINT;

  switch(type) {
  case NOSPECIALPOINT:
    return "no specialpoint";
  case CAUGHT_FOX:
    return "caught fox";
  case FOX_LAST_TRICK:
    return "fox last trick";
  case CHARLIE:
    return "charlie";
  case CAUGHT_CHARLIE:
    return "caught charlie";
  case DOLLE_CAUGHT_DOLLE:
    return "dolle caught dolle";
  case HEART_TRICK:
    return "heart trick";
  case DOPPELKOPF:
    return "Doppelkopf";
  case NO120:
    return "no 120";
  case NO90:
    return "no 90";
  case NO60:
    return "no 60";
  case NO30:
    return "no 30";
  case NO0:
    return "no 0";
  case NO90_WON:
    return "no 90 won";
  case NO60_WON:
    return "no 60 won";
  case NO30_WON:
    return "no 30 won";
  case NO0_WON:
    return "no 0 won";
  case NO120_SAID:
    return "no 120 said";
  case NO90_SAID:
    return "no 90 said";
  case NO60_SAID:
    return "no 60 said";
  case NO30_SAID:
    return "no 30 said";
  case NO0_SAID:
    return "no 0 said";
  case NO120_REPLY:
    return "no 120 reply said";
  case NO90_REPLY:
    return "no 90 reply said";
  case NO60_REPLY:
    return "no 60 reply said";
  case NO30_REPLY:
    return "no 30 reply said";
  case NO0_REPLY:
    return "no 0 reply said";
  case CONTRA_WON:
    return "contra won";
  case SOLO:
    return "solo";
  } // switch(specialpoints)

  return "";
} // string name(Specialpoint const specialpoint)

/**
 ** -> result
 **
 ** @param	name	special point name
 **
 ** @return	the special point with name 'name'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
Specialpoint
specialpoint_from_name(string const& name)
throw(ReadException)
{
  for (int s = SPECIALPOINT::FIRST; s <= SPECIALPOINT::LAST; ++s)
    if (name == ::name(static_cast<Specialpoint>(s)))
      return static_cast<Specialpoint>(s);

  throw ReadException("unknown special point '" + name + "'");
  return SPECIALPOINT::NOSPECIALPOINT;
} // Specialpoint specialpoint_from_name(string name)

/**
 ** writes the specialpoint (name) in the output stream
 **
 ** @param	ostr	output stream
 ** @param	type	type to write
 **
 ** @return	output stream
 **
 ** @author	Borg Enders
 **
 ** @version	0.6.8
 **
 **/
ostream&
operator<<(ostream& ostr, Specialpoint const& type)
{
  ostr << name(type);

  return ostr;
} // ostream operator<<(ostream& ostr, Specialpoint type);

/**
 ** Constructor
 **
 ** @param	istr	input stream to read the special points from
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
Specialpoints::Specialpoints(istream& istr) :
  type(SPECIALPOINT::NOSPECIALPOINT),
  team(TEAM::NOTEAM),
  counting_team(TEAM::NOTEAM),
  player_get_no(UINT_MAX),
  player_of_no(UINT_MAX)
{
  Config config;
  istr >> config;

  this->type = specialpoint_from_name(config.name);
  string::size_type pos_begin = 0;
  while (isspace(config.value[pos_begin]) && (pos_begin < config.value.size()))
    pos_begin++;
  string::size_type pos_end = config.value.find(',');
  if (pos_end == string::npos) {
    istr.clear(ios::failbit);
    return ;
  }
  this->team = TEAM::from_name(string(config.value,
				      pos_begin, pos_end - pos_begin));
  pos_begin = pos_end + 1;
  while (isspace(config.value[pos_begin]) && (pos_begin < config.value.size()))
    pos_begin++;
  pos_end = config.value.find(',', pos_begin);
  if (pos_end == string::npos) {
    istr.clear(ios::failbit);
    return ;
  }
  this->counting_team = TEAM::from_name(string(config.value,
					       pos_begin, pos_end - pos_begin));
  pos_begin = pos_end + 1;
  while (isspace(config.value[pos_begin]) && (pos_begin < config.value.size()))
    pos_begin++;
  pos_end = config.value.find(',', pos_begin);
  if (pos_end == string::npos) {
    istr.clear(ios::failbit);
    return ;
  }
  // explicit cast because of problems with MinGW g++-3.2
  this->player_get_no = Unsigned(string(config.value,
					pos_begin, pos_end - pos_begin)
				).operator unsigned const&();
  pos_begin = pos_end + 1;
  pos_end = config.value.size();
  // explicit cast because of problems with MinGW g++-3.2
  this->player_of_no = Unsigned(string(config.value,
				       pos_begin, pos_end - pos_begin)
			       ).operator unsigned const&();

} // Specialpoints::Specialpoints(istream& istr)

/**
 ** writes the specialpoint class in the output stream
 **
 ** @param	ostr		output stream
 ** @param	specialpoint	specialpoint to write
 **
 ** @return	output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
ostream&
operator<<(ostream& ostr, Specialpoints const& specialpoint)
{
  ostr << specialpoint.type << " = "
    << specialpoint.team << ", "
    << specialpoint.counting_team << ", "
    << Unsigned(specialpoint.player_get_no) << ", "
    << Unsigned(specialpoint.player_of_no)
    << '\n';

  return ostr;
} // ostream operator<<(ostream& ostr, Specialpoints specialpoint);

/**
 ** writes the specialpoint vector in the output stream
 **
 ** @param	ostr			output stream
 ** @param	specialpointsvector	specialpoints to write
 **
 ** @return	output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
ostream&
operator<<(ostream& ostr, Specialpointsvector const& specialpointsvector)
{
  for (Specialpointsvector::const_iterator sp = specialpointsvector.begin();
       sp != specialpointsvector.end();
       ++sp)
    ostr << *sp;

  return ostr;
} // ostream operator<<(ostream& ostr, Specialpointsvector specialpointsvector);

/**
 ** -> result
 **
 ** @param      -
 **
 ** @return     the value of the specialpoints
 **
 ** @author     Borg Enders
 ** @author     Diether Knof
 **
 ** @version    0.6.1
 **/
int
Specialpoints::value() const
{
  DEBUG_CALLING(INFO_BASISTYPES && INFO_VALUE,
		"Specialpoints::value()");

  DEBUG_RETURNING(Value_of_Specialpoint(this->type),
		  INFO_BASISTYPES && INFO_VALUE,
		  "Specialpoints::value()");
} // int Specialpoints::value() const

/**
 ** -> result
 **
 ** @param      team	teamvector
 **
 ** @return     whether the specialpoint is valid at the end of the game
 **
 ** @author     Borg Enders
 ** @author     Diether Knof
 **
 ** @version    0.6.1
 **/
bool
Specialpoints::is_valid(vector<Team> const team) const
{
  switch(type) {
  case SPECIALPOINT::CAUGHT_FOX:
  case SPECIALPOINT::CAUGHT_CHARLIE:
  case SPECIALPOINT::DOLLE_CAUGHT_DOLLE:
    return (team[this->player_of_no] != team[this->player_get_no]);
  case SPECIALPOINT::FOX_LAST_TRICK:
  case SPECIALPOINT::CHARLIE:
  case SPECIALPOINT::HEART_TRICK:
  case SPECIALPOINT::DOPPELKOPF:
    return true;
  case SPECIALPOINT::NOSPECIALPOINT:
  case SPECIALPOINT::NO120:
  case SPECIALPOINT::NO90:
  case SPECIALPOINT::NO60:
  case SPECIALPOINT::NO30:
  case SPECIALPOINT::NO0:
  case SPECIALPOINT::NO90_WON:
  case SPECIALPOINT::NO60_WON:
  case SPECIALPOINT::NO30_WON:
  case SPECIALPOINT::NO0_WON:
  case SPECIALPOINT::NO120_SAID:
  case SPECIALPOINT::NO90_SAID:
  case SPECIALPOINT::NO60_SAID:
  case SPECIALPOINT::NO30_SAID:
  case SPECIALPOINT::NO0_SAID:
  case SPECIALPOINT::NO120_REPLY:
  case SPECIALPOINT::NO90_REPLY:
  case SPECIALPOINT::NO60_REPLY:
  case SPECIALPOINT::NO30_REPLY:
  case SPECIALPOINT::NO0_REPLY:
  case SPECIALPOINT::CONTRA_WON:
  case SPECIALPOINT::SOLO:
    return true;
  } // switch(specialpoints)
  return false;
} // bool Specialpoints::is_valid(vector<Team> const team) const

/**********************************************************************
 **
 **    int Sum_of_Specialpoints(Specialpointsvector spv,Team winner)
 **
 **    Parameters:  input a specialpointsvector and Winnerteam
 **
 **    Result: sum of all Specialpoints for winnerteam minus#
 **            all specialpoints of losing team
 **
 **    Version: Alpha
 **
 **    Description: 
 **      
 **
 **********************************************************************/
int
Sum_of_Specialpoints(Specialpointsvector const& spv,
		     Team const winner,
		     Game const& g)
{
  DEBUG_CALLING(INFO_BASISTYPES && INFO_SPECIALPOINTS,
		"int Sum_of_Specialpoints(const Specialpointsvector& spv,Team winner)");

  int re_played=0;
  int re_ann=0;
  int re_trick=0;
  int contra_played=0;
  int contra_trick=0;
  int contra_ann=0;
  int no120s=1;

#ifdef OUTDATED
  // 0.6.8 2004-12-03 (-> GameSummary::points(), GameSummary::points(playerno) )
  int const win_mod = ( ( GAMETYPE::is_solo(g.type())
			 && (winner == TEAM::RE) )
		       ? 3 : 1);
#endif

#ifdef OUTDATED
  // 0.6.7 2004-11-14 (-> GameSummary::evaluate(Game) )
  if (GAMETYPE::is_solo(g.type()))
  {
    if (winner == TEAM::RE)
    {
      win_mod=3;
      Specialpoints sp(SPECIALPOINT::SOLO, TEAM::RE);
      sp.player_of_no = g.soloplayer().no();

      spv.push_back( sp );
    }
  }
#endif


  for (unsigned int i=0; i<spv.size(); i++)  {
    DEBUG_PRINTING((spv[i].team!=TEAM::RE) &&
		   (spv[i].team!=TEAM::CONTRA),
		   "WARNING: Specialpoints for undetermined team");
#ifdef BENDERS
#ifndef RELEASE
    if ((spv[i].team != TEAM::RE)
	&& (spv[i].team != TEAM::CONTRA)) {
      COUT << "Game: \n"
	<< "  Current trick no: " << g.trick_current_no()
	<< endl;

      COUT << "Teams: " << endl;
      for (unsigned p = 0; p < g.playerno(); p++)
	COUT << p << ": " << g.team(g.player(p)) << endl;

      COUT << "Specialpoint:\n"
	<< "  Type: " << spv[i].type << '\n'
	<< "  Team: " << spv[i].team << '\n'
	<< "  player_get_no: " << spv[i].player_get_no << '\n'
	<< "  player_of_no:  " << spv[i].player_of_no << '\n'
	<< endl;
      DEBUG_ASSERTION((spv[i].team==TEAM::RE)
		      || (spv[i].team==TEAM::CONTRA),
		      "WARNING: Specialpoints for undetermined team");
    } // if (undetermined team)
#endif
#endif


    Specialpoints const& sp = spv[i];
    // splitting of points in Announcements and trick specialpoints 
    // for doubling with RE
    switch (spv[i].type) {
    case SPECIALPOINT::NO120:
    case SPECIALPOINT::NO90:
    case SPECIALPOINT::NO60:
    case SPECIALPOINT::NO30:
    case SPECIALPOINT::NO0:
      if (sp.counting_team == TEAM::RE)
	re_played += Value_of_Specialpoint(spv[i].type);
      else
	contra_played += Value_of_Specialpoint(spv[i].type);
      break;
    case SPECIALPOINT::NO120_SAID:
      if (::party.rule()(Rule::ANNOUNCEMENT_RE_DOUBLES))
	no120s *= 2;
      else
	if (sp.counting_team == TEAM::RE)
	  re_ann += Value_of_Specialpoint(spv[i].type);
	else
	  contra_ann+= Value_of_Specialpoint(spv[i].type);
      break;  
    case SPECIALPOINT::NO90_SAID:
    case SPECIALPOINT::NO60_SAID:
    case SPECIALPOINT::NO30_SAID:
    case SPECIALPOINT::NO0_SAID:
      if (sp.counting_team == TEAM::RE)
	re_ann += Value_of_Specialpoint(spv[i].type);
      else
	contra_ann+= Value_of_Specialpoint(spv[i].type);
      break;
    case SPECIALPOINT::NO90_WON:
    case SPECIALPOINT::NO60_WON:
    case SPECIALPOINT::NO30_WON:
    case SPECIALPOINT::NO0_WON:
      if (sp.counting_team == TEAM::RE)
	re_played += Value_of_Specialpoint(spv[i].type);
      else
	contra_played += Value_of_Specialpoint(spv[i].type);
      break;
    case SPECIALPOINT::NO120_REPLY:
    case SPECIALPOINT::NO90_REPLY:
    case SPECIALPOINT::NO60_REPLY:
    case SPECIALPOINT::NO30_REPLY:
    case SPECIALPOINT::NO0_REPLY:
      if (sp.counting_team == TEAM::RE)
	re_ann += Value_of_Specialpoint(spv[i].type);
      else
	contra_ann+= Value_of_Specialpoint(spv[i].type);
      break;
    case SPECIALPOINT::CONTRA_WON:
      if (::party.rule()(Rule::ANNOUNCEMENT_CONTRA_DOUBLES_AGAINST_RE))
	contra_played += Value_of_Specialpoint(spv[i].type);
      else
	contra_trick += Value_of_Specialpoint(spv[i].type);
      break;

    default:
      if (sp.counting_team == TEAM::RE)
	re_trick += Value_of_Specialpoint(spv[i].type);
      else
	contra_trick += Value_of_Specialpoint(spv[i].type);
      break;
    } // switch (spv[i].type)


  } // for (unsigned int i=0; i<spv.size(); i++)


  DEBUG_ASSERTION((no120s <= 4),
		  "Sum_of_Specialpoints():\n"
		  "  'no120s' > 4 (no120s = " << no120s);

  if (winner==TEAM::RE)
  {
    int result = 0;
    result+= re_played + re_ann - contra_played;
    result*=no120s;
    result+= re_trick - contra_trick; 
#ifdef OUTDATED
    // 0.6.8 2004-12-03 (-> GameSummary::points(), GameSummary::points(playerno) )
    result*= win_mod;
#endif
    DEBUG_RETURNING(result,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Sum_of_Specialpoints(const Specialpointsvector& spv,Team winner)");
  }


  if (winner==TEAM::CONTRA)
  {
    int result = 0;
    result+= contra_played + contra_ann - re_played;
    result*=no120s;
    result+= contra_trick - re_trick; 
#ifdef OUTDATED
    // 0.6.8 2004-12-03 (-> GameSummary::points(), GameSummary::points(playerno) )
    result*= win_mod;
#endif
    DEBUG_RETURNING(result,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Sum_of_Specialpoints(const Specialpointsvector& spv,Team winner)");
  }

  if (winner==TEAM::NOTEAM)
  {
    int result = 0;
    result+= re_played - contra_played;
    result*=no120s;
    result+= re_trick - contra_trick; 
#ifdef OUTDATED
    // 0.6.8 2004-12-03 (-> GameSummary::points(), GameSummary::points(playerno) )
    result*= win_mod;
#endif
    DEBUG_RETURNING(result,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Sum_of_Specialpoints(const Specialpointsvector& spv,Team winner)");
  }

  DEBUG_RETURNING(INT_MIN,
		  INFO_BASISTYPES && INFO_SPECIALPOINTS,
		  "int Sum_of_Specialpoints(const Specialpointsvector& spv,Team winner)");
} // int sum_of_specialpoints(Specialpointsvector, Team winner)
