/*!
  \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:
    //! Pointers to neighbours
    Array<string > neighbours;

    //! 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->merger().covariance();
            if (sumsum(Cov)==0.0) {
                bdm_error("merging failed");
            }
            joint_pred.set_parameters(merger->merger().mean(), Cov);
            joint_pred.set_rv(merger->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
