<?php

include_once("trackers/map_rotator_invasion.php");

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

class MapRotatorAdventure extends MapRotatorInvasion {
	protected $next_stage_index = 0;
	protected $player_character_id = -1;
	protected $tracked_hitboxes = array();
	protected $extraction_hitboxes = NULL;
	protected $world_transport_map = array();
	protected $local_player_check_timer = 0.0;
	
	protected $LOCAL_PLAYER_CHECK_TIME = 5.0;

	// --------------------------------------------
	function init() {
		parent::init();

		$this->set_loop(false);

		$this->next_stage_index = $this->current_stage_index;

		$this->define_transport("map1", "hitbox_extraction8", "map8");
		$this->define_transport("map1", "hitbox_extraction6", "map6");
		$this->define_transport("map1", "hitbox_extraction3", "map3");    

		$this->define_transport("map2", "hitbox_extraction8", "map8");

		$this->define_transport("map3", "hitbox_extraction7", "map7");
		//$this->define_transport("map3", "hitbox_extraction9", "map9");
		$this->define_transport("map3", "hitbox_extraction1", "map1");
		$this->define_transport("map3", "hitbox_extraction4", "map4");

		$this->define_transport("map4", "hitbox_extraction3", "map3");
		$this->define_transport("map4", "hitbox_extraction3-1", "map3");

		$this->define_transport("map5", "hitbox_extraction6", "map6");
		$this->define_transport("map5", "hitbox_extraction6-2", "map6");
		$this->define_transport("map5", "hitbox_extraction6_3", "map6");

		$this->define_transport("map6", "hitbox_extraction5", "map5");
		//$this->define_transport("map6", "hitbox_extraction9", "map9");
		$this->define_transport("map6", "hitbox_extraction1", "map1");
		$this->define_transport("map6", "hitbox_extraction8", "map8");

		$this->define_transport("map7", "hitbox_extraction3", "map3");

		$this->define_transport("map8", "hitbox_extraction2", "map2");
		$this->define_transport("map8", "hitbox_extraction6", "map6");
		$this->define_transport("map8", "hitbox_extraction1", "map1");
	}

	// --------------------------------------------
	// --------------------------------------------
	// --------------------------------------------
	// --------------------------------------------
	// --------------------------------------------
	protected function ready_to_advance() {
		// in adventure mode, allow player to choose next map using hitboxes and choose when to get extraction there

		// - receive hitbox event
		// - determine next map from hitbox data, store as a variable
		// - call wait_and_start
		// - in wait_and_start get_next_map_index will return the correct map index

		// redetermine now that the map was completed, opens up the closed exit points
		$this->determine_extraction_hitbox_list();

		// hitboxes may have changed
		$this->refresh_hitboxes();

		// also at this point, when the map is getting completed, change the stage settings already
		// so that next time if we come back here, it's changed
		$stage = $this->stages[$this->current_stage_index];
		$stage = $this->get_generic_completed_stage($stage->map_info);
		$this->stages[$this->current_stage_index] = $stage;
	}

	// --------------------------------------------------------
	protected function refresh_hitboxes() {
		$this->metagame->clear_hitbox_associations("character", $this->player_character_id, $this->tracked_hitboxes);

		$list = $this->get_extraction_hitbox_list();
		if (!$list) return;

		$add_ids = array();
		$this->metagame->associate_hitboxes_ex($list, "character", $this->player_character_id, $this->tracked_hitboxes, $add_ids);

		// check add_ids, report them, but report only one instances
		$reported = array();
		foreach ($add_ids as $key => $id) {
			$index = $this->get_stage_index_from_extraction_hitbox_id($id);
			if (!array_key_exists($index, $reported)) {
				$text = "";
				if ($index >= 0 && $index < count($this->stages)) {
					$text = $this->stages[$index]->map_info->name;
					$reported[$index] = true;
				}
				if ($text != "") {
					$a = array("%map_name" => $text);
					$this->metagame->send_faction_message_key(0, "extraction point available", $a);
				}
			}
		}
	}

	// --------------------------------------------
	protected function get_stage_index_from_extraction_hitbox_id($id) {
		/*
		$offset = strpos($id, "stage");
		$offset += strlen("stage");
		$index = substr($id, $offset, 1);
		return $index;
		*/
		// bind maps together here rather than directly in maps themselves
		$target_stage = -1;

		if (array_key_exists($this->current_stage_index /*stage*/, $this->world_transport_map)) {
			$transport_map = $this->world_transport_map[$this->current_stage_index];
			if (array_key_exists($id, $transport_map)) {
				$target_stage = $transport_map[$id];
			} else {
				_log("ERROR, transport map for stage " . $this->current_stage_index . " doesn't have extraction id " . $id);
			}
		} else {
			_log("ERROR, stage " . $this->current_stage_index . " doesn't have transport map");
		}

		return $target_stage;
	}

