#include "../ui-base.hpp"

Movie movie;

void Movie::chooseFile() {
  fileBrowser->onChange.reset();
  fileBrowser->onActivate = { &Movie::play, this };
  fileBrowser->onAccept = { &Movie::play, this };
  fileBrowser->setWindowTitle("Select Movie");
  fileBrowser->setPath(config().path.current.movie);
  fileBrowser->setNameFilters("bsnes Movies (*.bsv)");
  fileBrowser->chooseFile();
}

void Movie::play(const string &filename) {
  if(QDir(filename).exists()) return;  //ignore folders
  config().path.current.movie = dir(filename);

  if(Movie::state != Inactive) stop();

  if(fp.open(filename, file::mode::read)) {
    if(fp.size() < 32) goto corrupt;

    unsigned signature = fp.readm(4);
    if(signature != 0x42535631) goto corrupt;

    unsigned version = fp.readl(4);
    if(version != SNES::Info::SerializerVersion) goto corrupt;

    unsigned crc32 = fp.readl(4);
    if(crc32 != SNES::cartridge.crc32()) goto corrupt;

    unsigned size = fp.readl(4);
    uint8_t *data = new uint8_t[size];
    fp.read(data, size);
    serializer state(data, size);
    SNES::system.unserialize(state);

    Movie::state = Playback;
    mainWindow->syncUi();
    utility.showMessage("Playback started.");

    return;
  }

corrupt:
  fp.close();
  utility.showMessage("Movie file is invalid, playback cancelled.");
}

void Movie::record() {
  if(Movie::state != Inactive) {
    utility.showMessage("Movie mode already active, recording cancelled.");
  } else {
    SNES::system.runtosave();
    serializer state = SNES::system.serialize();

    utility.showMessage("Recording started.");

    Movie::state = Record;
    mainWindow->syncUi();
    fp.open(makeFilename(), file::mode::write);
    fp.writem(0x42535631, 4);
    fp.writel(SNES::Info::SerializerVersion, 4);
    fp.writel(SNES::cartridge.crc32(), 4);
    fp.writel(state.size(), 4);
    fp.write(state.data(), state.size());
  }
}

void Movie::stop() {
  if(Movie::state != Inactive) {
    Movie::state = Inactive;
    mainWindow->syncUi();
    fp.close();
    utility.showMessage("Recording / playback stopped.");
  }
}

string Movie::makeFilename() const {
  string filename = nall::basename(cartridge.fileName);

  time_t systemTime = time(0);
  tm *currentTime = localtime(&systemTime);
  char t[512];
  sprintf(t, "%.4u%.2u%.2u-%.2u%.2u%.2u",
    1900 + currentTime->tm_year, 1 + currentTime->tm_mon, currentTime->tm_mday,
    currentTime->tm_hour, currentTime->tm_min, currentTime->tm_sec
  );
  filename << "-" << t << ".bsv";

  return filepath(filename, config().path.data);
}

int16_t Movie::read() {
  int16_t result = fp.readl(2);

  if(fp.end()) {
    Movie::state = Inactive;
    mainWindow->syncUi();
    fp.close();
    utility.showMessage("Playback finished.");
  }

  return result;
}

void Movie::write(int16_t value) {
  fp.writel(value, 2);
}

Movie::Movie() {
  state = Inactive;
}
