// DatabaseUtility.h: interface for the CDatabaseUtility class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_DATABASEUTILITY_H__02069DB9_2872_407E_9CFD_50FAD481ADD7__INCLUDED_)
#define AFX_DATABASEUTILITY_H__02069DB9_2872_407E_9CFD_50FAD481ADD7__INCLUDED_

#if _MSC_VER < 1400 // To prevent STL warning for pre VC++ 2005
#pragma warning(disable:4786)
#endif

#include <afxdao.h>
#include <set>
#include <map>
#include <vector>
#include <functional>
#include "DaoRunnable.h"

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifdef  BUILD_UTILITYDLL
#define     UTILITYDLL  __declspec(dllexport)
#else
#define     UTILITYDLL  __declspec(dllimport)
#endif

template <typename T>
class unique_dao_handle
{
T* handle;
public:
    explicit unique_dao_handle(T * handle = nullptr)
        : handle(handle)
    {
    }

    ~unique_dao_handle()
    {
        close();
    }

    void reset(T* ptr =  nullptr)
    {
        if (handle != ptr)
        {
            close();
            handle = ptr;
        }
    }

    T* get() const
    {
        return handle;
    }

    T* operator->() const
    {
        return handle;
    }

    void close()
    {
        if (*this)
        {
            if (handle->IsOpen())
            {
                handle->Close();
            }

            delete handle;
        }

        handle = nullptr;
    }

    operator bool() const
    {
        return handle != nullptr;
    }

    T& operator *()
    {
        return *handle;
    }
};

struct CMyDaoFieldInfo : CDaoFieldInfo
{
    // default copy constructor
    CMyDaoFieldInfo()
    {
    }

    //  copy constructor for initialising CMyFieldInfo with CDaoFieldInfo
    CMyDaoFieldInfo(const CDaoFieldInfo& rhs)
    {
        m_strName           = rhs.m_strName;           // Primary
        m_nType             = rhs.m_nType;             // Primary
        m_lSize             = rhs.m_lSize;             // Primary
        m_lAttributes       = rhs.m_lAttributes;       // Primary
        m_nOrdinalPosition  = rhs.m_nOrdinalPosition;  // Secondary
        m_bRequired         = rhs.m_bRequired;         // Secondary
        m_bAllowZeroLength  = rhs.m_bAllowZeroLength;  // Secondary
        m_lCollatingOrder   = rhs.m_lCollatingOrder;   // Secondary
        m_strForeignName    = rhs.m_strForeignName;    // Secondary
        m_strSourceField    = rhs.m_strSourceField;    // Secondary
        m_strSourceTable    = rhs.m_strSourceTable;    // Secondary
        m_strValidationRule = rhs.m_strValidationRule; // All
        m_strValidationText = rhs.m_strValidationText; // All
        m_strDefaultValue   = rhs.m_strDefaultValue;   // All
    }

    // Need to overload < opertor bcos std::set requires this for less than comparison
    virtual bool operator <(const CMyDaoFieldInfo& rhs) const
    { // apply operator< to operands
      // if 2 field is same name do not save the other field to set
        if (m_strName.Compare(rhs.m_strName) == 0)
        {
            return false;
        }

        // if field position is the same as right hand side check if they are the same field
        if (m_nOrdinalPosition == rhs.m_nOrdinalPosition)
        {
            return m_strName.Compare(rhs.m_strName) == -1; // A negative value if m_strName is less than rhs.m_strName
        }
        else // sort according to field position
        {
            return m_nOrdinalPosition <  rhs.m_nOrdinalPosition;
        }
    }
};

/**
 * @class   CDatabaseUtility
 *
 * @brief   An utility class to perform common operations on the database.
 *
 */

class UTILITYDLL CDatabaseUtility
{
public:
    static bool DeleteRecord(const CString &csDatabaseName, const CString &csTableName, const CString &csPackageName);
    static bool DeleteAllRecipesAndPackages();
    static bool DeleteRecipeAndPackage(const CString &csDatabaseName, const CString &csPackageName);
    static bool DeleteAllRecords(const CString &csDatabaseName, const CString &csTableName);
    static bool GetAllLanguageTables(CDaoDatabase* pDatabase, CStringArray *csaTableArray);
    static bool DeleteField(const CString &csPath, const CString &csTableName, const CString &csFieldName);
    static bool SetFieldValueToAllRecords(const CString &csPath, const CString &csTableName, const CString &csFieldName, short nFieldType, const CString &csDefaultValue);
    static bool CreateNewField(const CString &csPath, const CString &csTableName, CString strName, short nType, long lSize, long lAttributes, const CString &csDefaultValue);
    static bool IsFieldPresent(const CString &csPath, const CString &csTableName, const CString &csFieldName);
    static bool IsFieldPresent(CDaoDatabase *db, const CString &csTableName, const CString &csFieldName);
    static void IsFieldPresent(CDaoDatabase *db, const CString &csTableName, std::set<CString> &vFieldName, std::map<CString, bool> &mapFieldName);

    static bool CreateDatabase(const CString &csPath);
    static bool CreateNewField(const CString &csPath, const CString &csTableName, CDaoFieldInfo *pFieldInfo, const CString &csDefaultValue);

    static bool InsertField(const CString &csPath, const CString &csTableName, const CString &csNewFieldName, short nNewFieldPosition, short nType, long lSize, long lAttributes, const CString &csDefaultValue);
    static bool InsertFieldSql(const CString &csPath, const CString &csTableName, const CString &csNewFieldName, short nNewFieldPosition, short nType, long lSize, long lAttributes, const CString &csDefaultValue);

