//Abstract classes
#pragma once
#ifndef BASE_CLF_H
#define BASE_CLF_H

#include <vector>
#include "cxml/cxml.h"
#include "matrix.h"
#include "dll_api\interop_negotiator.h"


typedef double data_t;
typedef int label_t;
typedef double score_t;

template class Matrix<label_t>;
template class Matrix<data_t>;
//template class Matrix<size_t>;


class XmlSerializableABC {
public:
    void from_xml_file(const std::string& filename);
    void save_to_xml(const std::string& filename) const;
    virtual TCXMLnode* to_node(void) const = 0;
    virtual XmlSerializableABC& from_node(TCXMLnode* node) = 0;
};


class BaseEstimatorABC {
protected:
    size_t  _n_features;
    size_t  _n_samples;

    void _check_fit_data(const Matrix<data_t>& data, const vector<label_t>& targets) const;
    void _check_input_data(const Matrix<data_t>& data) const;
    void _check_input_data(const vector<data_t>& sample) const;
    void _is_fitted() const;
    BaseEstimatorABC() : _n_features(0), _n_samples(0) {}

public:
    const size_t& n_samples() const { return _n_samples; }
    const size_t& n_features() const { return _n_features; }
};

/*
Classifier that inherit ClassifierABC need to implement _fit, and _predict methods
*/
class ClassifierABC : public BaseEstimatorABC {
protected:
    virtual void _fit(const Matrix<data_t>& data, const vector<label_t>& targets) = 0;
    virtual vector<label_t> _predict(const Matrix<data_t>& data) const = 0;
    virtual label_t _predict(const vector<data_t>& sample) const = 0;

public:
	/*
	data - each row is a sample, size is (number of samples,  number of features)
	targets - target values, size if number of samples
	*/
    void fit(const Matrix<data_t>& data, const vector<label_t>& targets);
	/*
	predicts unknown samples
	when there are many samples to predict, it is better to use this method instead of "for" cycle
	*/
    vector<label_t> predict(const Matrix<data_t>& data) const;
	/* predic unknown sample*/
    label_t predict(const vector<data_t>& sample) const;
};


class TransformerABC : public BaseEstimatorABC {
protected:
    virtual void _fit(const Matrix<data_t>& data, const vector<label_t>& targets) = 0;
    virtual void _fit(const Matrix<data_t>& data) = 0;
    virtual Matrix<data_t> _transform(const Matrix<data_t>& data) const = 0;
    virtual vector<data_t> _transform(const vector<data_t>& sample) const = 0;

public:
	/*
	data - each row is a sample, size is (number of samples,  number of features)
	targets - target values, size if number of samples
	*/
    void fit(const Matrix<data_t>& data, const vector<label_t>& targets);
    void fit(const Matrix<data_t>& data);
	/*
	transform input data matrix
	return matrix with the same number of samples but number of feature may be modified
	when there are many samples to transform, it is better to use this method instead of "for" cycle
	*/
    Matrix<data_t> transform(const Matrix<data_t>& data) const;
    vector<data_t> transform(const vector<data_t>& sample) const;
};


#endif
