#include "stdafx.h"

#include "xtr/ExtSourceManager.h"

#include "resource.h"
#include "xtr/dialog/BasicDlg.h"
#include "xtr/Exteor.h"
#include "xtr/doc/RSFormDoc.h"
#include "xtr/doc/OSSDoc.h"

namespace xtr {

static constexpr auto noView = true;

using ccl::src::Descriptor;
using ccl::src::Source;
using ccl::src::SrcType;

Source* ExtSourceManager::Find(const Descriptor& desc) {
  switch (desc.type) {
  default: return SourceManager::Find(desc);
  case SrcType::rsDoc:
    return ExteorApplication::Docs().FindRSDocByPath(std::filesystem::path(desc.name).c_str());
  }
}

bool ExtSourceManager::TestDomain(const Descriptor& global, const std::u8string& domain) const {
  switch (global.type) {
  default: return SourceManager::TestDomain(global, domain);
  case SrcType::rsDoc: {
    auto domainDir = std::filesystem::path(domain);
    if (std::empty(domainDir.filename())) {
      domainDir = domainDir.parent_path();
    }
    return std::filesystem::path(global.name).parent_path().lexically_normal() == domainDir.lexically_normal();
  }
  }
}

Descriptor ExtSourceManager::Convert2Local(const Descriptor& global, const std::u8string& domain) const {
  switch (global.type) {
  default: return SourceManager::Convert2Local(global, domain);
  case SrcType::rsDoc:
    Descriptor local = global;
    if (!std::empty(domain)) {
      local.name = std::filesystem::relative(std::filesystem::path(global.name), std::filesystem::path(domain)).u8string();
    }
    return local;
  }
}

Descriptor ExtSourceManager::Convert2Global(const Descriptor& local, const std::u8string& domain) const {
  switch (local.type) {
  default: return SourceManager::Convert2Global(local, domain);
  case SrcType::rsDoc: {
    Descriptor global = local;
    if (!std::empty(domain)) {
      global.name = (std::filesystem::path(domain) / std::filesystem::path(local.name).filename()).u8string();
    }
    return global;
  }
  }
}

Descriptor ExtSourceManager::CreateLocalDesc(SrcType type, std::u8string localName) const {
  switch (type) {
  default: return SourceManager::CreateLocalDesc(type, localName);
  case SrcType::rsDoc: {
    Descriptor result{};
    result.type = type;
    result.name = FixFilename(localName) + mfc::ToUTF8(ExtensionFor(FileType::trs));
    return result;
  }
  }
}

std::u8string ExtSourceManager::FixFilename(const std::u8string& name) {
  std::u8string result{};
  for (const auto symb : name) {
    if (symb != '?' && symb != '\\' && symb != ':' && symb != '*' 
        && symb != '"' && symb != '<' && symb != '>' && symb != '|') {
      result += symb;
    }
  }
  if (std::empty(result)) {
    return u8"noname";
  } else {
    return result;
  }
}

Descriptor ExtSourceManager::GetDescriptor(const Source& src) const {
  switch (src.Type()) {
  default: return SourceManager::GetDescriptor(src);
  case SrcType::rsDoc: {
    const auto& path = RSCast(src).GetPathName();
    return Descriptor{ src.Type(), mfc::ToUTF8(path.IsEmpty() ? RSCast(src).GetTitle() : path) };
  }
  }
}

bool ExtSourceManager::ChangeDescriptor(const Descriptor& desc, const Descriptor& newDesc) {
  if (desc.type != newDesc.type) {
    return false;
  }
  if (auto* src = Find(desc); src != nullptr) {
    Close(*src);
  }
  const auto oldPath = std::filesystem::path(desc.name);
  const auto newPath = std::filesystem::path(newDesc.name);
  switch (desc.type) {
  default: return false;
  case SrcType::rsDoc: {
    if (!std::filesystem::exists(oldPath)) {
      ::AfxMessageBox(mfc::FormatSID(IXTRE_FILE_NOT_FOUND, oldPath.c_str()), MB_ICONEXCLAMATION);
      return false;
    } else if (std::filesystem::exists(newPath)) {
      ::AfxMessageBox(mfc::FormatSID(IXTRS_RENAME_FILE_EXISTS, newPath.c_str()), MB_ICONEXCLAMATION);
      return false;
    }

    try {
      std::filesystem::rename(oldPath, newPath);
      g_Exteor.RemoveMRUItem(oldPath.c_str());
      return true;
    } catch (...) {
      ::AfxMessageBox(mfc::LoadSID(IXTRE_RENAME_UNKNOWN), MB_ICONEXCLAMATION);
      return false;
    }
  }
  }
}

Source* ExtSourceManager::CreateNew(const Descriptor& desc) {
  if (Find(desc) != nullptr) {
    return nullptr;
  }
  const auto path = std::filesystem::path(desc.name);
  if (std::filesystem::exists(path) &&
      ::AfxMessageBox(mfc::FormatSID(IXTRS_ASK_REWRITE_FILE, path.c_str()), MB_YESNO | MB_ICONQUESTION) != IDYES) {
    return nullptr;
  }

  const auto guardSilent = XTROptions::App().GuardSilence();
  switch (desc.type) {
  default: break;
  case SrcType::rsDoc: {
    if (auto* src = ExteorApplication::Docs().NewRSFormDoc(noView); src != nullptr) {
      if (auto& doc = RSCast(*src); doc.OnSaveDocument(path.c_str())) {
        return src;
      } else {
        src->OnCloseDocument();
      }
    }
    break;
  }
  }
  return nullptr;
}

void ExtSourceManager::Discard(const Descriptor& desc) {
  auto* src = Open(desc);
  if (src == nullptr) {
    return;
  }

  src->ReleaseClaim();
  Close(*src);

  switch (desc.type) {
  default: break;
  case SrcType::rsDoc: {
    auto newPath = std::filesystem::path(desc.name);
    CString newName = newPath.stem().c_str();
    newName += CTime::GetCurrentTime().Format(LR"(%Y%m%d_%H%M)");
    newName += newPath.extension().c_str();
    newPath = newPath.replace_filename(newName.GetString());
    ChangeDescriptor(desc, Descriptor{ SrcType::rsDoc, newPath.u8string() });
  }
  }
}

Source* ExtSourceManager::Open(const Descriptor& desc) {
  const auto path = std::filesystem::path(desc.name);
  if (!std::filesystem::exists(path)) {
    return nullptr;
  }
  const auto guardSilent = XTROptions::App().GuardSilence();
  switch (desc.type) {
  default: return SourceManager::Open(desc);
  case SrcType::rsDoc: return ExteorApplication::Docs().OpenRSFormDoc(path.c_str(), noView);
  }
}

void ExtSourceManager::Close(Source& src) {
  switch (src.Type()) {
  default: break;
  case SrcType::rsDoc: {
    RSCast(src).OnCloseDocument();
    break;
  }
  }
}

bool ExtSourceManager::SaveState(Source& src) {
  switch (src.Type()) {
  default: return false;
  case SrcType::rsDoc: {
    const auto path = std::filesystem::path(RSCast(src).GetPathName().GetString());
    if (path.has_parent_path() && !std::filesystem::exists(path)) {
      return false;
    } else {
      return RSCast(src).DoFileSave();
    }
  }
  }
}

// false positive noexcept recomendation for function using dynamic_cast to references
#pragma warning( push )
#pragma warning( disable : 26496 26440 ) 

const doc::RSFormDoc& ExtSourceManager::RSCast(const Source& src) {
  return dynamic_cast<const doc::RSFormDoc&>(src);
}

doc::RSFormDoc& ExtSourceManager::RSCast(Source& src) {
  return dynamic_cast<doc::RSFormDoc&>(src);
}

#pragma warning( pop )

} // namespace xtr