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

#ifdef USE_NETWORK

#include "constants.h"

#include "interpreter.h"

#include "../server.h"
#include "../../party/rule.h"
#include "../../misc/setting.h"

#include "../../utils/string.h"

namespace Network {
  namespace FreeDoko {

  /**
   ** constructor
   **
   ** @param     interpreter   corresponding interpreter
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.7.1
   **/
   Interpreter::Sender::Sender(Interpreter& interpreter) :
     Connection::Interpreter::Sender(interpreter),
     counter_(0),
     pending_()
  { }

  /**
   ** destructor
   **
   ** @param     -
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.7.1
   **/
   Interpreter::Sender::~Sender()
  {
      this->counter_ = 0;
      this->pending_.clear();
  } //Interpreter::Sender::~Sender()

  /**
   ** the arrival of the message number 'number' was confirmated
   ** remove the message from the pending list
   **
   ** @param     number   number of received message
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.7.1
   **/
   void
     Interpreter::Sender::confirmation(unsigned const number)
     {
       // remove the message from the list of sent messages
       for (list<Packet>::iterator
	    m = this->pending_.begin();
	    m != this->pending_.end();
	    ++m)
	 if (m->number == number) {
	   this->pending_.erase(m);
	   return ;
	 }

       cerr << "Connection::act_special(text)\n"
	 << "  received confirmation for not pending message number " << number
	 << endl;

       return ;
     } // void Interpreter::Sender::confirmation(unsigned number)


   /**
    ** the message number 'number' was received
    **
    ** @param     number   number of received message
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::received(unsigned const number)
     {
       this->send_with_time("received: " + DK::Utils::String::to_string(number));
       return ;
     } // void Interpreter::Sender::received(unsigned number)

   /**
    ** the connection is up
    **
    ** @param     -
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::connection_up()
     {
       if (this->connection().relation() == Connection::PARENT)
	 this->connection().send("FreeDoko server\n");

       this->name();

       if (this->connection().relation() == Connection::CHILD)
	 this->rules();

       if (this->connection().relation() == Connection::CHILD)
	 this->connection().create_network_player();

       for (vector<Player*>::const_iterator
	    p = this->connection().server().party().players().begin();
	    p != this->connection().server().party().players().end();
	    ++p) {
	   this->player_type(**p);
	   if (!this->connection().manages_player(**p))
	     this->player_name(**p);
       }


       return ;
     } // void Interpreter::Sender::connection_up()

   /**
    ** send a ping
    **
    ** @param     -
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::ping()
     {
       this->send_with_time("ping");
       return ;
     } // void Interpreter::Sender::ping()

   /**
    ** send a ping with a number
    **
    ** @param     n   number of the ping
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::ping(unsigned const n)
     {
       this->send_with_time("ping: " + DK::Utils::String::to_string(n));
       return ;
     } // void Interpreter::Sender::ping(unsigned n)

   /**
    ** send a pong
    **
    ** @param     -
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::pong()
     {
       this->send_with_time("pong");
       return ;
     } // void Interpreter::Sender::pong()

   /**
    ** send a pong with a number
    **
    ** @param     n   number of the pong
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::pong(unsigned const n)
     {
       this->send_with_time("pong: " + DK::Utils::String::to_string(n));
       return ;
     } // void Interpreter::Sender::pong(unsigned n)

   /**
    ** send a chat text
    **
    ** @param     text   chat text
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::chat(string const& text)
     {
       this->send("chat: " + text);
       return ;
     } // void Interpreter::Sender::chat(string text)

   /**
    ** send the name
    **
    ** @param     -
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::name()
     {
       this->send("name: " + ::setting(Setting::NAME));
       return ;
     } // void Interpreter::Sender::name()

   /**
    ** send the type of the player
    **
    ** @param     player     player to send the type of
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.2
    **/
   void
     Interpreter::Sender::player_type(Player const& player)
     {
       this->send("player " + DK::Utils::String::to_string(player.no())
		  + ": type = " + ( this->connection().manages_player(player)
				   ? "local"
				   : "network") );
       return ;
     } // void Interpreter::Sender::player_type(Player const& player)

   /**
    ** send the name of the player
    **
    ** @param     player     player to send the name of
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.2
    **/
   void
     Interpreter::Sender::player_name(Player const& player)
     {
       this->send("player " + DK::Utils::String::to_string(player.no())
		  + ": name = " + player.name());
       return ;
     } // void Interpreter::Sender::player_name(Player const& player)

