/***************************************************************************
                          ksuserdb.h  -  K Desktop Planetarium
                             -------------------
    begin                : Wed May 2 2012
    copyright            : (C) 2012 by Rishab Arora
    email                : ra.rishab@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#pragma once

#include "auxiliary/profileinfo.h"
#ifndef KSTARS_LITE
#include "oal/oal.h"
#endif
#include "skyobjects/skyobject.h"

#include <QFile>
#include <QSqlDatabase>
#include <QSqlError>
#include <QStringList>
#include <QVariantMap>
#include <QXmlStreamReader>

#include <memory>

class LineList;
class ArtificialHorizonEntity;

/**
 * @brief Single class to delegate all User database I/O
 *
 * usage: Call QSqlDatabase::removeDatabase("userdb"); after the object
 * of this class is deallocated
 * @author Rishab Arora
 * @author Jasem Mutlaq
 * @version 1.2
 **/
// cppcheck-suppress noConstructor
class KSUserDB
{
  public:
    ~KSUserDB();

    /**
     * Initialize KStarsDB while running splash screen
     * @return true on success
     */
    bool Initialize();

    QSqlDatabase GetDatabase();

    /************************************************************************
     ********************************* Drivers ******************************
     ************************************************************************/

    int AddProfile(const QString &name);

    bool DeleteProfile(ProfileInfo *pi);

    void SaveProfile(ProfileInfo *pi);

    /**
     * @brief GetAllProfiles Return all profiles in a QList
     * @return QMap with the keys as profile names and values are profile ids.
     */
    void GetAllProfiles(QList<std::shared_ptr<ProfileInfo>> &profiles);

    /************************************************************************
     ******************************* Dark Library****************************
     ************************************************************************/

    void AddDarkFrame(const QVariantMap &oneFrame);
    bool DeleteDarkFrame(const QString &filename);
    void GetAllDarkFrames(QList<QVariantMap> &darkFrames);


    /************************************************************************
     ******************************* Effective FOVs *************************
     ************************************************************************/

    void AddEffectiveFOV(const QVariantMap &oneFOV);
    bool DeleteEffectiveFOV(const QString &id);
    void GetAllEffectiveFOVs(QList<QVariantMap> &effectiveFOVs);


    /************************************************************************
     ******************************* Driver Alias *************************
     ************************************************************************/

    bool AddCustomDriver(const QVariantMap &oneDriver);
    bool DeleteCustomDriver(const QString &id);
    void GetAllCustomDrivers(QList<QVariantMap> &CustomDrivers);

    /************************************************************************
     *********************************** HiPS *******************************
     ************************************************************************/

    void AddHIPSSource(const QMap<QString, QString> &oneSource);
    bool DeleteHIPSSource(const QString &ID);
    void GetAllHIPSSources(QList<QMap<QString,QString>> &HIPSSources);

    /************************************************************************
     *********************************** DSLR *******************************
     ************************************************************************/

    void AddDSLRInfo(const QMap<QString, QVariant> &oneInfo);
    bool DeleteDSLRInfo(const QString &model);
    bool DeleteAllDSLRInfo();
    void GetAllDSLRInfos(QList<QMap<QString,QVariant>> &DSLRInfos);

    /************************************************************************
     ******************************* Observers ******************************
     ************************************************************************/

    /** @brief Adds a new observer into the database **/
    void AddObserver(const QString &name, const QString &surname, const QString &contact);

    /**
     * @brief Returns the unique id of the user with given name & surname
     *
     * @return true if found, false otherwise
     **/
    bool FindObserver(const QString &name, const QString &surname);
    /**
     * @brief Removes the user with unique id as given by FindObserver
     * Returns false if the user is not found
     *
     * @return bool
     **/
    bool DeleteObserver(const QString &id);

#ifndef KSTARS_LITE
    /**
     * @brief Updates the passed reference of observer_list with all observers
     * The original content of the list is cleared.
     *
     * @return void
     **/
    void GetAllObservers(QList<OAL::Observer *> &observer_list);
#endif
    /************************************************************************
     ********************************* Horizon ******************************
     ************************************************************************/

    // Jasem: Add API doc
    void DeleteAllHorizons();
    void AddHorizon(ArtificialHorizonEntity *horizon);
    QList<ArtificialHorizonEntity *> GetAllHorizons();

    /************************************************************************
     ********************************* Flags ********************************
     ************************************************************************/

    /**
     * @brief Erases all the flags from the database
     *
     * @return void
     **/
    void DeleteAllFlags();

    /**
     * @brief Add a new Flag with given parameters
     *
     * @param ra Right Ascension
     * @param dec Declination
     * @param epoch Epoch
     * @param image_name Name of the image used
     * @param label Content of display label on screen
     * @param labelColor Color of the label (name or hex code) eg #00FF00
     * @return void
     **/
    void AddFlag(const QString &ra, const QString &dec, const QString &epoch, const QString &image_name,
                 const QString &label, const QString &labelColor);
    /**
     * @brief Returns a QList populated with all stored flags
     * Order: const QString &ra, const QString &dec, const QString &epoch,
     *        const QString &imageName, const QString &label, const QString &labelColor
     * @return QList< QStringList >
     **/
    QList<QStringList> GetAllFlags();

    /************************************************************************
      ******************************* Equipment ******************************
     ************************************************************************/

