//Example 21-1. Creating and training a decision tree

#include <opencv2/opencv.hpp>
#include <stdio.h>
#include <iostream>

using namespace std;
using namespace cv;

void help(char **argv) {
  cout << "\n\n"
       << "Using binary decision trees to learn to recognize poisonous\n"
       << "    from edible mushrooms based on visible attributes.\n" 
       << "    This program demonstrates how to create and a train a \n"
       << "    decision tree using ml library in OpenCV.\n"
       << "Call:\n" << argv[0] << " <csv-file-path>\n\n"
       << "\nIf you don't enter a file, it defaults to ../mushroom/agaricus-lepiota.data\n"
       << endl;
}

int main(int argc, char *argv[]) {
  // jeśli wywołujący podał nazwę pliku, to doskonale; w przeciwnym razie program używa domyślnej nazwy
  //
  const char *csv_file_name = argc >= 2 ? argv[1] : "../mushroom/agaricus-lepiota.data";
  cout << "Wersja OpenCV: " << CV_VERSION << endl;
  help(argv);

  // wczytanie podanego pliku CSV
  //
  cv::Ptr<cv::ml::TrainData> data_set =
      csv_file_name, // nazwa pliku wejściowego
    0,             // linie nagłówkowe (tyle ignoruje)
    0,             // w tej kolumnie zaczynają się odpowiedzi
    1,             // w tej kolumnie zaczynają się dane wejściowe
    "cat[0-22]"    // wszystkie 23 kolumny to kategorie
  );               // używa domyślnych znaków rozdzielającego (,) i braku danych (?)

  
  // sprawdzenie, czy wczytujemy właściwe dane
  //
  int n_samples = data_set->getNSamples();
  if (n_samples == 0) {
    cerr << "Nie udało się wczytać pliku: " << csv_file_name << endl;
    exit(-1);
  } else {
    cout << "Wczytano " << n_samples << " próbek z " << csv_file_name << endl;
  }

  // podział danych w taki sposób, że 90% to dane szkoleniowe
  //
  data_set->setTrainTestSplitRatio(0.90, false);
  int n_train_samples = data_set->getNTrainSamples();
  int n_test_samples = data_set->getNTestSamples();
  cout << "Znaleziono " << n_train_samples << " próbek szkoleniowych i "
       << n_test_samples << " próbek testowych." << endl;

  // tworzenie klasyfikatora DTrees
  //
  cv::Ptr<cv::ml::RTrees> dtree = cv::ml::RTrees::create();
  // ustawienia parametrów
  //
  // to są parametry ze starego pliku mushrooms.cpp

  // ustawienie prawdopodobieństw a priori tak, aby „karać” grzyby trujące 10 razy bardziej niż jadalne
  //
  float _priors[] = {1.0, 10.0};
  cv::Mat priors(1, 2, CV_32F, _priors);
  dtree->setMaxDepth(8);
  dtree->setMinSampleCount(10);
  dtree->setRegressionAccuracy(0.01f);
  dtree->setUseSurrogates(false /* true */);
  dtree->setMaxCategories(15);
  dtree->setCVFolds(0 /*10*/); // wartość różna od zera powoduje zrzut rdzenia
  dtree->setUse1SERule(true);
  dtree->setTruncatePrunedTree(true);
  // dtree->setPriors( priors );
  dtree->setPriors(cv::Mat()); // wartość różna od zera powoduje zrzut rdzenia
  // szkolenie modelu
  // uwaga: wykorzystujemy tylko szkoleniową część zbioru danych
  //

  dtree->train(data_set);

  // po zakończeniu szkolenia powinniśmy być w stanie obliczyć skalę błędów zarówno w odniesieniu do danych szkoleniowych,
  // jak i do odłożonych danych testowych
  //
  cv::Mat results;
  float train_performance = dtree->calcError(data_set,
                                             false, // użyj danych szkoleniowych
                                             results // cv::noArray()
                                             );
  std::vector<cv::String> names;
  data_set->getNames(names);
  Mat flags = data_set->getVarSymbolFlags();

  // własne obliczenia statystyczne:
  //
  {
    cv::Mat expected_responses = data_set->getResponses();
    int good = 0, bad = 0, total = 0;
    for (int i = 0; i < data_set->getNTrainSamples(); ++i) {
      float received = results.at<float>(i, 0);
      float expected = expected_responses.at<float>(i, 0);
      cv::String r_str = names[(int)received];
      cv::String e_str = names[(int)expected];
      cout << "Oczekiwano: " << e_str << ", otrzymano: " << r_str << endl;
      if (received == expected)
        good++;
      else
        bad++;
      total++;
    }
    cout << "Poprawne odpowiedzi: " <<(float(good)/total) <<" % " << endl;
                cout << "Niepoprawne odpowiedzi: " << (float(bad) / total) << "%"
         << endl;
  }
  float test_performance = dtree->calcError(data_set,
                                            true, // użyj danych testowych
                                            results // cv::noArray()
                                            );
  cout << "Skuteczność na danych szkoleniowych: " << train_performance << "%" << endl;
  cout << "Skuteczność na danych testowych: " <<test_performance <<" % " <<endl;
  return 0;
}