   /**
    ** send all rules
    **
    ** @param     -
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::rules()
     {
       ostringstream ostr;
       ostr << "rules\n";
       ostr << "{\n";
       ostr << this->connection().server().party().rule();
       ostr << "}\n";
       this->send_lines(ostr.str());

       return ;
     } // void Interpreter::Sender::rules()

   /**
    ** send the rule 'type'
    **
    ** @param     type   type to send
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::rule(int const type)
     {
       Rule const& rule = this->connection().server().party().rule();

       if (   (type >= Rule::BOOL_FIRST)
	   && (type <= Rule::BOOL_LAST) ) {
	 this->send("rule: " + ::name(static_cast<Rule::TypeBool>(type))
		    + " = " + (rule(static_cast<Rule::TypeBool>(type))
			       ? "1" : "0") );
       } else if (   (type >= Rule::UNSIGNED_FIRST)
		  && (type <= Rule::UNSIGNED_LAST) ) {
	 this->send("rule: " + ::name(static_cast<Rule::TypeUnsigned>(type))
		    + " = " + DK::Utils::String::to_string(rule(static_cast<Rule::TypeUnsigned>(type)))
		   );
       } else if (type == Rule::COUNTING) {
	 this->send("rule: " + ::name(Rule::COUNTING)
		    + " = " + ::name(rule(Rule::COUNTING)) );
       } else {
	 cerr << "rule '" << type << "' unknown" << endl;
       } // if (type == ...)

       return ;
     } // void Interpreter::Sender::rule(int type)

   /**
    ** sends the text, the time and the counter over the connection
    **
    ** @param     text   text to send
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::send(string const& text)
     {
       this->counter_ += 1;
       Time const time =
	 this->send_with_time(DK::Utils::String::to_string(this->counter())
			      + ": " + text);

       this->pending_.push_back(Packet(time, this->counter(), text));

       return ;
     } // void Interpreter::Sender::send(string text)

   /**
    ** sends the lines over the connection (each line with time and counter)
    **
    ** @param     lines   lines to send
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::send_lines(string const& lines)
     {
       string::size_type pos = 0;
       while (pos != string::npos) {
	 string::size_type const pos_eol = lines.find('\n', pos);
	 if (pos_eol == string::npos)
	   break;

	 if (pos_eol != pos)
	   this->send(string(lines, pos, pos_eol - pos));
	 pos = pos_eol + 1;
       } // while (pos != string::npos)

       return ;
     } // void Interpreter::Sender::send_lines(string lines)

   /**
    ** sends the text and the time over the connection
    **
    ** @param     text   special text to send
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   void
     Interpreter::Sender::send_special(string const& text)
     {
       this->send_with_time(text);

       return ;
     } // void Interpreter::Sender::send_special(string text)

   /**
    ** prepends the time to the text and send it over the connection
    **
    ** @param     text   text to send
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.1
    **/
   Time
     Interpreter::Sender::send_with_time(string const& text)
     {
       Time const time = Time::current();
       string const timestring
	 = ( (time.sec == 0)
	    ? string("-")
	    : (DK::Utils::String::to_string(time.sec)
	       + "."
	       + DK::Utils::String::to_string(time.msec)) );

       this->connection().send(timestring + ": " + text + "\n");

       return time;
     } // Time Interpreter::Sender::send_with_time(string text)

   /**
    ** the party is started
    **
    ** @param     -
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.2
    **/
   void
     Interpreter::Sender::party_start()
     {
       this->send("party start");

       return ;
     } // void Interpreter::Sender::party_start()

   /**
    ** a new round is started
    ** do nothing
    **
    ** @param     roundno   number of round to start
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.2
    **/
   void
     Interpreter::Sender::party_start_round(unsigned const roundno)
     {
       this->send("party start round: "
		  + DK::Utils::String::to_string(roundno));

       return ;
     } // void Interpreter::Sender::party_start_round(unsigned roundno)

   /**
    ** the party is finished
    **
    ** @param     -
    **
    ** @return    -
    **
    ** @author    Diether Knof
    **
    ** @version   0.7.2
    **/
   void
     Interpreter::Sender::party_finish()
     {
       this->send("party finish");

       return ;
     } // void Interpreter::Sender::party_finish()


  } // namespace FreeDoko
} // namespace Network

#endif // #ifdef USE_NETWORK
