#include "stdafx.h"

#include "xtr/doc/OSSDoc.h"

#include "resource.h"

#include "xtr/Exteor.h"
#include "xtr/io/OSSLegacyLoader.h"
#include "xtr/doc/RSFormDoc.h"

#include "xtr/dialog/NewDocument.h"

using ccl::oss::PictID;
using ccl::oss::OSSchema;

namespace xtr::doc {

#pragma warning( push )
#pragma warning( disable : 26440 26443 )

IMPLEMENT_DYNCREATE(OSSDoc, CDocument)

BEGIN_MESSAGE_MAP(OSSDoc, CDocument)
  ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_AS, &ThisClass::OnUpdateFileSaveAs)
END_MESSAGE_MAP()

#pragma warning( pop )

OSSDoc::OSSDoc() {
  oss = std::make_unique<OSSchema>();
  oss->AddObserver(*this);
}

OSSDoc::~OSSDoc() {
  oss->RemoveObserver(*this);
}

void OSSDoc::OnObserve(const ccl::types::Message& change) {
  if (const auto* erasePtr = dynamic_cast<const OSSchema::ErasePictMod*>(&change); erasePtr != nullptr) {
    if (erasePtr->pid == selection) {
      if (!std::empty(*oss)) {
        selection = std::begin(*oss)->uid;
      }
    }
    SetModifiedFlag(TRUE);
  } else if (const auto* srcChange = dynamic_cast<const OSSchema::SrcStateMod*>(&change); srcChange != nullptr) {
    SetModifiedFlag(TRUE);
  } else if (const auto* srcStatus = dynamic_cast<const OSSchema::SrcStatusMod*>(&change); srcStatus != nullptr) {
    const auto* mainWindow = ::AfxGetMainWnd();
    if (mainWindow != nullptr && mainWindow->IsWindowVisible()) { // Note: do not call UpdateViews on closed application
      UpdateAllViews(nullptr);
    }
  } else {
    SetModifiedFlag(TRUE);
  }
}

BOOL OSSDoc::OnNewDocument() {
  dialog::NewOSS dialog{};
  if (dialog.DoModal() == IDOK) {
    oss->title = mfc::ToSTL(dialog.ossName);
    selection = oss->InsertBase()->uid;
    oss->InsertBase();
    if (CDocument::OnNewDocument()) {
      auto path = dialog.fullPath + 
        (dialog.fullPath[dialog.fullPath.GetLength() - 1] == '\\' ? "" : "\\");
      path += mfc::ToMFC(std::filesystem::path(mfc::ToUTF8(dialog.ossName)).replace_extension(ExtensionFor(FileType::oss).GetString()).u8string());

      SetPathName(path, FALSE);
      if (DoFileSave()) {
        oss->Src().ossDomain = std::filesystem::path(mfc::ToUTF8(GetPathName())).parent_path().u8string();
        return TRUE;
      } else {
        ::AfxMessageBox(IXTRE_OSS_NOTSAVED, MB_ICONEXCLAMATION);
      }
    }
  }
  return FALSE;
}

BOOL OSSDoc::OnOpenDocument(LPCTSTR path) {
  if (CString(path).Right(4).MakeLower() != ExtensionFor(FileType::oss)) {
    return FALSE;
  } 
  if (!LoadJSON(mfc::ToUTF8(path))
      && !CDocument::OnOpenDocument(path)) {
    return FALSE;
  }
  SetPathName(path, FALSE);
  oss->Src().ossDomain = std::filesystem::path(mfc::ToUTF8(GetPathName())).parent_path().u8string();
  return TRUE;
}

BOOL OSSDoc::OnSaveDocument(LPCTSTR path) {
  SaveAllDocs();
  if (!SaveJSON(mfc::ToUTF8(path))) {
    return FALSE;
  }
  SetPathName(path, FALSE);
  SetModifiedFlag(FALSE);
  return TRUE;
}


void OSSDoc::OnUpdateFileSaveAs(CCmdUI *pCmdUI) { // NOLINT(readability-convert-member-functions-to-static)
  pCmdUI->Enable(FALSE);
}

void OSSDoc::Serialize(CArchive& ar) {
  MFCSerializable<OSSVersion>::Serialize(ar);
}

BOOL OSSDoc::CanCloseFrame(CFrameWnd* pFrame) {
  if (!CDocument::CanCloseFrame(pFrame)) {
    return FALSE;
  } 

  if (::_wchdir(mfc::ToMFC(oss->Src().ossDomain)) != 0) {
    TRACE(LR"(Unable to set browse directory!)");
  }
  for (const auto& pict : *oss) {
    auto* src = dynamic_cast<RSFormDoc*>(oss->Src()(pict.uid)->src);
    if (src == nullptr || !src->IsModified() ||  std::empty(*oss->Src()(pict.uid))) {
      continue;
    }
    switch (::AfxMessageBox(mfc::FormatSID(IXTRE_OSS_SAVE_PICT, mfc::ToMFC(pict.title).GetString()),
                          MB_YESNOCANCEL | MB_ICONQUESTION | MB_DEFBUTTON1)) {
    case IDYES: {
      src->DoFileSave();
      SetModifiedFlag(TRUE);
      break;
    }
    case IDNO: continue;
    case IDCANCEL: return FALSE;
    }
  }
  return TRUE;
}

void OSSDoc::SaveAllDocs() {
  for (const auto& pict : *oss) {
    if (auto* doc = dynamic_cast<RSFormDoc*>(oss->Src().ActiveSrc(pict.uid));
        doc != nullptr && doc->IsModified()) {
      doc->DoFileSave();
    }
  }
}

JSON OSSDoc::ExtractData() const {
  auto result = JSON(*oss);
  result["selection"] = selection;
  return result;
}

bool OSSDoc::LoadData(const JSON& object) {
  [[maybe_unused]] const auto noObserving = DndGuard();
  object.get_to(*oss);
  object.at("selection").get_to<PictID>(selection);
  if (!OSS().Contains(selection)) {
    selection = std::begin(OSS())->uid;
  }
  oss->Src().ReconnectAll();
  return true;
}

bool OSSDoc::LegacyLoad(io::CArchiveAdapter& archive) {
  [[maybe_unused]] const auto noObserving = DndGuard();
  auto loader = io::OSSLegacyLoader{ archive, *oss };
  loader.LoadVersion();
  loader.DoLoad();
  selection = loader.selection;
  if (!OSS().Contains(selection)) {
    selection = std::begin(OSS())->uid;
  }
  oss->Src().ReconnectAll();
  return true;
}

} // namespace xtr::doc