/*!
  \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{
	string from; // from agent
	Array<string> to_sg; // to signal plans
	void from_setting(const Setting &set){
		Detector::from_setting(set);
		UI::get(from,set,"from",UI::compulsory);
		UI::get(to_sg, set, "to_sg", UI::compulsory);
	}
};

//! structure for output detectors
class RequestOut {
	public:
	string to; // agent
	Array<string> from; //detectors
	void from_setting(const Setting &set){
		UI::get(from,set,"from",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;
		//! link from data to global out_data
		datalink data2out;
	public:
		SignalGroupHandler(SignalGroup *sg0, const string &agent_name):rv_det_data(){
			sg=sg0;
			for (int i=0;i<sg->detectors.length();i++){
				rv_det_data.add(RV(agent_name + sg->detectors(i).name, 1)); //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;
		datalink outdata2out;
	public:
		RequestHandler(RequestOut *rq0, const string &agent_name){
			rq=rq0;
			for (int i=0;i<rq->from.length();i++){
				rv.add(RV(agent_name + rq->from(i), 1)); //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;
			
		RV action_rv;
		
		datalink_part action2ds;
		
		//! output recepient
		Array<RequestOut> requests;
		Array<RequestHandler> request_handler;
		
	public:
		void validate(){
			// write internal checks if all was loaded OK
		}
		void receive(const Setting &msg){
			string what;
			UI::get(what, msg, "what", UI::compulsory);
			
			if (what=="data"){ // 
				// field data 
				//! decription of detected data
				shared_ptr<RV> rv=UI::build<RV>(msg,"rv",UI::compulsory);
				ivec ind=rv->dataind(input_rv); // position of rv in in_rv;
				if (ind.length()>0){ //data belong here
					vec dt;
					UI::get(dt, msg, "value",UI::compulsory);
					set_subvector(input_data, ind,  dt); //check size?
				}				
			} else {
				bdm_warning("Unknown message of type "+what);
			}
		}
		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
				Setting &m_to=msg.add("to",Setting::TypeString);
				m_to=requests(i).to;
				Setting &m_what=msg.add("what",Setting::TypeString);
				m_what="data";
				//Setting &m_data=msg.add("data",Setting::TypeGroup);
				Setting &m_rv=msg.add("rv",Setting::TypeGroup);
				R.rv.to_setting(m_rv);
				Setting &m_val=msg.add("value",Setting::TypeGroup);
				vec val =R.outdata2out.pushdown(output_data);
				UI::save(val, m_val);
			}
		}
		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 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 = needs/sum(needs); // trivial stuff
			action2ds.filldown(action,glob_ut);
		}
		
		void ds_register(const DS &ds){
			for (int i=0; i<sgh.length(); i++){
				sgh(i).ds2data.set_connection(ds._drv(), sgh(i).rv_det_data);
			}
			action2ds.set_connection(action_rv, ds._urv());
		}
		
		void from_setting(const Setting &set);
};
UIREGISTER(BaseTrafficAgent);

#endif //TRAGE_H
