/*
 * proto-suff
 * Copyright (C) 2020  Univ. Artois & CNRS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "Tree.hpp"
#include <cassert>
#include <iostream>
#include <new>
#include <sys/types.h>

namespace protoss {
/**
 * @brief Destroy the Tree object
 *
 */
Tree::~Tree() {
  if (m_memory)
    delete[] m_memory;
}

/**
 * @brief Construct the tree regarding the raw representation given in
 * parameter.
 *
 * @param rawTree gives the tree in raw representation.
 */
void Tree::parse(std::vector<int> &rawTree) {
  if (m_memory) {
    delete[] m_memory;
    m_memory = nullptr;
  }

  // manage the memory needed to store the tree.
  m_memory = new u_char[rawTree.size() * sizeof(Node)];
  u_char *ptr = m_memory;

  unsigned pos = 0, count = 0;
  std::vector<Node *> stack(rawTree.size(), nullptr);

  // fill the stack.
  m_maxIdentifier = -2;
  for (auto elt : rawTree) {
    stack[pos++] = new (ptr) Node(elt);
    ptr += sizeof(Node);

    if (elt > 0 && elt > m_maxIdentifier)
      m_maxIdentifier = elt;

    if (elt < 0)
      count++;
    else
      count = 0;

    // merge.
    while (count == 2) {
      assert(!stack[pos - 3]->getFalseBranch() &&
             !stack[pos - 3]->getTrueBranch());

      stack[pos - 3]->setFalseBranch(stack[pos - 2]);
      stack[pos - 3]->setTrueBranch(stack[pos - 1]);
      pos -= 2;

      count = 1;
      if (pos > 1 && (stack[pos - 2]->getIdentifier() < 0 ||
                      stack[pos - 2]->getFalseBranch()))
        count++;
    }
  }
  // init the flag array.
  m_usedToExplain.resize(m_maxIdentifier + 1, false);
  m_nbCritical.resize(m_maxIdentifier + 1, 0);
  m_usedList.resize(m_maxIdentifier + 1, 0);

  assert(pos == 1);
  m_root = stack[0];
} // parse

/**
 * @brief Init the exemple for iteratively trying to reduce the reason that
 * explains the returned class.
 *
 * @param example is the assignment of the features we want to explain.
 * @return int the class, if 0 then the class is undefined.
 */
int Tree::initExample(std::vector<u_char> &storedExample) {
  // reinit the vector used to store information about which variables have
  // been used to explained.
  for (auto &&u : m_usedToExplain)
    u = false;
  m_usedList.resize(0);

  // search for the class.
  m_savedClass = m_root->getClass(storedExample, m_usedList, m_usedToExplain);
  return m_savedClass;
} // initExample

/**
 * @brief Removes a feature and returns class regarding the remaining.
 *
 * @param storedExample is a vector that contains for each feature if it is
 * assigned or not and if it is assigned it stores the polarity.
 * @param feature is the element we want to remove.
 * @return bool which specifies if the feature can be dropped out to explain
 * this tree.
 */
bool Tree::canRemoveFeature(std::vector<u_char> &storedExample, int feature) {
  m_usedList.resize(0);
  if (feature >= m_usedToExplain.size() || !m_usedToExplain[feature])
    return m_hasBeenCorrectlyClassified = true;

  int cl = m_root->getClass(storedExample, m_usedList, m_usedToExplain);
  return m_hasBeenCorrectlyClassified = (cl == m_savedClass);
} // canRemoveFeature

/**
 * @brief Set usedToExplain vector as before the last call to getClass.
 *
 */
void Tree::undoLastModification() {
  for (auto l : m_usedList)
    m_usedToExplain[l] = false;
} // undoLastModification

/**
 * @brief Display the tree.
 *
 */
void Tree::display() {
  m_root->display();
  std::cout << "\n";
} // display

} // namespace protoss