#pragma once
#ifndef LOCAL_MODEL_H_
#define LOCAL_MODEL_H_


#include "abc.h"
#include "neighbors.h"
#include "transformers.h"
#include "transformers.h"
#include "metrics.h"
#include "logistic_regression.h"

#define DIST_EPS 1E-6

using namespace neighbors;
//transform distance vector to sample weights vector
typedef vector<data_t>(*dist_tf_fptr)(const vector<data_t>& distances);
enum DistTfMethod { NONE, INVERSE, EXP10, EXP05, EXP20 };
const vector<string> DIST_TF_NAMES = {
	"f(d=dist(x,xi))=1.0",
	"f(d=dist(x, xi)) = 1 / d",
	"f(d=dist(x, xi)) = exp(d^2) / 1.0",
	"f(d=dist(x, xi)) = exp(d^2) / 0.5",
	"f(d=dist(x, xi)) = exp(d^2) / 2.0",
};

vector<data_t> disttf_none(const vector<data_t>& distances);
vector<data_t> disttf_inverse(const vector<data_t>& distances);
vector<data_t> disttf_exp05(const vector<data_t>& distances);
vector<data_t> disttf_exp10(const vector<data_t>& distances);
vector<data_t> disttf_exp20(const vector<data_t>& distances);
dist_tf_fptr get_disttf_func(DistTfMethod method);


struct LocalModelParameters {
	vector<data_t> feature_weights;
	size_t  n_neighbors = 100;
	Metric metric = Metric::EUCLIDEAN;
	DistTfMethod dist_tf = DistTfMethod::INVERSE;
	bool use_min_max = true;
	bool use_local_logit = true;
	double logit_eps = 0.0001;
	double logit_C = 1.0;
};

struct LocalModelClassificationDetails {
	label_t predicted;
	std::set<label_t> local_classes;
	vector<Neighbor> neighbors;
	vector<double> local_classes_probs;	//probabilities per class
};

class LocalModel : public ClassifierABC {
    Matrix<data_t> _data;
    vector<label_t> _targets;
	MinMaxScaler _minmax;
	Weight _weight;
	KNeighborsClassifier _knn;
	LocalModelParameters _params;
	
protected:
	//ClassifierAbc implementation
	void _fit(const Matrix<data_t>& data, const vector<label_t>& targets);
	vector<label_t> _predict(const Matrix<data_t>& data) const ;
	label_t _predict(const vector<data_t>& sample) const;
public:
	vector<Neighbor> get_neighbors(const vector<data_t>& sample, size_t n_neighbors) const;
	void set_parameters(const LocalModelParameters& params);
	void set_parameters(const LocalModelParameters* params);
	LocalModel();
	LocalModel(const LocalModelParameters& params);
	LocalModel(const LocalModelParameters* params);
	LocalModelClassificationDetails get_classif_details(const vector<data_t>& sample);


};



#endif