#include "abc.h"


#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdio>


#pragma warning (disable : 4996)


void
XmlSerializableABC::from_xml_file(const std::string& filename) {
	std::ifstream ifs(filename, std::ios::in);
	if (!ifs.is_open()) 
		throw std::invalid_argument("failed to open " + filename);
	std::stringstream buffer;
	buffer << ifs.rdbuf();
	TCXMLnode *root = CxmlTextToNode(&std::string(buffer.str().c_str())[0], buffer.str().length());
	from_node(root);
	CxmlDestroyNode(root);
	ifs.close();
}

void
XmlSerializableABC::save_to_xml(const std::string& filename) const {
	TCXMLnode * rootnode = to_node();
	int txtsize;
	char *txt = CxmlNodeToText(rootnode, &txtsize);
	CxmlDestroyNode(rootnode);

	std::ofstream ofs(filename);
	ofs << txt;
	ofs.close();
	CxmlTextFree(txt);
}

void
BaseEstimatorABC::_check_fit_data(const Matrix<data_t>& data, const vector<label_t>& targets) const {
    //check number of samples
    if(data.n_rows() != targets.size())
        throw std::invalid_argument(
        "Wrong data and targets shapes, \
        		number of samples is should be the same as number of targets");

}

void
BaseEstimatorABC::_check_input_data(const Matrix<data_t>& data) const {
    //check number of features
    if(data.n_rows() < 1)
        throw std::invalid_argument("to predict you need at least one sample");
    if(data.n_cols() != _n_features)
        throw std::invalid_argument("Wrong number of features, cant predict");
}

void
BaseEstimatorABC::_check_input_data(const vector<data_t>& sample) const {
    //check number of features
    if(sample.size() != _n_features)
        throw std::invalid_argument("Wrong number of features, cant predict");
}

void
BaseEstimatorABC::_is_fitted() const {
    if(_n_features < 1 || _n_samples < 1)
        throw std::invalid_argument("Fit estimator first");
}


void 
ClassifierABC::fit(const Matrix<data_t>& data, const vector<label_t>& targets) {
    _check_fit_data(data, targets);
    _n_features = data.n_cols();
    _n_samples = data.n_rows();
    _fit(data, targets);
}

vector<label_t> 
ClassifierABC::predict(const Matrix<data_t>& data) const {
    _is_fitted();
    _check_input_data(data);
    return _predict(data);
}

label_t 
ClassifierABC::predict(const vector<data_t>& sample) const {
    _is_fitted();
    _check_input_data(sample);
    return _predict(sample);
}


void 
TransformerABC::fit(const Matrix<data_t>& data, const vector<label_t>& targets) {
    _check_fit_data(data, targets);
    _n_features = data.n_cols();
    _n_samples = data.n_rows();
    _fit(data, targets);
}

void 
TransformerABC::fit(const Matrix<data_t>& data) {
    _n_features = data.n_cols();
    _n_samples = data.n_rows();
    _fit(data);
}

Matrix<data_t> 
TransformerABC::transform(const Matrix<data_t>& data) const {
    _check_input_data(data);
    return _transform(data);
}

vector<data_t> 
TransformerABC::transform(const vector<data_t>& sample) const {
    _check_input_data(sample);
    return _transform(sample);
}