	// --------------------------------------------------------
	protected function mark_extraction_points() {
		$list = $this->get_extraction_hitbox_list();
		if (!$list) return;

		$offset = 2000;
		for ($i = 0; $i < $list->length; ++$i) {
			$hitbox_node = $list->item($i);
			$id = $hitbox_node->getAttribute("id");
			$index = $this->get_stage_index_from_extraction_hitbox_id($id);
			$text = "?";
			if ($index >= 0 && $index < count($this->stages)) {
				$text = $this->stages[$index]->map_info->name;
			}
			$position = $hitbox_node->getAttribute("position");
			$color = "#FFFFFF";

			$command = "<command class='set_marker' id='" . $offset . "' faction_id='" . $this->friendly_id . "' atlas_index='1' text='" . $text . "' position='" . $position . "' color='" . $color . "' />\n";
			$this->metagame->comms->send($command);

			$offset++;
		}
	}

	// --------------------------------------------------------
	protected function unmark_extraction_points() {
		$list = $this->get_extraction_hitbox_list();
		if (!$list) return;

		$offset = 2000;
		for ($i = 0; $i < $list->length; ++$i) {
			$command = "<command class='set_marker' id='" . $offset  . "' enabled='0' />\n";
			$this->metagame->comms->send($command);

			$offset++;
		}
	}

	// ----------------------------------------------------
	protected function setup_character_for_tracking($id) {
		// it's the local player, do stuff now
		$this->metagame->clear_hitbox_associations("character", $this->player_character_id, $this->tracked_hitboxes);
		$this->player_character_id = $id;

		_log("setting up tracking for character " . $id);

		$list = $this->get_extraction_hitbox_list();
		if ($list) {
			$this->metagame->associate_hitboxes($list, "character", $this->player_character_id, $this->tracked_hitboxes);
		}
	}


	// ----------------------------------------------------
	protected function handle_player_spawn_event($doc) {
		_log("MapRotatorAdventure::handle_player_spawn_event");
		// don't care if already about to change map
		if ($this->is_about_to_change_map()) {
			_log("skipping");
			return;
		}

		$event = $doc->firstChild;

		$elements = $event->getElementsByTagName("player");
		if ($elements->length > 0) {
			$element = $elements->item(0);
			$name = utf_to_game($element->getAttribute("name"));
			_log("player spawned: " . $name . ", target username is " . $this->metagame->username);
			if ($name == $this->metagame->username) {
				_log("is local");

				$this->setup_character_for_tracking($element->getAttribute("character_id"));
			}
		} else {
			_log("no player elements?");
		}
	}

	// ----------------------------------------------------
	public function update($time) {
		// workaround:
		// - apparently it happens that player spawn events aren't always carried through
		// - could be related to loading a campaign; maybe player spawns before the script starts?
		// - to workaround those cases, ensure that we are tracking a character here, and if we are not, query if the local player exists
		if ($this->player_character_id < 0) {
			$this->local_player_check_timer -= $time;
			if ($this->local_player_check_timer < 0.0) {
				_log("tracked player character id " . $this->player_character_id);
				$player = $this->metagame->query_local_player();
				if ($player) {
					$this->setup_character_for_tracking($player->getAttribute("character_id"));
				} else {
					_log("local player query failed");
				}
				$this->local_player_check_timer = $this->LOCAL_PLAYER_CHECK_TIME;
			}
		}
	}

	// ----------------------------------------------------
	protected function is_about_to_change_map() {
		return $this->next_stage_index != $this->current_stage_index;

	}
	// ----------------------------------------------------
	protected function handle_hitbox_event($doc) {
		// don't care if already about to change map
		if ($this->is_about_to_change_map()) return;

		// hitbox_id
		// instance_type (vehicle/character)
		// instance_id
		$event = $doc->firstChild;

		_log("handle_hitbox_event, type=" . $event->getAttribute("instance_type") . ", id=" . $event->getAttribute("instance_id") . ", hitbox=" . $event->getAttribute("hitbox_id"));

		if ($event->getAttribute("instance_type") == "character" &&
			$event->getAttribute("instance_id") == $this->player_character_id) {

			// this event concerns our master player
			//$this->metagame->comms->send("say hello there, you reached " . $event->getAttribute("hitbox_id"));

			$id = $event->getAttribute("hitbox_id");
			$index = $this->get_stage_index_from_extraction_hitbox_id($id);

			_log("next stage is " . $index);

			// clear hitbox checking now
			$this->metagame->clear_hitbox_associations("character", $this->player_character_id, $this->tracked_hitboxes);

			$this->change_map($index);
		}
	}

