/*!
\file
\brief Traffic Light Agents 
\author Vaclav Smidl.
*/

#ifndef TRAGE_H
#define TRAGE_H


#include <base/participants.h>

using namespace bdm;

class BaseTrafficAgent;

//! detector of traffic variables
class Lane{
public:
	Array<string> inputs;
	Array<string> outputs;
	vec input_distances;
	vec output_distances;
	vec alpha; //size of outputs
	//! percent of queue lenght (for "multiline queues")
	double beta;
	string queue;
	string sg;

	//! function loading info from Setting
	void from_setting(const Setting &set){
		UI::get(inputs,set,"inputs",UI::compulsory);
		UI::get(outputs,set,"outputs",UI::compulsory);
		UI::get(input_distances,set,"input_distances",UI::compulsory);
		UI::get(output_distances,set,"output_distances",UI::compulsory);
		UI::get(alpha,set,"alpha",UI::compulsory);
		UI::get(queue,set,"queue",UI::compulsory);
		UI::get(sg,set,"sg",UI::compulsory);
		UI::get(beta, set, "beta");
	}
};

//! class that operates on a signal group
class LaneHandler {
protected:
	//! pointer to physical lane
	const Lane &lane;
	//! agent pointer
	BaseTrafficAgent *agent;

public:
	//! actual data from the relevant signal group
	vec inputs;
	//! queue length
	double queue; 
	//! description of det_data
	RV rv_inputs;
	//! description of det_data
	RV rv_outputs;
	//! description of det_data
	RV rv_queue;
	//! link from global measured data 
	datalink agentin2input;
	//!
	datalink output2agentout;
	//!
	int queue_index;

public:
	LaneHandler(const Lane &lane0): lane(lane0){
		for (int i=0;i<lane0.inputs.length();i++){
			rv_inputs.add(RV(lane.inputs(i), 2));
		}
		for (int i=0;i<lane0.outputs.length();i++){
			rv_outputs.add(RV(lane.outputs(i), 2)); 
		}
		rv_queue.add(RV(lane.queue, 1)); 
		inputs.set_size(rv_inputs._dsize());
	}


	void connect_data(BaseTrafficAgent &agent0);

	//! computes expected density in cars/s
	double expected_density();

	//! arbitrary function that computes the need of the signal group for green light in common units (number of waiting cars?)
	double expected_output(double green_time);
};

/*!
\brief  Basic Traffic Agent

*/
class BaseTrafficAgent : public Participant {

LOG_LEVEL(BaseTrafficAgent,logdata);
public:
	//! Signal Groups
	Array<Lane> lanes;

	Array<LaneHandler*> lanehs;

	//!data from messages
	vec inputs;

	//! decription of msg_data
	RV rv_inputs;

	//! data to broadcast
	vec outputs;

	//! description of broadcast dataind
	RV rv_outputs;

	vec queues;

	//! description of queues
	RV rv_queues;

	//! 
	vec green_starts;

	//!
	vec green_times;

	//!
	Array<string> green_names;

	//!
	Array<string> stage_names;

	//!
	vec stage_times;





	//! datalink from DS to input variables
	datalink ds2inputs;

	//! datalink from DS to output variables
	datalink ds2queues;

	//! action description
	RV rv_action;

	datalink_part action2ds;

	Array<string> neighbours;

	Array<RV> rv_neighbours_out;

	Array<datalink> output2neighbour;

	//! simulator's step length in seconds
	static const int step_length=90;

	//! lenght of cycle in seconds
	static const int cycle_length=80;



public:
	void validate(){

		lanehs.set_length(lanes.length());
		for (int l=0; l<lanes.length(); l++){
			lanehs(l) = new LaneHandler(lanes(l));

			rv_inputs.add(lanehs(l)->rv_inputs);
			rv_outputs.add(lanehs(l)->rv_outputs);
			rv_queues.add(lanehs(l)->rv_queue);
		}
		inputs.set_size(rv_inputs._dsize());
		outputs.set_size(rv_outputs._dsize());
		queues.set_size(rv_queues._dsize());

		for (int l=0; l<lanes.length(); l++){
			lanehs(l)->connect_data(*this);
		}

		//for -- rv_outputs -- 
		// TODO vybrat rv pro sousedy 
		rv_neighbours_out.set_length(neighbours.length());
		output2neighbour.set_length(neighbours.length());

		for (int i=0; i<neighbours.length(); i++){
			for (int r=0; r<rv_outputs.length(); r++){
				int str_pos = rv_outputs.name(r).compare(neighbours(i));
				if (str_pos>(int)neighbours(i).length()){
					rv_neighbours_out(i).add(rv_outputs.subselect(vec_1(r)));
				}
			}
			// connect datasource
			output2neighbour(i).set_connection(rv_neighbours_out(i), rv_outputs);
		}

		// lanehs knows RVS
		// write internal checks if all was loaded OK

	}
	void receive(const Setting &msg){
		string what;
		UI::get(what, msg, "what", UI::compulsory);

		if (what=="new_stable_state"){ // 
			// field data 
			// extract decription of teh received datavector
			shared_ptr<RV> rv=UI::build<RV>(msg,"rv",UI::compulsory);
			// find if it is needed
			ivec ind=rv->dataind(rv_inputs); // position of rv in in_rv;
			if (ind.length()>0){ //data are interesting
				vec dt;
				UI::get(dt, msg, "value",UI::compulsory); // get data
				set_subvector(inputs, ind,  dt); //check size?
			}				
		} else {
			Participant::receive(msg);
		}
	}
	void log_register(logger &L, const string &prefix){
		root::log_register ( L, prefix );
		if ( log_level[logdata]){
			L.add_vector ( log_level, logdata, RV ( 1 ), prefix );	
		}
	}
	void log_write() const {
		if (log_level[logdata]){
			log_level.store(logdata, inputs);
		}
	}

	void broadcast(Setting& set){
		// broadcast data to all neighbours
		for (int i=0; i<neighbours.length(); i++){
			Setting &msg =set.add(Setting::TypeGroup);

			// if...
			// copy from create message
			// create msg with fields {to=..., what=data, rv=..., value = ...}
			UI::save ( neighbours(i), msg, "to");
			UI::save ( (string)"new_stable_state", msg, "what");
			UI::save ( &(rv_neighbours_out(i)), msg, "rv");
			UI::save( output2neighbour(i).pushdown(outputs), msg, "value");
		}

	}
	void adapt(const vec &glob_dt){
		// copy data from global vector to sSGHandlers
		ds2inputs.filldown(glob_dt, inputs);
		//copy data from neighbours
		ds2queues.filldown(glob_dt, queues);
		// copy sg_length ... and others...
	}
	void act(vec &glob_ut){
		vec action; // trivial stuff
		action2ds.filldown(action,glob_ut);
	}

	void ds_register(const DS &ds){
		//register ds2output
		ds2inputs.set_connection(rv_inputs, ds._drv());
		ds2queues.set_connection(rv_queues, ds._drv());

		inputs.set_size(rv_inputs._dsize());
		action2ds.set_connection( ds._urv(), rv_action);

	}

	void from_setting(const Setting &set);
};
UIREGISTER(BaseTrafficAgent);

#endif //TRAGE_H
