#include "stdafx.h"

#include "xtr/Exteor.h"

#include "SetupManager.h"
#include "xtr/ui/HelpHandler.h"
#include "xtr/dialog/OptionsDialog.h"
#include "xtr/dialog/NewDocument.h"

#include "resource.h"

#include "ccl/lang/TextEnvironment.h"

namespace xtr {

using ui::ExteorFrame;

namespace {

class AboutDlg : public CDialog {
public:
  explicit AboutDlg(CWnd* pParent = nullptr)
    : CDialog(IXTRD_ABOUTBOX, pParent) {}

private:
  BOOL OnInitDialog() override {
    CDialog::OnInitDialog();
    SetDlgItemTextW(IXTRC_VERSION, g_Exteor.version.text);
    SetDlgItemTextW(IXTRC_COPYRIGHT, g_Exteor.version.copyright);
    return TRUE;
  }
};

} // anonymous namespace

#pragma warning( push )
#pragma warning( disable : 26426 )

// The one and only ExteorApplication object
ExteorApplication g_Exteor{}; // NOLINT

#pragma warning( pop )

#pragma warning( push )
#pragma warning( disable : 26440 26436 26454 )

BEGIN_MESSAGE_MAP(ExteorApplication, CWinAppEx)
  ON_COMMAND(ID_APP_ABOUT, &ThisClass::OnAppAbout)
  ON_COMMAND(IXTRC_EDIT_OPTIONS, &ThisClass::OnOpenOptions)
  ON_COMMAND(IXTRC_XTR_HELP, &ThisClass::OpenHelp)
  ON_COMMAND(IXTRC_BUG_REPORT, &ThisClass::OnBugReport)
  ON_COMMAND(IXTRC_MAIL_DEVELOPERS, &ThisClass::OnMailDevelopers)
  ON_COMMAND(ID_FILE_OPEN, &ThisClass::OnFileOpen)
  ON_COMMAND(ID_FILE_NEW, &ThisClass::OnFileNew)
  ON_COMMAND(IXTRC_START_PAGE, &ThisClass::OnStartPage)
END_MESSAGE_MAP()

#pragma warning( pop )

ExteorApplication::ExteorApplication() {
  SetAppID(L"CIHT CONCEPT.EXTEOR");
  docs = std::make_unique<doc::DocumentManager>(*this);
}

doc::DocumentManager& ExteorApplication::Docs() noexcept {
  return *g_Exteor.docs;
}

ExteorFrame& ExteorApplication::MainFrame() noexcept {
  return dynamic_cast<ExteorFrame&>(*g_Exteor.m_pMainWnd);
}

int32_t ExteorApplication::ExitInstance() {
  ::AfxOleTerm(FALSE);

  SetupManager::CCLExitSequence();
  CMFCVisualManager::DestroyInstance();
  ::ReleaseMutex(mutex);
  ::CloseHandle(mutex);
  return CWinAppEx::ExitInstance();
}

BOOL ExteorApplication::InitInstance() {
  if (CheckIfAnotherExteorIsRunning()) {
    if (*m_lpCmdLine != '\0') {
      FeedCommandLineToAnotherExteor();
    }
    return FALSE;
  }

  DefaultInitApplication();
  if (!InitVersion()) {
    return FALSE;
  } else if (!InitControls()) {
    return FALSE;
  } else if (!CreateMainFrame()) {
    return FALSE;
  }
  CreateStartingLook();
  return TRUE;
}

BOOL ExteorApplication::CheckIfAnotherExteorIsRunning() noexcept {
  mutex = ::CreateMutexW(nullptr, FALSE, L"Exteor");
  const auto res = mutex != nullptr && GetLastError() == ERROR_ALREADY_EXISTS;
  if (res) {
    mutex = nullptr;
  }
  return res;
}

void ExteorApplication::DefaultInitApplication() {
  SetRegistryKey(L"CIHT Concept");
  ::setlocale(LC_ALL, "RUSSIAN");

  InitContextMenuManager();
  InitShellManager();
  InitTooltipManager();

  CMFCToolTipInfo ttParams{};
  ttParams.m_bVislManagerTheme = TRUE;
  GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL, RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);
}

void ExteorApplication::FeedCommandLineToAnotherExteor() noexcept {
  COPYDATASTRUCT messageData{};
  messageData.cbData = static_cast<DWORD>((wcslen(m_lpCmdLine) + 1) * sizeof(wchar_t)); // NOLINT
  messageData.lpData = m_lpCmdLine;

  auto* const hWnd = FindWindowW(L"Exteor", nullptr);
  assert(hWnd != nullptr);
  ::SetForegroundWindow(hWnd);
  ::SendMessageW(hWnd, WM_COPYDATA, 0, reinterpret_cast<LPARAM>(&messageData));
}

BOOL ExteorApplication::InitVersion() {
  if (!InitVersionID()) {
    return FALSE;
  }
  version.text = mfc::FormatSID(IXTRS_VERSION_TEXT, version.id.GetString(), mfc::ToMFC(ccl::Environment::VersionInfo()).GetString());
  version.copyright = mfc::LoadSID(IXTRS_COPYRIGHT);
  return TRUE;
}