    static bool InsertBooleanField(const CString &csPath, const CString &csTableName, const CString &csNewFieldName, short nNewFieldPosition, bool bDefaultValue = false);
    static bool InsertStringField(const CString &csPath, const CString &csTableName, const CString &csNewFieldName, short nNewFieldPosition, const CString &csDefaultValue = "", long lSize = 50, long lAttributes = dbVariableField);
    static bool InsertLongField(const CString &csPath, const CString &csTableName, const CString &csNewFieldName, short nNewFieldPosition,  long lDefaultValue = 0);
    static bool InsertDoubleField(const CString &csPath, const CString &csTableName, const CString &csNewFieldName, short nNewFieldPosition,  double dDefaultValue = 0);

    static short GetFieldCount(CDaoDatabase* pDatabase, const CString &csTableName);
    static int   GetRecordCount(CDaoDatabase* pDatabase, const CString &csTableName);
    static bool  GetFieldData(CDaoDatabase *pDatabase, const CString &csTableName, short fieldType, CString fieldName, CString* m_pFieldData);
    static bool  GetAllFieldInfo(CDaoDatabase* pDatabase, const CString &csTableName, CDaoFieldInfo* m_pFieldInfo);
    static bool  IsTablePresent(CDaoDatabase *pDatabase, CString strTableName);
    static bool  SetFieldValueToAllRecords(CDaoDatabase* pDataBase, const CString &csTableName, CDaoFieldInfo& fieldInfo, CString* csFieldData, bool bAddNewRecord = false, int iRecordCount = 0);
    static bool  SetFieldValueToAllRecords(CDaoDatabase* pDataBase, const CString &csTableName, CDaoFieldInfo& fieldInfo, const CString &csFieldData, bool bAddNewRecord = false, int iRecordCount = 0);

/*  InsertFieldList() is for consolidate all the fields to be insert into the same table.
    ExecuteInsertField() is for insert all the consolidated fields into the same table.
    User has to create an instance of CDatabaseUtility. Then call InsertFieldList() each time to insert different fields.
    Finally, call ExecuteInsertField() to execute insert fields to table.*/
    bool InsertFieldList(const CString &csNewFieldName, short nNewFieldPosition, short nType, long lSize, long lAttributes, const CString &csDefaultValue);
    bool ExecuteInsertField(const CString &csPath, const CString &csTableName);
    bool ExecuteInsertField(const CString &csPath, const CString &csTableName, std::vector<CString> &vFieldNameSeq);

/*  copy table structure and data from source table to destination table in same or another database file.
    Set csDestinationDbFile to empty if same database file else set csDestinationTable to the path of the database file.
    Set bPromptError = true to prompt database error, else set to false.
    Return true if successful */
    static bool CopyTableStructAndData(CDaoDatabase* sourceDb, const CString &csSourceTable, const CString &csDestinationTable, const CString &csDestinationDbFile = "", bool bPromptError = false);

/*  copy table all fielded data from source table to destination table in same or another database file.
    Set csDestinationDbFile to empty if same database file else set csDestinationTable to the path of the database file.
    Set bPromptError = true to prompt database error, else set to false.
    Return true if successful */
    static bool CopyTableFieldData(CDaoDatabase* sourceDb, const CString &csSourceTable, const CString &csDestinationTable,
                                   std::map<CString, CString>* fieldAndCriteria = NULL, const CString &csDestinationDbFile = "", bool bPromptError = false);

/*  copy package info from existing package to new package.
    iStartFieldNo = the field no to start copy package
    Set bPromptError = true to prompt database error, else set to false.
    Return true if successful */
    static bool CopyToNewPackageInfo(CDaoDatabase* db, const CString &csNewPackage, const CString &csExistingPackage, const CString &csTable, int iStartFieldNo = 0, bool bPromptError = false);

/*  copy record field from existing package to new package
    iStartFieldNo = the field no to start copy package
    Set bPromptError = true to prompt database error, else set to false.
    Return true if successful */
    static bool CopyRecField(CDaoRecordset &NewPkgRS, CDaoRecordset &ExistPkgRS, const CString &csTable,
                             const CString &csNewPkg, const CString &csExistingPkg, int iStartFieldNo, bool bPromptError = false);

/*  Return true if sucessful. Create new table and copy over field information
    Set bPromptError = true to prompt database error, else set to false. */
    static bool CreateNewTableAndCopyFieldInfo(CDaoDatabase *pSourceDatabase, CDaoDatabase *pDestDatabase, const CString &csTableName, bool bPromptError = false);

/// copy packageData from source table to destination table. return true if successful
    static bool CopyPackageData(CDaoDatabase* pDatabase, const CString &csSourceTableName, const CString &csDestTableName, const CString &csPackageName);
    static void SafeClose(CDaoDatabase &db);
    static void SafeClose(CDaoRecordset &rs);
    static void SafeClose(CDaoTableDef &td);
    static bool Execute(const DaoRunner &runner, const CString &csErrorMsg = "", const char* filename = nullptr, int lineNo = 0);
    static bool ValidateNotNull(CDaoDatabase* pDatabase, int msgId);

private:

    /*  A sorted List to store field name to be inserted to database table.
       This list is used by InsertFieldList() to store the field name.
       Call ExecuteInsertField to insert the field to the database table */
    std::set<CMyDaoFieldInfo> m_setFieldInfoList;

public:
    CDatabaseUtility();
    virtual ~CDatabaseUtility();
};
#endif // !defined(AFX_DATABASEUTILITY_H__02069DB9_2872_407E_9CFD_50FAD481ADD7__INCLUDED_)
