/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2005  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)


/**********************************************************************
 *
 **    int Value_of_Specialpoint(Specialpoint sp)
 *
 **    Parameters:  input a specialpoint
 *
 **    Result: points for this specialpoint
 *
 **    Version: Alpha
 *
 **    Description: 
 **      
 *
 **********************************************************************/
int
Value_of_Specialpoint(Specialpoint sp)
{
  DEBUG_CALLING(INFO_BASISTYPES && INFO_SPECIALPOINTS,
		"int Value_of_Specialpoint(Specialpoint sp)");

  using namespace SPECIALPOINT;

  switch (sp) {
  case NOSPECIALPOINT:
    DEBUG_RETURNING(0,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case CAUGHT_FOX:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case FOX_LAST_TRICK:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case CHARLIE:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case CAUGHT_CHARLIE:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case DOLLE_CAUGHT_DOLLE:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case HEART_TRICK:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case DOPPELKOPF:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO120:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO90:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO60:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO30:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO0:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;    
  case NO90_WON:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO60_WON:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO30_WON:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO0_WON:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case NO120_SAID:
    if (party.rule()(Rule::ANNOUNCEMENT_RE_DOUBLES))
    {
      DEBUG_RETURNING(0,
		      INFO_BASISTYPES && INFO_SPECIALPOINTS,
		      "int Value_of_Specialpoint(Specialpoint sp)");
    } else
    {
      DEBUG_RETURNING(2,
		      INFO_BASISTYPES && INFO_SPECIALPOINTS,
		      "int Value_of_Specialpoint(Specialpoint sp)");
    }
    break;    
  case NO90_SAID:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO60_SAID:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO30_SAID:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case NO0_SAID:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case NO120_REPLY:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case NO90_REPLY:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case NO60_REPLY:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case NO30_REPLY:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case NO0_REPLY:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break;
  case CONTRA_WON:
    DEBUG_RETURNING(1,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  case SOLO:
    DEBUG_RETURNING(0,
		    INFO_BASISTYPES && INFO_SPECIALPOINTS,
		    "int Value_of_Specialpoint(Specialpoint sp)");
    break; 
  } // switch(sp)

  DEBUG_RETURNING(-1,
		  INFO_BASISTYPES && INFO_SPECIALPOINTS,
		  "int Value_of_Specialpoint(Specialpoint sp)");
} // int Value_of_Specialpoint(Specialpoint sp)

/**********************************************************************
 *
 ** string name(Specialpoint const& type)
 *
 ** Parameters:  type - the type of the special point
 *
 ** Result:  name of the specialpoints
 *
 ** Version: 0.4.1
 *
 ** Description: -> result
 *
 **********************************************************************/
string
name(Specialpoint const& type)
{
  DEBUG_CALLING(INFO_SPECIALPOINTS && INFO_NAME,
		"string name(specialpoints)");

  using namespace SPECIALPOINT;

  string name;

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

  DEBUG_RETURNING(name,
		  INFO_SPECIALPOINT && INFO_NAME,
		  "string name(specialpoint)");
} // string name(Specialpoint const& specialpoint)

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

/**
 ** 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.8
 **
 **/
ostream&
operator<<(ostream& ostr, Specialpoints const& specialpoint)
{
  ostr << specialpoint.type << " = "
    << specialpoint.team << ", "
    << specialpoint.counting_team << ", "
    << specialpoint.player_get_no << ", "
    << 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
 **
 ** @version    0.6.1
 **
 ** @author     Borg Enders
 ** @author     Diether Knof
 **
 **/
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
 **
 ** @version    0.6.1
 **
 ** @author     Borg Enders
 ** @author     Diether Knof
 **
 **/
bool
Specialpoints::is_valid(vector<Team> const team) const
{
  DEBUG_CALLING(INFO_BASISTYPES && INFO_VALUE,
		"Specialpoints::is_valid(team)");

  bool valid = false;
  switch(type) {
  case SPECIALPOINT::CAUGHT_FOX:
  case SPECIALPOINT::CAUGHT_CHARLIE:
  case SPECIALPOINT::DOLLE_CAUGHT_DOLLE:
    valid = (team[this->player_of_no] != team[this->player_get_no]);
    break;
  case SPECIALPOINT::FOX_LAST_TRICK:
  case SPECIALPOINT::CHARLIE:
  case SPECIALPOINT::HEART_TRICK:
  case SPECIALPOINT::DOPPELKOPF:
    valid = true;
    break;
  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:
    valid = true;
    break;
  } // switch(specialpoints)

  DEBUG_RETURNING(valid,
		  INFO_BASISTYPES && INFO_VALUE,
		  "Specialpoints::is_valid(team)");
} // 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;
    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)
