#pragma once
#ifndef OPTIMIZATION_H
#define OPTIMIZATION_H

#include <vector>
//#include <thread>
#include <mutex>
#include <algorithm>
#include <sstream>
#include "abc.h"
#include "common.h"
#include "dll_api\interop_negotiator.h"

#define _DEFAULT_CD_INIT_VAL_ 1.0
#define _DEFAULT_CD_GRID_ 0.0, 0.3, 0.5, 1.0, 2.0, 3.0

using std::string;

namespace optimization {
    //function to optimize (fitness function)
    struct FitnessABC {
        virtual score_t func(const vector<data_t>& x) = 0;
    };

    class OptimizerABC {
    private:
		std::stringstream _ss;
        vector<data_t> _xopt;
        score_t _fopt;
        Matrix<data_t> _xs; //func(_xs[i]) = _fs[i]
        vector<score_t> _fs;
        vector<size_t> _n_evals;
        size_t _total_evals;
        FitnessABC* _fitness;
    protected:
        void _find_opt();
        virtual void _run(const InteropNegotiator& negotiator) = 0;
    public:
        bool cancellation_pending = false;
        //best x getter
        const vector<data_t>& xopt() const { return _xopt; }
        //best x setter
        OptimizerABC& xopt(const vector<data_t>& x);
        //best x getter
        const score_t& fopt() const { return _fopt; }
        //best function value setter
        OptimizerABC& fopt(score_t fval);
        //evaluated inputs
        const Matrix<data_t>& xs() const { return _xs; };
        //fintess function values
        const vector<score_t>& fs() const { return _fs; };
        //number of funcion evaluations (per iteration)
        const vector<size_t>& n_evals() const { return _n_evals; }
        //total number of function evaluation
        const size_t& total_evals() const { return _total_evals; }
        //fitness function
        score_t func(const vector<data_t>& x);
        //find local minimum
        void run(FitnessABC* fitness, const InteropNegotiator& negotiator);
    };


    struct CoordinateDescentSettings{
        vector<data_t> x_init;
        //x_grid.size() - number of coordinates, x_grid[k] - search space at k coordinate 
        vector<vector<data_t> >  x_grid;
        size_t n_iter = 1;
        bool random_order = false;

		CoordinateDescentSettings(size_t n_dimensions);
        CoordinateDescentSettings() {};
        //CoordinateDescentSettings(const CoordinateDescentSettings&) = default;
        //CoordinateDescentSettings& operator=(const CoordinateDescentSettings&) = default;
    };


    class CoordinateDescent : public OptimizerABC {
    private:
        CoordinateDescentSettings _settings;
        vector<double> _current_x;

    public:
        CoordinateDescent()  {}
        CoordinateDescent(const CoordinateDescentSettings& settings);
        //CoordinateDescent(const CoordinateDescent&) = default;
        //~CoordinateDescent() = default;
        void _run(const InteropNegotiator& negotiator);

    };



} //namespace optimization
#endif