BOOL ExteorApplication::InitVersionID() {
  static constexpr auto VERSION_PREFIX_LEN = 6;
  // get the filename of the executable containing the version resource
  CString extFileName{};
  if (::GetModuleFileNameW(nullptr, extFileName.GetBuffer(MAX_PATH), MAX_PATH) == 0) {
    TRACE("GetModuleFileName failed with error %d\n", GetLastError());
    return FALSE;
  }
  extFileName.ReleaseBuffer();

  // allocate a block of memory for the version info
  DWORD dummy{};
  const auto dwSize = ::GetFileVersionInfoSizeW(extFileName, &dummy);
  if (dwSize == 0) {
    TRACE("GetFileVersionInfoSize failed with error %d\n", GetLastError());
    return FALSE;
  }

  // load the version info
  std::vector<BYTE> data(dwSize);
  if (!::GetFileVersionInfoW(extFileName, NULL, dwSize, &data.at(0))) {
    TRACE("GetFileVersionInfo failed with error %d\n", GetLastError());
    return FALSE;
  }

  // 041904b0 - russian language resource ID
  void* pvProductVersion{ nullptr };
  unsigned int iProductVersionLen{ 0 };
  if (!::VerQueryValueW(&data.at(0), LR"(\StringFileInfo\041904b0\ProductVersion)", &pvProductVersion, &iProductVersionLen)) {
    TRACE("Can't obtain ProductVersion from resources\n");
    return FALSE;
  }

  version.id.SetString(static_cast<LPCTSTR>(pvProductVersion), iProductVersionLen); // NOLINT
  if (version.id.GetLength() <= VERSION_PREFIX_LEN) {
    TRACE("Invalid version format\n");
    return FALSE;
  }
  version.id = version.id.Left(version.id.GetLength() - VERSION_PREFIX_LEN); // NOLINT

#ifdef _WIN64
  version.id.Append(L" 64bit");
#else
  version.id.Append(L" 32bit");
#endif

  return TRUE;
}

BOOL ExteorApplication::CreateMainFrame() {
  m_pMainWnd = new ExteorFrame();
  if (MainFrame().LoadFrame(IXTRAD_MENU_MAINFRAME)) {
    m_pMainWnd->DragAcceptFiles();
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();
    return TRUE;
  } else {
    return FALSE;
  }
}

BOOL ExteorApplication::InitControls() {
  if (!docs->Initialize()) {
    return FALSE;
  } else if (AfxOleInit()) {
    ::AfxEnableControlContainer();
    ::AfxInitRichEdit2();
    return TRUE;
  } else {
    ::AfxMessageBox(IXTRE_OLE_INIT_FAILED, MB_ICONERROR);
    return FALSE;
  }
}

void ExteorApplication::LoadCustomState() {
  CWinAppEx::LoadCustomState();

  LoadOptions();
  SetupManager::InitEnvironment();
  SetupManager::CCLInitSequence();
}

void ExteorApplication::LoadOptions() {
  static constexpr auto COUNT_RECENT_FILE = 10;
  LoadStdProfileSettings(COUNT_RECENT_FILE);
  XTROptions::Get().Load(this);
}

void ExteorApplication::SaveCustomState() {
  CWinAppEx::SaveCustomState();
  XTROptions::Get().Save();
}

void ExteorApplication::CreateStartingLook() {
  if (*m_lpCmdLine != '\0') {
    OpenDocumentFile(m_lpCmdLine);
  } else if (!XTROptions::App().showStartPage) {
    OnStartPage();
  }
}

CString ExteorApplication::GetModuleFolder() const {
  CString path{};
  ::GetModuleFileNameW(m_hInstance, path.GetBuffer(_MAX_PATH + 1), _MAX_PATH + 1);
  path.ReleaseBuffer();
  return path.Mid(0, path.ReverseFind('\\') + 1);
}

BOOL ExteorApplication::RemoveMRUItem(const CString item) {
  for (auto i = 0; i < m_pRecentFileList->GetSize(); ++i) {
    if ((*m_pRecentFileList)[i] == item) {
      m_pRecentFileList->Remove(i);
      return TRUE;
    }
  }
  return FALSE;
}

void ExteorApplication::OnAppAbout() { // NOLINT(readability-convert-member-functions-to-static)
  AboutDlg(&MainFrame()).DoModal();
}

void ExteorApplication::OnStartPage() {
  startPage = std::make_unique<dialog::ExtStartPage>();
  startPage->Create(IXTRD_START_PAGE, m_pMainWnd);
}

void ExteorApplication::OnFileOpen() {
  if (::_wchdir(XTROptions::App().pathSaveAs) != 0) {
    TRACE0("Cannot set default browsing directory");
  }
  CFileDialog dialog(TRUE, nullptr, nullptr, 0, mfc::LoadSID(IXTRS_FILTER_MAIN));
  if (dialog.DoModal() != IDOK) {
    return;
  }  
  OpenDocumentFile(dialog.GetPathName());
}

void ExteorApplication::OnFileNew() { // NOLINT(readability-convert-member-functions-to-static)
  dialog::NewDocument().DoModal();
}

void ExteorApplication::OnOpenOptions() { // NOLINT(readability-convert-member-functions-to-static)
  dialog::ExteorOptionsSheet().DoModal();
}

void ExteorApplication::OpenHelp() { // NOLINT(readability-convert-member-functions-to-static)
  ui::HelpHandler::RunHelp();
}

void ExteorApplication::OnBugReport() { // NOLINT(readability-convert-member-functions-to-static)
  ::AfxMessageBox(IXTRS_REPORT_BUG_PROMPT, MB_ICONASTERISK);
  const auto email = mfc::FormatSID(IXTRS_REPORT_BUG_SUBJECT, g_Exteor.version.id.GetString());
  ::ShellExecuteW(nullptr, L"open", email, nullptr, nullptr, SW_SHOWNORMAL);
}

void ExteorApplication::OnMailDevelopers() { // NOLINT(readability-convert-member-functions-to-static)
  const auto email = mfc::FormatSID(IXTRS_REPORT_SUGGESTION, g_Exteor.version.id.GetString());
  ::ShellExecuteW(nullptr, L"open", email, nullptr, nullptr, SW_SHOWNORMAL);
}

} // namespace xtr