<?php

// this is used to (re)start the script without changing map, useful for live script tweaking
$SKIP_START = false;
global $argv;
if (isset($argv[1]) && $argv[1] == "skip") {
	$SKIP_START = true;
}

// --------------------------------------------

// careful here:
// log.php introduces _log function
include_once("internal/log.php");

// error_setup.php needs _log function, and makes 
// stdout silent for PHP
include_once("internal/error_setup.php");

// only now setup logging, i.e. clear the log
// -> clearing the log could produce PHP to output
//    in stdout if it fails, causing comms to off-sync
//    right from the start
include_once("internal/log_setup.php");

// --------------------------------------------

include_once("internal/helpers.php");
include_once("internal/comms.php");
include_once("internal/task_sequencer.php");

// --------------------------------------------

abstract class MetaGame {
	public $comms = NULL;
	public $task_sequencer = NULL;
	protected $trackers = array();
	protected $time_tracker;

	// --------------------------------------------
	public function init() {
		// initialize communications to/from server
		$this->comms = new Comms();
		$this->comms->init();

		$this->task_sequencer = new TaskSequencer();
		
		global $argv;
		if (test_parameter($argv, "single_player")) {
		} else {
			// now start server, if not started yet
			$this->start_server();
		}

		$this->time_tracker = microtime(true);
	}

	// --------------------------------------------
	public function run() {
		while (true) {
			{
				$time_start = $this->time_tracker;
				$this->time_tracker = microtime(true); 
				$time_end = $this->time_tracker; 

				$time = $time_end - $time_start;
				$this->update($time);
			}

			_log_inline(".");

			// blocks here until message received
			$data = $this->comms->receive();
			if ($data === false) break;

			if (!empty($data)) { 
				if (count($this->trackers) > 0) {
					$doc = new DOMDocument();
					$doc->loadXML($data);

					if ($doc->firstChild->localName == "user_quit_event") {
						_log("user quit signal detected, stopping script");
						break;
					}

					// inform all trackers about the event
					foreach ($this->trackers as $key => $tracker) {
						$tracker->handle_event($doc);
					}
				} else {
					_log("no event tracker registered, skipping message");
				}
			}
		}
	}

	// --------------------------------------------
	public function uninit() {
		_log("uninitializing");
		
		$this->comms->uninit();
	}

	// --------------------------------------------
	public function pre_begin_match() {
		_log("pre begin match");
		// also clear message queue just in case
		// - it's possible we received events from the game after we decided to change the map
		// - the events still in the queue would be related to the old map, and can be just ignored here
		$this->comms->clear_queue();
		
		$this->task_sequencer->clear();

		$this->clear_trackers();
	}

	// --------------------------------------------
	public function post_begin_match() {
		_log("post begin match");
	}

	// --------------------------------------------
	protected function start_server() {
		// get_start_server_command defined in server_settings.php
		$command = get_start_server_command();
		if ($command != "") {
			$this->comms->send($command);
		} else {
			_log("not starting server");
		}
	}

	// --------------------------------------------
	public function add_tracker($tracker) {
		$this->trackers[] = $tracker;
	}

	// --------------------------------------------
	protected function clear_trackers() {
		$this->trackers = array();
	}

	// --------------------------------------------
	protected function tracker_completed($tracker) {
		// by default, nothing to do here
	}

	// --------------------------------------------
	protected function update($time) {
		$this->task_sequencer->update($time);

		//_log("update: time since last time " . $t);
		//_log("  trackers: " . count($this->trackers));

		// maintain trackers
		foreach ($this->trackers as $key => $tracker) {
			// track passed time here and feed it to tracker, trackers can then
			// use if for updating timers and looking out for expiration to trigger stuff
			//

			// windows + PHP + named pipes (+ the fact that PHP thread support is less than perfect) 
			// has one issue:
			// - we need to pulse dummy events from the server to keep the script not blocking at
			//   checking if there's anything to read in the input pipe
			// - the pulses come in at max 1 second intervals, so the script won't update itself faster
			//   than that except when other events happen to come in

			$tracker->update($time);

			if ($tracker->has_ended()) {
				$this->tracker_completed($tracker);
				_log("tracker " . $key . " ended, removing from list");
				// cleanup
				unset($this->trackers[$key]);
			} else if (!$tracker->has_started()) {
				// not ended and not started
				// try to start it
				$tracker->start();
			}
		}
	}

}

?>