#include "traffic_agent.h"
#include <list>

class GreenWaveTrafficAgent : public BaseTrafficAgent {
protected:
	double stable_state_loss;
	double best_offset;

	RV rv_recieved_exps;
	vec recieved_exps;
	
	list<string> seznam;
	

	double car_leaving; //s; how long is 1 car leaving queue

	bool recieved_exp_request;
			
	//counts all expected cars going from each lane
	void expected_cars(/*const string dest*/) {
		double start_time;

		for (int i=0;i<lanes.length();i++) {
			for (int j=0;j<lanes(i).outputs.length();j++) {
				//		if (lanes(i).outputs(j).substr(0,3)==dest) {
				//
				string group = name+"_"+lanes(i).sg;		//e.g. 495_VA
				ivec index=rv_inputs.dataind(RV(group,1));
				vec green_time=inputs(index);


				rv_outputs.add(RV(lanes(i).outputs(j)+"-"+group,3));
				outputs.set_size(rv_outputs._dsize());

				ivec indexes = rv_outputs.dataind(RV(lanes(i).outputs(j)+"-"+group,3));

				//Number of cars
				outputs(indexes(0))=lanehs(i)->expected_output(green_time(0));	

				start_time = green_starts(sg_index(lanes(i).sg)) + lanes(i).output_distances(j)/VP + planned_offset;
				//first car arrive time
				outputs(indexes(1))=start_time;									
				//last car arrive time
				outputs(indexes(2))= start_time + green_time(0);

				//}
			}
		}
	}

public:
	int last_offset;
	int planned_offset;
	double actual_rating;
	
	//! array of existing signal groups
	Array<string> sgs;
	//! relative starts of green for signal groups
	ivec green_starts;

	//! avarage speed of cars
	int VP;

	void validate() {
		rv_action = RV(name+"_offset", 1); // <======= example

		for (int i=0; i<sgs.length();i++) {
			rv_inputs.add(RV(name+"_"+sgs(i),1));
			
			//place for expected inputs
			//rv_expected_traffic.add(RV(name+"_"+sgs(i)+"_exp",3));
		}

		inputs.set_size(rv_inputs._dsize());
		//expected_traffic.set_size(rv_expected_traffic._dsize());
		
		BaseTrafficAgent::validate();
	}

	//! returns index of signal group sg
	int sg_index(string sg) {
		for (int i=0;i<sgs.length();i++) {
			if (sgs(i)==sg) {
				return i;
			}
		}
		return -1;
	}

	void adapt(const vec &glob_dt) {
		BaseTrafficAgent::adapt(glob_dt);

		planned_offset=last_offset;
	}
	//! counts actual rating using planned_offset and recieved_exps
	void count_rating() {
		double virtual_queue;
		double t_emptiyng;
		double t_green_begin;
		double t_green_end;
		double t_cars_begin;
		double t_cars_end;
		double t_mixing;
		bool found;

		actual_rating=0.0;

		for (int i=0;i<lanehs.length();i++) {

			//Finding, if we have some expectations
			found=false;
			for (int j=0;j<rv_recieved_exps.length();j++) {
				for (int k=0;k<lanes(i).inputs.length();k++) {
					int result=rv_recieved_exps.name(j).find(lanes(i).inputs(k)+"-");
					if (result>=0) {
						ivec indexes = rv_recieved_exps.dataind(RV(rv_recieved_exps.name(j),3));

						t_cars_begin=recieved_exps(indexes(1));
						t_cars_end=recieved_exps(indexes(2));
						found=true;
					}
				}
			}
			if (!found) {
				t_cars_begin=0;
				t_cars_end=cycle_time;
			}

			//counting rating
			t_green_begin=green_starts(sg_index(lanes(i).sg)) + planned_offset;
			ivec indexes = rv_inputs.dataind(RV(name+"_"+lanes(i).sg,1));
			t_green_end=t_green_begin+inputs(indexes(0));

			if (t_green_begin<t_cars_begin) {
				virtual_queue=lanehs(i)->queue-(t_cars_begin-t_green_begin)/car_leaving;
			} 
			else if (t_green_begin>t_cars_begin) {
				virtual_queue=lanehs(i)->queue+(t_green_begin-t_cars_begin)*lanehs(i)->expected_density();
			}
			//TODO lanehs(i)->queue je zatim prazdne => nesmyslne vysledky

			t_emptiyng=virtual_queue/(1/car_leaving - lanehs(i)->expected_density());
			t_mixing=min(t_green_end,t_cars_end)-max(t_green_begin,t_cars_begin);

			actual_rating+=max((t_mixing-t_emptiyng)*lanehs(i)->expected_density(),0.0);  
		}
	}


	void broadcast(Setting& set){
			
		//ask neighbours for exptected arrive times
		if (true) {
			for (int i=0; i<neighbours.length(); i++){
				Setting &msg =set.add(Setting::TypeGroup);

				UI::save ( neighbours(i), msg, "to");
				UI::save (name,msg,"from");
				UI::save ( (string)"expected_times_request", msg, "what");
			}
		}


		// broadcast expected cars
		if (!seznam.empty()) {
			do {
				vec value;
				
				expected_cars();
				Setting &msg =set.add(Setting::TypeGroup);
				UI::save ( seznam.back(), msg, "to");
				UI::save ( name, msg, "from");
				UI::save ( (string)"new_expected_cars", msg, "what");
				UI::save ( &(rv_outputs), msg, "rv");
				UI::save ( outputs, msg, "value");
				seznam.pop_back();

			} while (!seznam.empty());
			
		}

	}

	void receive(const Setting &msg){
		string what;
		string to;
		string from;
		vec value;
		RV *rv;
		
		UI::get(what, msg, "what", UI::compulsory);
		UI::get(to, msg, "to", UI::compulsory);
		UI::get(from, msg, "from");
		UI::get(rv, msg, "rv");
		UI::get(value, msg, "value");
		if (what=="expected_times_request"){ 
			recieved_exp_request=true;
			seznam.push_back(from);

		} 
		else if (what=="new_expected_cars") {
			rv_recieved_exps=*rv;
			recieved_exps=value;
			count_rating();
			//TODO skutecne vyjednavani
		}
		else {
			BaseTrafficAgent::receive(msg);
		}
	}
	
	void ds_register(const DS &ds) {
		BaseTrafficAgent::ds_register(ds);
		action2ds.set_connection( ds._urv(), rv_action);

	}

	void from_setting(const Setting &set) {
		BaseTrafficAgent::from_setting(set);
		
		car_leaving=2;
		VP=45;
		recieved_exp_request=false;
		
		// load from file
		UI::get(sgs, set, "sgs", UI::compulsory);	
		UI::get(green_starts, set, "green_starts", UI::compulsory);
		UI::get(last_offset, set, "offset", UI::compulsory);
	
	}
	void act(vec &glob_ut){
		vec action;
		action.set_size(rv_action._dsize());
		
		ivec index = rv_action.dataind(RV(name+"_offset",1));
		action(index(0))=planned_offset;

		action2ds.filldown(action,glob_ut);
	}

};
UIREGISTER(GreenWaveTrafficAgent);