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


/**
 **
 ** Alpha-Version of FreeDoko 
 **
 **   developed since fall 2001
 **
 **/

#include "constants.h"
#include <time.h>
#ifdef WINDOWS
// for 'mkdir'
#include <io.h>
#else
// for 'mkdir'
#include <sys/stat.h>
#endif

#include "class/getopt/getopt.h"

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

#include "utils.h"
#include "basistypes.h"

#include "misc/setting.h"
#include "misc/translations.h"
#include "misc/bug_report.h"

#include "party/party.h"

#include "ui/ui.wrap.h"
#include "ui/ui.dummy.h"

#ifdef USE_NETWORK
#include "network/server.h"
#endif

#include "os/seed.h"
#include "os/party_points.h"
#include "os/bug_report_replay.h"
extern OS_NS::BugReportReplay* bug_report_replay;

UI* ui = NULL;
Translator translator;
Setting setting_default;
Setting setting;
Party party;

#include "utils.h"


#include "player/namegen.h"
#ifndef WINDOWS
int
main(int argc, char* argv[])
#else
  // ToDo: use 'WinMain'
int
main(int argc, char* argv[])
#endif
{
  try {
    ::game_status = GAMESTATUS::PROGRAMSTART;
    string party_filename; // file with the party to load from

    using DK::Utils::Version;
    using DK::Utils::Date;


    // make an assertion create a bug report
    ::debug_function = &create_assertion_bug_report;

    // list of all Versions
    ::all_versions.push_back(new Version(0, 6, 3,	Date(2004,  5,  5)));
    ::all_versions.push_back(new Version(0, 6, 4,	Date(2004,  6,  1)));
    ::all_versions.push_back(new Version(0, 6, 5, 	Date(2004,  6, 24)));
    ::all_versions.push_back(new Version(0, 6, 5, 'b',	Date(2004,  6, 28)));
    ::all_versions.push_back(new Version(0, 6, 6,	Date(2004,  9, 25)));
    ::all_versions.push_back(new Version(0, 6, 7,	Date(2004, 12, 16)));
    ::all_versions.push_back(new Version(0, 6, 7, 'b',	Date(2004, 12, 20)));
    ::all_versions.push_back(new Version(0, 6, 8,	Date(2005,  4, 11)));
    ::all_versions.push_back(new Version(0, 6, 9,	Date(2005,  6, 30)));
    ::all_versions.push_back(new Version(0, 7, 0,	Date(2005,  8, 22)));
    ::all_versions.push_back(new Version(0, 7, 1,	Date(2005, 11, 12)));
    ::all_versions.push_back(new Version(0, 7, 2,	Date(2006,  1, 23)));
    ::all_versions.push_back(new Version(0, 7, 2, 'b',	Date(2006,  2,  9)));

    // initialisation for random functions
    ::srand(static_cast<unsigned>(::time(NULL) % RAND_MAX));

    ui = new UI_Wrap;
#ifndef RELEASE
#ifdef DKNOF
    //static_cast<UI_Wrap*>(ui)->append(new_OS(OS_TYPE::GAMEPLAY));
#endif
#endif
#ifdef POSTPHONED
    static_cast<UI_Wrap*>(ui)->append(new_OS(OS_TYPE::CHATTER));
#endif


    // output of the seed each game
    static_cast<UI_Wrap*>(ui)->append(new OS_NS::Seed(new ofstream((::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/seed").c_str(),
								   ios::app)));
#ifndef RELEASE
    // output of the party points
    static_cast<UI_Wrap*>(ui)->append(new OS_NS::PartyPoints(new ofstream("FreeDoko.party_points")));
#endif

#ifdef USE_NETWORK
    // open the network
    ::server = new Network::Server();
    static_cast<UI_Wrap*>(::ui)->append(::server);
#ifdef DKNOF
    (void)
      (   ::server->create_listener(Network::Server::FREEDOKO_STANDARD_PORT)
       || ::server->create_listener(Network::Server::FREEDOKO_STANDARD_PORT

				    + 1)
       || ::server->create_listener(Network::Server::FREEDOKO_STANDARD_PORT
				    + 2)
       || ::server->create_listener(Network::Server::FREEDOKO_STANDARD_PORT
				    + 3)
      );
#endif
#endif // #ifdef USE_NETWORK


    // ui
    UI* ui_tmp = NULL;
    { // parse options

      // This file contains the help you get calling the program with '-?'.
      // It includes what arguments you can use.
#ifdef WINDOWS
#include "text/gpl.dos.string"
#include "text/help.dos.string"
#else
#include "text/gpl.string"
#include "text/help.string"
#endif

      // load the settings
      ::setting.load();

      // parse the options
      GetOpt::Option option;
      while (option =
	     GetOpt::getopt(argc, argv,
			    "help", '?', GetOpt::Syntax::BOOL,
			    "hilfe", 'h', GetOpt::Syntax::BOOL,
			    "version", 'v', GetOpt::Syntax::BOOL,
			    "license", 'L', GetOpt::Syntax::BOOL,
			    "ui", 'u', GetOpt::Syntax::BSTRING,
			    "settings", '\0', GetOpt::Syntax::BSTRING,
			    "name", 'n', GetOpt::Syntax::BSTRING,
			    "language", 'l', GetOpt::Syntax::BSTRING,
			    "seed", 's', GetOpt::Syntax::UNSIGNED,
			    "startplayer", 'p', GetOpt::Syntax::UNSIGNED,
			    "cardset", 'C', GetOpt::Syntax::BSTRING,
			    "cards_height", 'H', GetOpt::Syntax::UNSIGNED,
			    "bug_report", 'b', GetOpt::Syntax::BSTRING,
			    "fast_play", 'F', GetOpt::Syntax::BOOL,
			    // end of arguments
			    "", 0, GetOpt::Syntax::END)) {

	if (option.fail()) {
	  // wrong usage
	  cerr << argv[0] << "\n"
	    << "wrong usage: "
	    << option.error() << " " << option.value_string() << "\n"
	    << "For the syntax type '" << argv[0] << " -?'"
	    << endl;

	  return EXIT_FAILURE;
	} // if (option.fail())

	if ((option.name() == "help") 
	    || (option.name() == "hilfe")) {
	  // output of the help
	  cout << help_string << endl;
	  return EXIT_SUCCESS;
	} else if (option.name() == "version") {
	  // output of the version
	  cout << "FreeDoko " << ::version << "\n"
	    << "\n"
	    << GPL_string
	    << endl;
	  return EXIT_SUCCESS;
	} else if (option.name() == "license") {
	  // output of the license (GPL)
	  cout << "FreeDoko -- License:\n\n"
	    << GPL
	    << '\n'
	    << '\n'
	    << "----------\n"
	    << '\n'
	    << "Cardset '" << ::setting(Setting::CARDSET) << "' license:\n"
	    << cardset_license
	    << endl;
	  return EXIT_SUCCESS;
	} else if (option.name() == "ui") {
	  delete ui_tmp;
	  if (option.value_string() == "none")
	    ui_tmp = UI::new_(UI_TYPE::DUMMY);
#ifdef USE_UI_TEXT
	  else if (option.value_string() == "text")
	    ui_tmp = UI::new_(UI_TYPE::TEXT);
#endif
#ifdef USE_UI_GTKMM
	  else if (option.value_string() == "gtkmm")
	    ui_tmp = UI::new_(UI_TYPE::GTKMM_DOKO);
#endif
	  else {
	    cerr << "ui '" << option.value_string() << "' not implemented.\n"
	      << "exiting";
	    return 1;
	  } // if (option.value_string() == "...")
	} else if (option.name() == "settings") {
	  setting.load(option.value_string());
	} else if (option.name() == "name") {
	  setting.set(Setting::NAME, option.value_string());
	} else if (option.name() == "language") {
	  setting.set(Setting::LANGUAGE, option.value_string());
	} else if (option.name() == "seed") {
	  ::party.set_seed_first(option.value(GetOpt::Option::UNSIGNED));
	  party_filename = "-";
	} else if (option.name() == "startplayer") {
	  unsigned const no = option.value(GetOpt::Option::UNSIGNED);
	  if ( !( (no >= 1)
		 && (no <= ::party.playerno()) ) ) {
	    cerr << "startplayer number '" << no << "' not valid.\n"
	      << "must be between '1' and '" << ::party.playerno() << "'.\n"
	      << "ignoring the value\n"
	      << endl;
	    continue;
	  }
	  ::party.set_startplayer_first(no - 1);
	  party_filename = "-";
	} else if (option.name() == "graphic_size") {
	  setting.set(Setting::CARDS_HEIGHT,
		      option.value(GetOpt::Option::UNSIGNED));
	} else if (option.name() == "cardset") {
	  setting.set(Setting::CARDSET, option.value_string());
	} else if (option.name() == "bug_report") {
	  static_cast<UI_Wrap*>(::ui)->append(new (OS_NS::BugReportReplay)(option.value_string()));
	} else if (option.name() == "fast_play") {
	  FAST_PLAY = option.value(GetOpt::Option::BOOL);
	  // change all humans to ai's
	  for (unsigned p = 0; p < ::party.playerno(); p++) {
	    if (::party.player(p).type() == Player::HUMAN)
	      ::party.set_playertype(p, Player::AI);
	  }
	} else if (option.name().empty()
		   && party_filename.empty()) {
	  party_filename = option.value_string();
	} else {
	  cerr << "Option " << option.name() << " unknown!" << endl;
	  exit(1);
	} // if (option.name() == ...)
      } // while (getopt())
    } // parse options

    if (ui_tmp == NULL) {
      // set the default ui
#if defined(USE_UI_GTKMM)
      ui_tmp = UI::new_(UI_TYPE::GTKMM_DOKO);
#elif defined(USE_UI_AATEXT)
      ui_tmp = UI::new_(UI_TYPE::AATEXT);
#elif defined(USE_UI_TEXT)
      ui_tmp = UI::new_(UI_TYPE::TEXT);
#else
      ui_tmp = UI::new_(UI_TYPE::DUMMY);
#endif
    }

#ifdef RELEASE
    if (FAST_PLAY)
      ::setting.set(Setting::SAVE_PARTY_CHANGES, false);
#endif

    { // set the ui
      delete static_cast<UI_Wrap*>(::ui)->ui;
      static_cast<UI_Wrap*>(::ui)->ui = ui_tmp;

      if (SEED_OUT) {
	delete static_cast<UI_Wrap*>(::ui)->ui;
	static_cast<UI_Wrap*>(::ui)->ui = UI::new_(UI_TYPE::DUMMY);
      } // if (SEED_OUT)

      // test, whether the ui is valid
      if ((::ui->type() == UI_TYPE::DUMMY)
	  && !FAST_PLAY) {
	cerr << "Uitype is 'dummy', aborting." << endl;
#ifdef RELEASE
	exit(1);
#endif
      } // if (::ui->type() == UI::NONE)
    } // set the ui

    // add the bug report in the ui.wrap,
    // so it gets the information of the gameplay
    static_cast<UI_Wrap*>(::ui)->append(&::bug_report);

    { // if this is the first start, create some directories
      vector<string> dirnames;
      dirnames.push_back(::setting(Setting::PRIVATE_DATA_DIRECTORY));
      dirnames.push_back(::setting(Setting::PRIVATE_DATA_DIRECTORY)
			 + "/" + ::setting(Setting::PARTIES_DIRECTORY));
      dirnames.push_back(::setting(Setting::PRIVATE_DATA_DIRECTORY)
			 + "/" + ::setting(Setting::RULES_DIRECTORY));
      dirnames.push_back(::setting(Setting::PRIVATE_DATA_DIRECTORY)
			 + "/" + ::setting(Setting::AI_DIRECTORY));
      Translator::Translation information = ::translation("");

      for (vector<string>::iterator dir = dirnames.begin();
	   dir != dirnames.end();
	   dir++) {
	if (!DK::Utils::File::isdirectory(*dir)) {
	  information += (::translation("Message::creating directory \'%sdir%\'",
					*dir)
			  + "\n");
#ifdef WINDOWS
	  mkdir(dir->c_str());
#else
	  mkdir(dir->c_str(), 00700);
#endif
	} // if (!isdirectory(*dir))
      } // for (dir \in dirnames)
      if (!information.empty())
	::ui->information(information, INFORMATION::NORMAL);
    } // if this is the first start, create some directories

    { // read the version of the last usage of FreeDoko and write the new version

      // read the old version
      DK::Utils::Version* const version_old
	= DK::Utils::new_Version_from_file(::setting(Setting::PRIVATE_DATA_DIRECTORY)
					   + "/Version");

      { // write the new version
	ofstream ostr((::setting(Setting::PRIVATE_DATA_DIRECTORY)
		       + "/Version").c_str());
	if (!ostr.fail())
	  ostr << version;
      } // write the new version

      if (version_old == NULL) {
	// This seems to be the first start -- give some introduction.
	::ui->first_run(::translation("Greeting::first run"));
      } else { // if !(version_old == NULL)
#ifdef RELEASE
	// give information if the version is updated
	if (*version_old < version)
	  ::ui->program_updated(*version_old);
#endif

#ifndef OUTDATED
	if (*version_old < DK::Utils::Version(0, 6, 3)) {
	  ::ui->information(::translation("ChangeLog::Adjusting settings time"),
			    INFORMATION::NORMAL);
	  // adjust the times in the settings
	  ::setting.set(Setting::CARD_PLAY_DELAY,
			100 * ::setting(Setting::CARD_PLAY_DELAY));
	  ::setting.set(Setting::FULL_TRICK_CLOSE_DELAY,
			100 * ::setting(Setting::FULL_TRICK_CLOSE_DELAY) );
	  ::setting.set(Setting::GAMETYPE_WINDOW_CLOSE_DELAY,
			100 * ::setting(Setting::GAMETYPE_WINDOW_CLOSE_DELAY));
	  ::setting.set(Setting::MARRIAGE_WINDOW_CLOSE_DELAY,
			100 * ::setting(Setting::MARRIAGE_WINDOW_CLOSE_DELAY));
	  ::setting.set(Setting::GENSCHER_WINDOW_CLOSE_DELAY,
			100 * ::setting(Setting::GENSCHER_WINDOW_CLOSE_DELAY));
	  ::setting.set(Setting::ANNOUNCEMENT_WINDOW_CLOSE_DELAY,
			100 * ::setting(Setting::ANNOUNCEMENT_WINDOW_CLOSE_DELAY));
	  ::setting.set(Setting::SWINES_WINDOW_CLOSE_DELAY,
			100 * ::setting(Setting::SWINES_WINDOW_CLOSE_DELAY));
	} // if !(version_old == NULL)
#endif

	delete version_old;
      } // if (version_old != NULL)

    } // read the version of the last usage of FreeDoko and write the new version

    // init the ui
    ::ui->init(argc = 1, argv);

    if (!::bug_report_replay) {
      // load the party (either the one given on the command line
      // or the default one
      if (!party_filename.empty()) {
	if (party_filename != "-") {
	  try {
	    ::party.load(party_filename);
	    ::game_status = GAMESTATUS::PARTY_INITIAL_LOADED;
	  } catch (ReadException const& exception) {
	    ::ui->information(::translation("Party load error: '%sfilename%'",
					    party_filename)
			      + "\n"
			      + exception.message(),
			      INFORMATION::PROBLEM);
	  }
	}
      } else { // if !(party_filename.empty())
	party_filename = (::setting(Setting::PRIVATE_DATA_DIRECTORY)
			  + "/"
			  + ::setting(Setting::PARTIES_DIRECTORY)
			  + "/"
			  + "current");
	if (DK::Utils::File::isfile(party_filename))
	  if (::party.load(party_filename))
	    ::game_status = GAMESTATUS::PARTY_INITIAL_LOADED;
      } // if !(party_filename.empty())
    } // if (!::bug_report_replay)

    // load the default rules from the private data directory
    if (::game_status != GAMESTATUS::PARTY_INITIAL_LOADED)
      ::party.rule().load(::setting(Setting::PRIVATE_DATA_DIRECTORY)
			  + "/"
			  + ::setting(Setting::RULES_DIRECTORY)
			  + "/last");

    // If no game has been saved in the party, the ui gets a problem with
    // a random startplayer.
    // So we check here, whether a game was already played.
    // Here, because so the rules and players are taken from the saved party
    // and not from the other files.
    if (::party.gameno() == 0)
      ::game_status = GAMESTATUS::PARTY_NEW;

    // now the preparations have finished

    if (::game_status == GAMESTATUS::PROGRAMSTART)
      ::game_status = GAMESTATUS::PARTY_NEW;
    while (::game_status != GAMESTATUS::QUIT) {
      try {
	try {
	  ::party.open();
	  ::ui->party_open(::party);

#ifdef USE_NETWORK
#ifdef DKNOF
	  // for network testing:
	  //   create a connection to an already running FreeDoko server
	  if (   (::server->ports().size() == 1)
	      && (::server->ports().front()
		  != Network::Server::FREEDOKO_STANDARD_PORT)) 
	    ::server->create_connection("localhost",
					Network::Server::FREEDOKO_STANDARD_PORT);
#endif
#endif
	  if ( (::game_status == GAMESTATUS::PARTY_INITIAL_LOADED)
	      || (::game_status == GAMESTATUS::PARTY_LOADED) ) {
	    ::ui->party_loaded();
	  } else {
	    ::party.remove_auto_save();
	    ::ui->party_get_settings();
	  }

	  if (::game_status == GAMESTATUS::PARTY_NEW)
	    ::party.init();

	  // start playing
	  ::game_status = GAMESTATUS::PARTY_PLAY;
	  ::party.play();
	} catch (GameStatus const& gs) {
	  if (gs == GAMESTATUS::PARTY_FINISHED)
	    ::game_status = GAMESTATUS::PARTY_FINISHED;
	  else
	    throw;
	} // try
	::ui->party_finish();

	::game_status = GAMESTATUS::PARTY_NEW;
      } catch (GameStatus const& gs) {
	DEBUG_ASSERTION(((gs == GAMESTATUS::PARTY_NEW)
			 || (gs == GAMESTATUS::PARTY_LOADED)
			 || (gs == GAMESTATUS::QUIT)),
			"main():\n"
			"  caught gamestatus '" << gs << "'");
	::game_status = gs;
      } catch (...) {
	::ui->party_close();
	::party.close();
	throw;
      }
      ::ui->party_close();
      if (::game_status != GAMESTATUS::PARTY_LOADED)
	::party.close();
    } // while (game_status != QUIT)

    // last chance to save the changes
    if (::setting(Setting::SAVE_PARTY_CHANGES))
      ::party.save_changes();
    ::setting.save();

    // clean up
    delete ::ui;
  } catch (std::exception const& e) {
    cerr << "main()\n"
      << "  uncaught standard exception: " << e.what() << endl;
    throw;
  } catch (...) {
    cerr << "main()\n"
      << "  uncaught unknown exception" << endl;
    throw;
  } // try

  return EXIT_SUCCESS;
} // int main(argc, argv)
