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

#ifndef TRAGE_H
#define TRAGE_H


#include <base/participants.h>
	
using namespace bdm;

//! detector of traffic variables
class Detector{
	public:
		string name;             //! detector name
		int distance;		     //! distance from stop-line

		//! function loading info from Setting
		void from_setting(const Setting &set){
			UI::get(name,set,"name",UI::compulsory);
			UI::get(distance,set,"distance",UI::compulsory);
			
		}
};

//! detector fo incomming flow
class DetectorIn : public Detector{
	public:
	Array<string> to_sg; // to signal plans
	void from_setting(const Setting &set){
		Detector::from_setting(set);
		UI::get(to_sg, set, "to_sg", UI::compulsory);
	}
};

//! structure for output detectors
class RequestOut {
	public:
	string to; // agent
	Array<string> detectors; //detectors
	void from_setting(const Setting &set){
		UI::get(detectors,set,"detectors",UI::compulsory);
		UI::get(to, set, "to", UI::compulsory);
	}
};


//! class with physical information about a signal group
class SignalGroup {
	public:
		string name;                 //! names of the group
		Array<Detector> detectors;   //! (possible) detectors
		
		//! function that loads information from Setting
		void from_setting(const Setting &set);		
};

//! class that operates on a signal group
class SignalGroupHandler {
	protected:
		//! pointer to physical signal group
		SignalGroup *sg;
		
	public:
		//! actual data from the relevant signal group
		vec det_data;
		//! description of det_data
		RV rv_det_data;
		//! link from global measured data 
		datalink ds2data;
	public:
		void connect_sg(SignalGroup &sg0, const string &agent_name){
			sg=&sg0;
			for (int i=0;i<sg->detectors.length();i++){
				rv_det_data.add(RV(agent_name +"_"+ sg->detectors(i).name, 2)); //TODO intensity occupancy
			}
			
		}
		
		//! arbitrary function that computes the need of the signal group for green light in common units (number of waiting cars?)
		double expected_load(){
			if (det_data.length()>0){
				return det_data(0); // whatever
			} else {
				return 1.0; // mean value
			}
		}
};

class RequestHandler{
	protected:
		RequestOut *rq;
	public:
		RV rv;
	public:
		void connect_request(RequestOut &rq0, const string &agent_name){
			rq=&rq0;
			for (int i=0;i<rq->detectors.length();i++){
				rv.add(RV(rq->detectors(i), 2)); //TODO intensity occupancy
			}
		}
};

/*!
\brief  Basic Traffic Agent

*/
class BaseTrafficAgent : public Participant {
	protected:
		//! Signal Groups
		Array<SignalGroup> sg;

		Array<SignalGroupHandler> sgh;
										
		//!data from messages
		vec input_data;
		
		//! decription of msg_data
		RV input_rv;
		
		//! data to broadcast
		vec output_data;
		
		//! description of broadcast dataind
		RV output_rv;
		
		//! datalink from DS to output variables
		datalink ds2output;
					
		//! output recepient
		Array<RequestOut> requests;
		Array<RequestHandler> request_handler;
		
		//! action description
		RV action_rv;
		
		datalink_part action2ds;
		
	public:
		void validate(){
			// write internal checks if all was loaded OK
			
			// set action variable == this is a feature of the agent!
			action_rv = RV(name+"_Tc", 1); // <======= example
		}
		void receive(const Setting &msg){
			string what;
			UI::get(what, msg, "what", UI::compulsory);
			
			if (what=="data"){ // 
				// 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(input_rv); // 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(input_data, ind,  dt); //check size?
				}				
			} else {
				bdm_warning("Unknown message of type "+what);
			}
		}
		void log_register(logger &L, const string &prefix){
			root::log_register ( L, prefix );
			logrec->ids.set_size(sg.length()+2);
			int i;
			for (i=0;i <sg.length(); i++) {
				logrec->ids(i)=logrec->L.add_vector(sgh(i).rv_det_data, "");
			}
			logrec->ids(i)=logrec->L.add_vector(input_rv,"in_"); i++;
			logrec->ids(i)=logrec->L.add_vector(output_rv,"out_");
		}
		void log_write() const {
			int i;
			for (i=0;i <sg.length(); i++) {
				logrec->L.log_vector(logrec->ids(i), sgh(i).det_data);
			}
			logrec->L.log_vector(logrec->ids(i),input_data); i++;
			logrec->L.log_vector(logrec->ids(i),output_data); i++;
		}
		
		void broadcast(Setting& set){
			// broadcast data to all neighbours
			for (int i=0; i<request_handler.length(); i++){
				Setting &msg =set.add(Setting::TypeGroup);
				RequestHandler &R=request_handler(i);
				
				// copy from create message
				// create msg with fields {to=..., what=data, rv=..., value = ...}
				UI::save ( requests(i).to, msg, "to");
				UI::save ( (string)"data", msg, "what");
				UI::save ( &R.rv, msg, "rv");
				UI::save(output_data, msg, "value");
			}
		}
		void adapt(const vec &glob_dt){
			// copy data from global vector to sSGHandlers
			for (int i=0; i<sgh.length();i++){
				sgh(i).ds2data.filldown(glob_dt, sgh(i).det_data);
			}
			//copy data fro neighbours
			ds2output.filldown(glob_dt, output_data);
			// copy sg_length ... and others...
		}
		void act(vec &glob_ut){
			vec needs(sgh.length());
			for (int i=0; i<sgh.length();i++){
				needs(i) = sgh(i).expected_load();
			}
			vec action; // trivial stuff
			action2ds.filldown(action,glob_ut);
		}
		
		void ds_register(const DS &ds){
			//register ds2output
			ds2output.set_connection(output_rv, ds._drv());
			output_data.set_size(output_rv._dsize());
			for (int i=0; i<sgh.length(); i++){
				sgh(i).ds2data.set_connection(sgh(i).rv_det_data, ds._drv());
			}
			action2ds.set_connection( ds._urv(), action_rv);
		}
		
		void from_setting(const Setting &set);
};
UIREGISTER(BaseTrafficAgent);

#endif //TRAGE_H
