#include "HalfMove.hpp"

HalfMove::HalfMove(std::string move_absolute,std::string move_san) : capture(' ') {
  this->move_absolute=move_absolute;
  this->move = move_san;
  fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
}

HalfMove::HalfMove(std::string move_absolute, std::string move_san, std::string fen) : fen(fen), capture(' ') {
  this->move_absolute=move_absolute;
  this->move = move_san;
}

HalfMove::~HalfMove() {
  if (mainline != nullptr) {
    delete mainline;
  }
  for (HalfMove *m : variations) {
    delete m;
  }
}

HalfMove::HalfMove(HalfMove *m){
  move_absolute=m->move_absolute;
  move=m->move;
  fen=m->fen;
  capture=m->capture;
  IsBlack = m->IsBlack;
  Number = m->Number;
  nag = m->nag;
  comment=m->comment;
  if(m->mainline != nullptr){
    SetMainline(new HalfMove(m->mainline));
  }
  for (int i=0; i < m->variations.size(); i++) {
    AddVariation(new HalfMove(m->variations[i]));
  }
}

void HalfMove::AddVariation(HalfMove *m) {
  m->IsBlack = this->IsBlack;
  m->Number = this->Number;
  HalfMove::variations.push_back(m);
  cgeditor::CGEHalfMove::variations.push_back(m);
  m->SetParent(this);
}

std::map<char, std::uint8_t> HalfMove::GetLineCaptures() {
  std::map<char, std::uint8_t> captures;
  HalfMove *m = this;
  do {
    char c = m->capture;
    if (captures.find(c) != captures.end()) {
      captures[c]++;
    } else {
      captures[c] = 1;
    }
    m = m->parent;
  } while (m != nullptr);
  return (captures);
}

void HalfMove::SetCapture(char c) { capture = c; }

void HalfMove::AddMove(HalfMove *m) {
  if (this->mainline == nullptr) {
    SetMainline(m);
  } else {
    if (mainline != nullptr) {
      mainline->AddVariation(m);
    }
  }
}

void HalfMove::SetMainline(HalfMove *m) {
  if (!this->IsBlack) {
    m->IsBlack = true;
    m->Number = this->Number;
  } else {
    m->IsBlack = false;
    m->Number = this->Number + 1;
  }
  HalfMove::mainline = m;
  cgeditor::CGEHalfMove::MainLine = m;
  if (m != nullptr) {
    m->SetParent(this);
  }
}

void HalfMove::SetParent(HalfMove *m) {
  HalfMove::parent = m;
  CGEHalfMove::Parent = m;
}

void HalfMove::RemoveChild(HalfMove *m) {
  std::uint32_t i = 0;
  bool found = false;
  for (i; i < HalfMove::variations.size(); i++) {
    if (HalfMove::variations[i] == m) {
      found = true;
      break;
    }
  }
  if (found) {
    HalfMove::variations.erase(HalfMove::variations.begin() + i);
  }
  if (HalfMove::mainline == m) {
    HalfMove::mainline = nullptr;
  }
  cgeditor::CGEHalfMove::RemoveChild((CGEHalfMove *)m);
}

HalfMove *HalfMove::GetParent() { return (parent); }

HalfMove *HalfMove::GetRoot() {
  HalfMove *m = this;
  HalfMove *p = HalfMove::parent;
  while (p != nullptr) {
    if (p->mainline != m) {
      return (m);
    }
    m = p;
    p = m->HalfMove::parent;
  }
  return (m);
}

void HalfMove::SetAsMainline() {
  HalfMove *root = GetRoot();
  HalfMove *lastRoot;
  do {
    lastRoot = root;
    root->HalfMove::Promote();
    root = GetRoot();
  } while (root != lastRoot);
}

HalfMove *HalfMove::GetMainline() { return (mainline); }

HalfMove::HalfMove(pgnp::HalfMove *m) : capture(' ') {
  this->move = m->move;
  this->nag = m->NAG;
  this->IsBlack = m->isBlack;
  this->comment=m->comment;
  this->Number = m->count;
  if (m->MainLine != nullptr) {
    this->SetMainline(new HalfMove(m->MainLine));
  }
  for (pgnp::HalfMove *v : m->variations) {
    this->AddVariation(new HalfMove(v));
  }
}

void HalfMove::SetFen(std::string fen) { this->fen = fen; }

void HalfMove::Promote() {
  HalfMove *root = GetRoot();
  if (root->parent != nullptr) {
    HalfMove *p = root->parent;
    if (p->HalfMove::mainline != root) {
      if (root->parent->HalfMove::parent != nullptr) {
        HalfMove *pp = root->parent->HalfMove::parent;
        if (pp->HalfMove::mainline == p) {
          pp->HalfMove::SetMainline(root);
        } else {
          pp->AddVariation(root);
          pp->HalfMove::RemoveChild(p);
        }
      }
      if (p->HalfMove::mainline == root) {
        p->HalfMove::SetMainline(nullptr);
      } else {
        p->HalfMove::RemoveChild(root);
      }
      root->AddVariation(p);
    }
  }
}

bool HalfMove::IsVariation() {
  HalfMove *m = this;
  HalfMove *p = HalfMove::parent;
  while (p != nullptr) {
    if (p->mainline != m) {
      return (true);
    }
    m = p;
    p = m->HalfMove::parent;
  }
  return (false);
}

std::string HalfMove::GetFen() { return (fen); }

std::vector<HalfMove *> HalfMove::GetVariations() { return (variations); }

bool HalfMove::IsABlackMove() { return (IsBlack); }

void HalfMove::BuildAndVerify(HalfMove *m, std::string fen) {
  arbiter.Setup(fen);
  m->move_absolute=arbiter.ParseSAN(m->move);
  bool work = arbiter.Play(m->move_absolute,arbiter.ParseSANPromotion(m->move));
  if (!work) {
    wxLogDebug("Bug! %s", m->move);
  }
  char capture = arbiter.GetCapture();
  if (capture != ' ') {
    m->capture = capture;
  }
  m->fen = arbiter.GetFEN();
  if (m->mainline != nullptr) {
    BuildAndVerify(m->mainline, arbiter.GetFEN());
  }
  for (HalfMove *v : m->variations) {
    BuildAndVerify(v, fen);
  }
}
void HalfMove::BuildAndVerify(std::string initial_fen) {
  BuildAndVerify(this, initial_fen);
}