#pragma once

#include <cstddef>
#include <cstdint>
#include <iostream>
#include <optional>
#include <vector>

#include <QApplication>
#include <QCloseEvent>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QIcon>
#include <QMainWindow>
#include <QMdiSubWindow>
#include <QMessageBox>

#include "bindings.h"

#include "../ui/ui_about.h"
#include "../ui/ui_license.h"
#include "../ui/ui_mapprops.h"
#include "../ui/ui_mapview.h"
#include "../ui/ui_menu.h"
#include "../ui/ui_project.h"

// Types ---------------------------------------------------------------------|

class MapModel;
class MapProps;
class MapView;
class Menu;
class Project;

enum class ProjectType
{
   Map,
};

using byte = std::uint8_t;

// Interfaces ----------------------------------------------------------------|

class ProjectModel
{
public:
   virtual ~ProjectModel() {}

   virtual bool isDirty() const = 0;
   virtual bool open(QString const &path) = 0;
   virtual bool save() const = 0;
   virtual bool saveAs(QString const &path) const = 0;
};

class ProjectView
{
public:
   virtual ~ProjectView() {}
};

// Implementations -----------------------------------------------------------|

class MapModel final : public AbstractMapModel, public ProjectModel
{
   Q_OBJECT

public:
   explicit MapModel(Project *parent);
   ~MapModel() override;

   bool isDirty() const override;
   bool open(QString const &path) override;
   bool save() const override;
   bool saveAs(QString const &path) const override;

public slots:
   void deselect();
   void select(QModelIndex const &index);

signals:
   void deselected();
   void selected(std::uint16_t index);

private:
   QVariant data(const QModelIndex &index, int role = Qt::DisplayRole)
      const override;
};

class MapView final : public QWidget, public ProjectView, private Ui::MapView
{
   Q_OBJECT

public:
   explicit MapView(Project *parent);
   ~MapView();

private:
   MapModel *const mapModel;
};

// UI ------------------------------------------------------------------------|

class MapProps final : public QDialog, private Ui::MapProps
{
   Q_OBJECT

public:
   explicit MapProps(Project *parent);
   ~MapProps();

   void accept() override;

private:
   MapModel *const mapModel;
};

class Menu final : public QMainWindow, private Ui::Menu
{
   Q_OBJECT

public:
   explicit Menu(QWidget *parent = nullptr);
   ~Menu();

public slots:
   void mapNew();
   void mapOpen();
   void openAbout();
   void openAboutQt();
   void openMapProperties();
   void updateActions();

protected:
   void closeEvent(QCloseEvent *event) override;
   void openLicense(QWidget *parent);

private:
   Project       *activeProject() const;
   QMdiSubWindow *activeSubWindow() const;

   void addProject(Project *proj);
};

class Project final : public QMdiSubWindow, private Ui::Project
{
   Q_OBJECT

public:
   explicit Project(ProjectType type);
   ~Project();

   ProjectType getType() const;

   ProjectModel *getModel() const;
   MapModel     *getMapModel() const;

protected:
   void closeEvent(QCloseEvent *event) override;

private:
   ProjectType const type;

   ProjectModel *const model;
   ProjectView  *const view;
};

// Functions -----------------------------------------------------------------|

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-security"

template<typename... VA>
static inline
void dbgPrint([[maybe_unused]] char const *fmt, [[maybe_unused]] VA &&...va)
{
   #ifdef TYCHO_DEBUG_ASSERTIONS
   qDebug(fmt, std::forward<VA>(va)...);
   #endif
}

#pragma clang diagnostic pop

#define dbgPrintFunc() dbgPrint("%s", __func__)

constexpr
std::uint32_t fourCC(byte a, byte b, byte c, byte d)
{
   return (static_cast<std::uint32_t>(a) << 24) |
          (static_cast<std::uint32_t>(b) << 16) |
          (static_cast<std::uint32_t>(c) <<  8) |
           static_cast<std::uint32_t>(d);
}

extern "C" {
   char const *tychoAuthors();
   char const *tychoHomepage();
   char const *tychoLicenseText();
   char const *tychoRepository();
   char const *tychoVersion();
}

// EOF