    /**
     * @brief Erase the equipment with given type and unique id
     * Valid equipment types: "telescope","lens","filter"
     *
     * @param type Equipment type (same as table name)
     * @param id Unique id (same as row number)
     * @return void
     **/
    void DeleteEquipment(const QString &type, const int &id);
    /**
     * @brief Erases the whole equipment table of given type
     *
     * @param type Equipment type (same as table name)
     * @return void
     **/
    void DeleteAllEquipment(const QString &type);

    /************************************************************************
     ********************************** Scope *******************************
     ************************************************************************/

    /**
     * @brief Appends the scope with given details in the database
     *
     * @return void
     **/
    void AddScope(const QString &model, const QString &vendor, const QString &driver, const QString &type,
                  const double &focalLength, const double &aperture);
    /**
     * @brief Replaces the scope with given ID with provided content
     *
     * @return void
     **/
    void AddScope(const QString &model, const QString &vendor, const QString &driver, const QString &type,
                  const double &focalLength, const double &aperture, const QString &id);
#ifndef KSTARS_LITE
    /**
     * @brief updates the scope list with all scopes from database
     * List is cleared and then filled with content.
     *
     * @param m_scopeList Reference to list to be updated
     * @return void
     **/
    void GetAllScopes(QList<OAL::Scope *> &m_scopeList);
#endif
    /************************************************************************
     ******************************* Eye Piece ******************************
     ************************************************************************/

    /**
     * @brief Add new eyepiece to database
     *
     * @return void
     **/
    void AddEyepiece(const QString &vendor, const QString &model, const double &focalLength, const double &fov,
                     const QString &fovunit);
    /**
     * @brief Replace eyepiece at position (ID) with new content
     *
     * @return void
     **/
    void AddEyepiece(const QString &vendor, const QString &model, const double &focalLength, const double &fov,
                     const QString &fovunit, const QString &id);
#ifndef KSTARS_LITE
    /**
     * @brief Populate the reference passed with all eyepieces
     *
     * @param m_eyepieceList Reference to list of eyepieces
     * @return void
     **/
    void GetAllEyepieces(QList<OAL::Eyepiece *> &m_eyepieceList);
#endif
    /************************************************************************
     ********************************** Lens ********************************
     ************************************************************************/

    /**
     * @brief Add a new lens to the database
     *
     * @return void
     **/
    void AddLens(const QString &vendor, const QString &model, const double &factor);
    /**
     * @brief Replace a lens at given ID with new content
     *
     * @return void
     **/
    void AddLens(const QString &vendor, const QString &model, const double &factor, const QString &id);
#ifndef KSTARS_LITE
    /**
     * @brief Populate the reference passed with all lenses
     *
     * @param m_lensList Reference to list of lenses
     * @return void
     **/
    void GetAllLenses(QList<OAL::Lens *> &m_lensList);
#endif
    /************************************************************************
     ******************************** Filters *******************************
     ************************************************************************/

    /**
     * @brief Add a new filter to the database
     *
     * @return void
     **/
    void AddFilter(const QString &vendor, const QString &model, const QString &type, const QString &color,
                             int offset, double exposure, bool useAutoFocus, const QString &lockedFilter, int absFocusPos);
    /**
     * @brief Replace a filter at given ID with new content
     *
     * @return void
     **/
    void AddFilter(const QString &vendor, const QString &model, const QString &type, const QString &color,
                             int offset, double exposure, bool useAutoFocus, const QString &lockedFilter, int absFocusPos, const QString &id);
#ifndef KSTARS_LITE
    /**
     * @brief Populate the reference passed with all filters
     *
     * @param m_filterList Reference to list of filters
     * @return void
     **/
    void GetAllFilters(QList<OAL::Filter *> &m_filterList);
#endif
  private:
    /**
     * @brief This function initializes a new database in the user's directory.
     * To be run only when a new db is needed. Should not be run over existing database file.
     *
     * @return bool
     **/
    bool RebuildDB();
    /**
     * @brief Rebuilds the User DB from scratch using RebuildDB.
     * Also, loads any previous user data into the DB.
     *
     * @return bool
     **/
    bool FirstRun();

#if 0
        /**
         * @brief Imports flags data from previous format
         *
         * @return bool
         **/
        bool ImportFlags();
        /**
         * @brief Imports users from previous format
         *
         * @return bool
         **/
        bool ImportUsers();
        /**
         * @brief Imports equipment from previous format
         *
         * @return bool
         **/
        bool ImportEquipment();

#endif
    // Helper functions
    void readScopes();
    void readScope();
    void readEyepieces();
    void readEyepiece();
    void readLenses();
    void readLens();
    void readFilters();
    void readFilter();

    void DeleteProfileDrivers(ProfileInfo *pi);
    void GetProfileDrivers(ProfileInfo *pi);
    //void GetProfileCustomDrivers(ProfileInfo *pi);

    /**
     * @brief Function to return the last error encountered by SQLite
     *
     * @return QSqlError
     **/
    inline QSqlError LastError();

    /** Linked to the user database _once_. **/
    QSqlDatabase userdb_;
    /** XML reader for importing old formats **/
    QXmlStreamReader *reader_ { nullptr };

    static const uint16_t SCHEMA_VERSION = 305;
};
