#ifndef API_H
#define API_H

#include "binom/binom.h"
#include "tcp/TcpClient.h"

#include "common.hpp"

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QObject>
#include <QStandardPaths>
#include <QVariant>
#include <QQuickItem>
#include <QMimeDatabase>
#include <QMimeData>

TcpClient& operator<<(TcpClient& client, binom::ByteArray data);
TcpClient& operator<<(TcpClient& client, binom::Variable data);

/// LibreHub application programming interface
class ApplicationProgrammingInterface : public QObject {
  Q_OBJECT
  TcpClient client;
  binom::DynamicStorage db;
  binom::FileNodeVisitor server_node;
  binom::FileNodeVisitor account_node;
  // Security
  EVP_PKEY* transport_ecdh_key_pair = nullptr;
  EVP_PKEY* account_ecdh_key_pair = nullptr;
  Security::AES_t transport_aes256_key;
  Security::AES_t private_aes256_key;
  binom::Variable profile;

  void routeOpenLayer(binom::Variable data);
  void routeSecuredLayer(binom::Variable data);

  void invalidActionCode();

  void saveServerKey(binom::Variable data);
  void processError(binom::Variable data);

  void processServerInfo(binom::Variable data);
  void processAuthentication(binom::Variable data);
  void processRequestResult(binom::Variable data);

  void gotoSecuredLayer(binom::Variable data); // Open -> Secured

  void requestUserList();
  void requestServerInfo();

  binom::Variable encrypt(binom::Variable data);
  binom::Variable auth(binom::Variable data);

  void connectImpl(uint32_t host, uint16_t port);

public:
  ApplicationProgrammingInterface(std::string database_path);

  static QVariant converBinOMtoQVariant(binom::Variable variable);

  // QML App public interface
  Q_PROPERTY(bool is_connected READ isConnected NOTIFY connectionStateChanged)
  Q_PROPERTY(bool is_connection_secured READ isConnectionSecured NOTIFY connectionSecureStateChanged)
  Q_PROPERTY(bool is_authenticated READ isAuthenticated NOTIFY authenticationStateChanged)
  Q_PROPERTY(QVariant server_info READ getServerInfo NOTIFY serverInfoChanged)
  Q_PROPERTY(QVariant user_list READ getUserList NOTIFY userListChanged)
  Q_PROPERTY(QVariant account_profile READ getProfile NOTIFY profileChanged)

  Q_INVOKABLE void connect(QString host, quint16 port);
  Q_INVOKABLE bool disconnect();
  Q_INVOKABLE bool authenticate(QString login, QString password);
  Q_INVOKABLE bool registerate(QString login, QString password, QString email);
  Q_INVOKABLE void sendPost(QVariant data);
  // Utilite functions
  Q_INVOKABLE QString getMimeType(QString url);

  bool isConnected() const {return client.getStatus() == SocketStatus::connected;}
  bool isConnectionSecured() const {return isConnected() && transport_aes256_key;}

  bool isAuthenticated() const { return account_ecdh_key_pair; }
  void tryConnect();
  void tryAuthenticateByUUID();

  QVariant getServerInfo() const;
  QVariant getUserList() const;
  QVariant getProfile();

signals:
  void recivedError(QString err);
  void connectionStateChanged(bool is_connected);
  void connectionSecureStateChanged(bool is_connection_secured);
  void authenticationStateChanged(bool is_authenticated);

  void userListChanged(QVariant user_list);
  void serverInfoChanged(QVariant server_info);
  void profileChanged(QVariant account_profile);
};

/// LibreHub application programming interface
typedef ApplicationProgrammingInterface API;

#endif // API_H