    // ----------------------------------------------------
	// debugging tools
    protected function handle_chat_event($doc) {
		// player_id
		// player_name
		// message
		// global
		$event = $doc->firstChild;

		$message = $event->getAttribute("message");
		// for the most part, chat events aren't commands, so check that first
		if (!starts_with($message, "/")) {
				return;
		}

		$sender = $event->getAttribute("player_name");
		$sender_id = $event->getAttribute("player_id");
		//if (!is_admin($sender)) {
		//	return;
		//}

        if (check_command($message, "warp")) {
			$parameters = parse_parameters($message, "warp");
			if (count($parameters) > 0) {
				$index = $parameters[0];
				$this->metagame->comms->send("say warping to " . $index);

				$this->change_map($index);
			}
		}
	}

	// -------------------------------------------------------
	protected function change_map($index) {
		// commit to this map change
		$this->next_stage_index = $index;

		// query current status about bases, and store it, we may want to continue from where we left
		{
			$base_query_doc = new DOMDocument();
			$command = 
				"<command class='make_query' id='base_query_1'>\n" .
				"	<data class='bases' />\n" .
				"</command>";
			$base_query_doc->loadXML($command);

			// send the query command now
			$base_doc = $this->metagame->comms->query($base_query_doc);

			$list = NULL;
			if ($base_doc) {					
				$stage = &$this->stages[$this->current_stage_index];
				// clear owned base information in factions now
				foreach ($stage->factions as $key => $f) {
					$f->owned_bases = array();
				}

				$list = $base_doc->getElementsByTagName("base");

				for ($i = 0; $i < $list->length; ++$i) {
					$node = $list->item($i);
					$id = $node->getAttribute("id");
					$owner_id = $node->getAttribute("owner_id");
					$name = $node->getAttribute("name");

					if ($owner_id >= 0 && $owner_id < count($stage->factions)) {
						$f = &$stage->factions[$owner_id];
						$f->owned_bases[] = $id;
					}
				}
			}
		}

		$this->metagame->send_faction_message_key(0, "standby for extraction");

		$this->wait_and_start(1, false);
	}

	// -------------------------------------------------------
	protected function define_transport($source_map_name, $hitbox_id, $target_map_name) {
		$source_stage_index = get_stage_index(get_map_index_from_map_name($source_map_name));
		$target_stage_index = get_stage_index(get_map_index_from_map_name($target_map_name));

		if (!array_key_exists($source_stage_index, $this->world_transport_map)) {
			// add new container
			$this->world_transport_map[$source_stage_index] = array();
		}

		$this->world_transport_map[$source_stage_index][$hitbox_id] = $target_stage_index;
	}

	// -------------------------------------------------------
	protected function get_extraction_hitbox_list() {
		// return cached stuff
		return $this->extraction_hitboxes;
	}

	// -------------------------------------------------------
	protected function determine_extraction_hitbox_list() {
		// query about hitboxes
		$list_query_doc = new DOMDocument();
		$command = 
			"<command class='make_query' id='extraction_hitbox_query_1'>\n" .
//			"	<data class='hitboxes' faction_id='" . $this->friendly_id . "' />\n" .
			"	<data class='hitboxes' />\n" .
			"</command>";
		$list_query_doc->loadXML($command);

		// send the query command now
		// -- using query-call, it will block here until the response is got from the server
		$list_doc = $this->metagame->comms->query($list_query_doc);

		$list = NULL;
		if ($list_doc) {
			$list = $list_doc->getElementsByTagName("hitbox");

			// go through the list and only leave the ones in we're interested of, extraction_*
			for ($i = 0; $i < $list->length; ++$i) {
				$hitbox_node = $list->item($i);
				$id = $hitbox_node->getAttribute("id");
				$rule_out = false;
				if (strpos($id, "extraction") === false) {
					$rule_out = true;

				// - if the current map is completed, all exit points are ok
				// - if the current map is not completed, only the exit points taking to a completed map are ok, check that
				} else if (!$this->is_stage_completed($this->current_stage_index)) {
					// current map not completed

					$stage_index = $this->get_stage_index_from_extraction_hitbox_id($id);
					if (!$this->is_stage_completed($stage_index)) {
						// the map this point takes to is not either
						$rule_out = true;
					} else {
						// this point takes to a completed map, it's ok
					}

				} else {
					// current stage completed, all exit points are fine
				}

				if ($rule_out) {
					_log("ruling out " . $id);
					// remove this
					$hitbox_node->parentNode->removeChild($hitbox_node);
					// one step back
					$i--;
				} else {
					_log("including " . $id);
				}
			}
			_log("* " . $list->length . " extraction points found");
		}

		$this->extraction_hitboxes = $list;

		// update map markers
		$this->mark_extraction_points();

		// update world markers
		if ($this->extraction_hitboxes) {
			$transports = array();
			$transport_map = $this->world_transport_map[$this->current_stage_index];
			$source_map_index = get_map_index($this->current_stage_index);
			foreach ($transport_map as $hitbox_id => $target_stage_index) {
				// only show the hitboxes available that are among our accepted extraction hitboxes
				for ($i = 0; $i < $this->extraction_hitboxes->length; ++$i) {
					$node = $this->extraction_hitboxes->item($i);
					$id = $node->getAttribute("id");
					if ($id == $hitbox_id) {
						$target_map_index = get_map_index($target_stage_index);
						$transports[] = get_transport_name($source_map_index, $target_map_index);
					}
				}
			}
			$this->world->set_available_transports($transports);
		}
	}

