#include "Theme.hpp"
#include <wx/filename.h>

Theme::Theme() : square_radius(10) {
  // Load config
  CONFIG_OPEN(config);
  std::string piece =
      config->Read("board/theme/pieces/path", "default").ToStdString();
  wxFileName piece_file(piece);
  std::string square =
      config->Read("board/theme/squares/path", "default").ToStdString();
  wxFileName square_file(square);
  CONFIG_CLOSE(config);
  // Piece
  if (piece == "default" || !piece_file.FileExists()) {
    wxLogDebug("Loading piece skin from binres");
    LoadPiecesSkin(LoadPNG("cburnett").ConvertToImage());
  } else {
    wxLogDebug("Loading piece skin: %s", piece);
    LoadPiecesSkin(wxImage(piece, wxBITMAP_TYPE_PNG));
  }
  // Square
  if (square == "default" || !square_file.FileExists()) {
    wxLogDebug("Loading square skin from binres");
    LoadSquaresSkin(LoadPNG("chesscom_8bits").ConvertToImage());
  } else {
    wxLogDebug("Loading square skin: %s", square);
    LoadSquaresSkin(wxImage(square, wxBITMAP_TYPE_PNG));
  }
}

Theme::Theme(std::string piece, std::string square) : square_radius(10) {
  wxLogDebug("Loading piece skin: %s", piece);
  LoadPiecesSkin(wxImage(piece, wxBITMAP_TYPE_PNG));
  wxLogDebug("Loading square skin: %s", square);
  LoadSquaresSkin(wxImage(square, wxBITMAP_TYPE_PNG));
}

Theme::~Theme() {
  for (std::pair<char, wxBitmap *> c : skin_scaled) {
    delete c.second;
  }
}

std::uint8_t Theme::GetSquareRadius() { return (square_radius); }

void Theme::LoadPiecesSkin(wxImage iskin) {
  if (iskin.GetWidth() != 2 * ELT_DIM || iskin.GetHeight() != 6 * ELT_DIM) {
    throw "Invalid piece theme";
  }

  int offset = 0;
  // Kings
  skin['k'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
  skin['K'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
  // Queen
  offset = ELT_DIM;
  skin['q'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
  skin['Q'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
  // Rook
  offset = ELT_DIM * 2;
  skin['r'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
  skin['R'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
  // Bishop
  offset = ELT_DIM * 3;
  skin['b'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
  skin['B'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
  // Knight
  offset = ELT_DIM * 4;
  skin['n'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
  skin['N'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
  // Pawn
  offset = ELT_DIM * 5;
  skin['p'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
  skin['P'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));

  // Update scaled version
  ResizePieces(DEFAULT_SIZE * PIECE_SIZE_FACTOR);
}

void Theme::LoadSquaresSkin(wxImage iskin) {
  if (iskin.GetWidth() != 2 * ELT_DIM || iskin.GetHeight() != ELT_DIM) {
    throw "Invalid piece theme";
  }

  // Square
  skin['s'] = iskin.GetSubImage(wxRect(0, 0, ELT_DIM, ELT_DIM));
  skin['S'] = iskin.GetSubImage(wxRect(ELT_DIM, 0, ELT_DIM, ELT_DIM));

  // Update scaled version
  ResizeSquares(DEFAULT_SIZE);
}

wxBitmap *Theme::Get(char c) { return (skin_scaled[c]); }

void Theme::ResizePieces(std::uint32_t width) {
  for (std::pair<char, wxImage> c : skin) {
    if (c.first != 's' && c.first != 'S') {
      if (skin_scaled.count(c.first))
        delete skin_scaled[c.first];
      skin_scaled[c.first] =
          new wxBitmap(c.second.Scale(width, width, wxIMAGE_QUALITY_HIGH));
    }
  }
}

void Theme::ResizeSquaresAndPieces(std::uint32_t width) {
  ResizeSquares(width);
  ResizePieces(width * PIECE_SIZE_FACTOR);
}

void Theme::ResizeSquares(std::uint32_t width) {
  if (skin_scaled.count('s'))
    delete skin_scaled['s'];
  skin_scaled['s'] =
      new wxBitmap(skin['s'].Scale(width, width, wxIMAGE_QUALITY_HIGH));
  if (skin_scaled.count('S'))
    delete skin_scaled['S'];
  skin_scaled['S'] =
      new wxBitmap(skin['S'].Scale(width, width, wxIMAGE_QUALITY_HIGH));

  skin_scaled['0'] = new wxBitmap(*skin_scaled['S']);
  skin_scaled['1'] = new wxBitmap(*skin_scaled['s']);
  skin_scaled['2'] = new wxBitmap(*skin_scaled['s']);
  skin_scaled['3'] = new wxBitmap(*skin_scaled['S']);

  skin_scaled['0']->SetMask(RoundedMask(width, 0));
  skin_scaled['1']->SetMask(RoundedMask(width, 1));
  skin_scaled['2']->SetMask(RoundedMask(width, 2));
  skin_scaled['3']->SetMask(RoundedMask(width, 3));
}

void Theme::Zoom(int amount) {
  double width = skin_scaled['s']->GetWidth() + amount;
  ResizeSquares(std::max(width, 1.0));
  ResizePieces(std::max(width * PIECE_SIZE_FACTOR, 1.0));
}

void Theme::SetSquareRadius(std::uint8_t radius) {
  square_radius = radius;
  Zoom(0); // Refresh scale
}

/**
 * This will never fail since k entry always exists (cf constructor and
 * ResizePieces)
 */
double Theme::GetPiecesSizes() { return (skin_scaled['k']->GetWidth()); }

/**
 * This will never fail since s entry always exists (cf constructor and
 * ResizeSquares)
 */
double Theme::GetSquaresSizes() { return (skin_scaled['s']->GetWidth()); }

wxMask *Theme::RoundedMask(std::uint32_t width, std::uint8_t corner) {

  wxBitmap b(width, width, 1);
  wxMemoryDC dc;
  dc.SelectObject(b);
  dc.SetPen(*wxBLACK_PEN);
  dc.SetBrush(*wxBLACK_BRUSH);
  dc.DrawRectangle(0, 0, width, width);
  dc.SetBrush(*wxWHITE_BRUSH);
  dc.SetPen(*wxWHITE_PEN);
  dc.DrawRoundedRectangle(0, 0, width, width, square_radius);

  dc.SetBrush(*wxWHITE_BRUSH);
  dc.SetPen(*wxWHITE_PEN);
  if (corner == 0) {
    dc.DrawRectangle(0, width / 2, width, width);
    dc.DrawRectangle(width / 2, 0, width, width);
  } else if (corner == 1) {
    dc.DrawRectangle(0, 0, width / 2, width);
    dc.DrawRectangle(0, width / 2, width, width);
  } else if (corner == 2) {
    dc.DrawRectangle(0, 0, width, width / 2);
    dc.DrawRectangle(width / 2, 0, width, width);
  } else if (corner == 3) {
    dc.DrawRectangle(0, 0, width / 2, width);
    dc.DrawRectangle(0, 0, width, width / 2);
  }
  return (new wxMask(b));
}
