/*!
  \file
  \brief ARX Agents for MPDM
  \author Vaclav Smidl.
*/

#ifndef ARXAG_H
#define ARXAG_H


#include <design/arx_ctrl.h>
#include <stat/merger.h>
#include <base/participants.h>


namespace bdm{
	
/*!
\brief  ARX agent

*/
class ARXAgent : public Participant {
	protected:
		
		//! Internal ARX Controller
		shared_ptr<LQG_ARX> lqg_arx;
		//! Merger for predictors from neighbours
		shared_ptr<merger_mix > merger;
		//! All available predictors
		Array<shared_ptr<pdf> > preds;
		//! My own predictor
		mlnorm_chmat_ptr my_pred;
		
		//! data link of data
		datalink_part dlU;
		//! data link of 
		datalink_buffered dlDt;

		//! internal data vector for controller
		vec dt;
		//! internal data vector for controller output
		vec ut;
	public:
		void validate(){
			lqg_arx->validate();
			my_pred=lqg_arx->_ar().ml_predictor<chmat>();
		}
		void receive(const Setting &msg){
			string what;
			UI::get(what, msg, "what", UI::compulsory);
			
			if (what=="predictor"){ // add predictor 
				preds.set_length(preds.length()+1, true);
				preds(preds.length()-1) = UI::build<pdf>(msg["data"]);
			} else {
				bdm_warning("Unknown message of type "+what);
			}
		}
		void broadcast(Setting& set){
			// broadcast predictor (created in adapt) to all neighbours
			for (int i=0; i<neighbours.length(); i++){
				Setting &msg =set.add(Setting::TypeGroup);
				create_message(msg, neighbours(i), "predictor", *preds(0).get());
			}
		}
		void adapt(const vec &glob_dt){			
			dlDt.filldown(glob_dt, dt); 
			dlDt.store_data(glob_dt); // do not ever use dlDt again! 
			
			lqg_arx->adapt(dt);
			// remove old predictors
			preds.set_length(1, false);
			lqg_arx->_ar().ml_predictor_update<chmat>(*my_pred) ;
			preds(0)=my_pred;
			
			// we arer finished with datasource
		}
		void act(vec &glob_ut){
			if (preds.length()>1){
				merger->set_sources(preds);
				if (preds.length()>1){cout<<preds(1)->to_string()<<endl;}
				merger->merge();
				enorm<chmat> joint_pred; 
				mat Cov=merger->covariance();
				if (sumsum(Cov)==0.0){
					bdm_error("merging failed");
				}
				joint_pred.set_parameters(merger->mean(), Cov);
				joint_pred.set_rv(merger->_rv());
				
				enorm<chmat> marg_pred; // remove rvs taht should not be there
				joint_pred.marginal(concat(preds(0)->_rv(), preds(0)->_rvc()), marg_pred);
				
				// assign 
				mlnorm<chmat> *merg_pred = lqg_arx->_pred();
				marg_pred.condition(preds(0)->_rv(),*merg_pred);
			} else {
				lqg_arx->_ar().ml_predictor_update<chmat>(*lqg_arx->_pred()) ;
			}
			// 
			lqg_arx->redesign();
			ut=lqg_arx->ctrlaction(dt);
			dlU.filldown(ut,glob_ut);
		}
		virtual void ds_register(const DS &ds){
				dlDt.set_connection(lqg_arx->_rvc(), ds._drv() );
				dlU.set_connection(ds._urv() , lqg_arx->_rv() );
				dt=zeros(lqg_arx->_rvc()._dsize());
				ut=zeros(lqg_arx->_rv()._dsize());
		}
		void from_setting(const Setting &set){
			Participant::from_setting(set);
			lqg_arx = UI::build<LQG_ARX>(set, "lqg_arx",UI::compulsory);
			merger = UI::build<merger_mix>(set, "merger", UI::compulsory);
			UI::get(neighbours, set, "neighbours",UI::compulsory);
			validate();
		}
};
//UIREGISTER(LQG_ARX);
UIREGISTER(ARXAgent);

}//namespace
#endif //ARXAG_H