	// --------------------------------------------
	protected function get_next_stage_index() {
		return $this->next_stage_index;
	}

	// --------------------------------------------
	protected function get_generic_completed_stage($map_info) {
		global $_faction_configs;

		$stage = new Stage();
		$stage->map_info = $map_info;

		$stage->max_soldiers = 40;

		{
			$f = new Faction($_faction_configs[0], create_commander_ai_command(0));
			$stage->factions[] = $f;
		}

		return $stage;
	}

	// --------------------------------------------
    public function start_map($index, $begin_only = false) {
		parent::start_map($index, $begin_only);

		$this->determine_extraction_hitbox_list();

		$this->refresh_hitboxes();
	}

	// --------------------------------------------
	public function save(&$doc, &$root) {
		$parent = $root;
		$subroot = $doc->createElement("map_rotator");
		foreach ($this->stages_completed as $key => $value) {
			$e = $doc->createElement("map_completed");
			$e->setAttribute("index", $key);
			$subroot->appendChild($e);
		}
		foreach ($this->stages as $key => $stage) {
			$e = $doc->createElement("stage");
			//$e->setAttribute("index", $key);
			foreach ($stage->factions as $key2 => $f) {
				$e2 = $doc->createElement("faction");
				$e2->setAttribute("index", $f->config->index);
				foreach ($f->owned_bases as $key3 => $base_id) {
					$e3 = $doc->createElement("base");
					$e3->setAttribute("id", $base_id);
					$e2->appendChild($e3);
				}
				$e->appendChild($e2);
			}
			$subroot->appendChild($e);
		}
		$parent->appendChild($subroot);
	}

	// --------------------------------------------
	public function load(&$doc, &$root) {
		_log("loading map rotator");
		$this->stages_completed = array();

		$subroot = $root->getElementsByTagName("map_rotator")->item(0);
		if ($subroot) {
			_log("map_rotator found");
			$list = $subroot->getElementsByTagName("map_completed");
			for ($i = 0; $i < $list->length; $i++) {
				$e = $list->item($i);
				$index = $e->getAttribute("index");
				_log("index=" . $index);
				$this->stages_completed[$index] = true;
			}

			$list = $subroot->getElementsByTagName("stage");
			_log("stages: " . $list->length);
			for ($i = 0; $i < $list->length; $i++) {
				$e = $list->item($i);
				$list2 = $e->getElementsByTagName("faction");
				_log("factions: " . $list2->length);
				for ($i2 = 0; $i2 < $list2->length; $i2++) {
					$e2 = $list2->item($i2);

					if ($this->stages[$i]->factions < $i2) {
						// assuming here that the participants in a map do not change, yet
						$f = &$this->stages[$i]->factions[$i2];
						$f->owned_bases = array();

						$faction_index = $e2->getAttribute("index");
						$list3 = $e2->getElementsByTagName("base");
						for ($i3 = 0; $i3 < $list3->length; $i3++) {
							$e3 = $list3->item($i3);
							$base_id = $e3->getAttribute("id");
							$f->owned_bases[] = $base_id;
							_log("base " . $base_id . " owned by " . $f->config->index);
						}
					}
				}
			}

			// in adventure mode, we can't determine current/next map from completed maps, 
			// so we need to obtain it somewhere else
			// general info has been retrieved from game by now, so determine it from there
			{
				$map_path = $this->metagame->game_map_path;
				$index = get_map_index_from_map_name($map_path);
				$this->next_stage_index = get_stage_index($index);
				$this->current_stage_index = $this->next_stage_index;
				_log("current/next stage at load: " . $this->current_stage_index);
			}

		} else {
			_log("map_rotator not found?");
		}

		// if maps have been completed, 
		// we should modify the stages 
		// to reflect that
		foreach ($this->stages_completed as $key => $value) {
			$stage = $this->stages[$key];
			$stage = $this->get_generic_completed_stage($stage->map_info);
			$this->stages[$key] = $stage;
		}

		// if current map is completed, refresh extraction points
		if ($this->current_stage_index < count($this->stages_completed) && 
			$this->is_stage_completed($this->current_stage_index)) {
			_log("current map is completed, refresh extraction hitboxes");
			$this->determine_extraction_hitbox_list();
			$this->refresh_hitboxes();
		}
	}


}


?>