#pragma once

#include <cstddef>
#include <cstdint>
#include <iostream>
#include <memory>
#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"

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

enum class ProjectModelType
{
   Map,
};

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

   virtual ProjectModelType type() const = 0;

   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 MapModel final : public AbstractMapModel, public ProjectModel
{
   Q_OBJECT

public:
   explicit MapModel(QObject *parent = nullptr);
   ~MapModel() override;

   ProjectModelType type() const override;

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

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

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

public:
   explicit MapProps(std::weak_ptr<MapModel> mapModel,
                     QWidget *parent = nullptr);
   ~MapProps();

private:
   std::weak_ptr<MapModel> mapModel;
};

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

public:
   explicit MapView(std::weak_ptr<MapModel> mapModel,
                    QWidget *parent = nullptr);
   ~MapView();

private:
   std::weak_ptr<MapModel> 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(ProjectModelType type);
   ~Project();

   std::shared_ptr<ProjectModel> getModel();
   std::shared_ptr<MapModel>     getMapModel();

protected:
   void closeEvent(QCloseEvent *event) override;

private:
   std::shared_ptr<ProjectModel> model;
   QWidget                      *view;
};

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
}

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

static inline constexpr std::uint32_t fourCC(std::uint8_t a,
                                             std::uint8_t b,
                                             std::uint8_t c,
                                             std::uint8_t d)
{
   return (a << 24) | (b << 16) | (c << 8) | d;
}

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

// EOF
