#include "HalfMove.hpp"

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

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

HalfMove::~HalfMove() {
  for (HalfMove *m : variations) {
    delete m;
  }
}

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 != NULL);
  return (captures);
}

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

void HalfMove::AddMove(HalfMove *m) {
  if (this->mainline == NULL) {
    SetMainline(m);
  } else {
    if (mainline != NULL) {
      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 != NULL) {
    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 = NULL;
  }
  cgeditor::CGEHalfMove::RemoveChild((CGEHalfMove *)m);
}

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

HalfMove *HalfMove::GetRoot() {
  HalfMove *m = this;
  HalfMove *p = HalfMove::parent;
  while (p != NULL) {
    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->IsBlack = m->isBlack;
  this->SetComment(m->comment);
  this->Number = m->count;
  if (m->MainLine != NULL) {
    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 != NULL) {
    HalfMove *p = root->parent;
    if (p->HalfMove::mainline != root) {
      if (root->parent->HalfMove::parent != NULL) {
        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(NULL);
      } else {
        p->HalfMove::RemoveChild(root);
      }
      root->AddVariation(p);
    }
  }
}

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

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

std::string HalfMove::GetPGN() { return (GetPGN(IsBlack)); }

std::string HalfMove::GetPGN(bool needDots) {
  std::string part;
  bool newNeedDots = false;

  if (!IsBlack || needDots) {
    part += std::to_string(Number) + ".";
    if (needDots) {
      part += "..";
    }
  }
  part += move;

  if (GetNbLineComment() > 0) {
    part += " {";
    part += GetComment();
    part += "}";
    newNeedDots = true;
  }

  if (variations.size() > 0) {
    newNeedDots = true;
    for (HalfMove *v : variations) {
      part += " (";
      part += v->GetPGN(IsBlack);
      part += ")";
    }
  }

  if (mainline != NULL) {
    part += " " + mainline->GetPGN(newNeedDots);
  }

  return (part);
}

void HalfMove::BuildAndVerify(HalfMove *m, std::string fen) {
  arbiter.Setup(fen);
  bool work = arbiter.Play(arbiter.ParseSAN(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 != NULL) {
    BuildAndVerify(m->mainline, arbiter.GetFEN());
  }
  for (HalfMove *v : m->variations) {
    BuildAndVerify(v,fen);
  }
}
void HalfMove::BuildAndVerify(std::string initial_fen) {
  BuildAndVerify(this, initial_fen);
}