/**************************************************************************
*
* Project:  ChartManager
* Purpose:  Basic Chart Info Storage
* Author:   David S Register, Mark A Sikes
*
***************************************************************************
*   Copyright (C) 2010 by David S. Register *
*                                                                         *
*   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.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
**************************************************************************/

#ifndef __CHARTDBS_H__
#define __CHARTDBS_H__

#include <map>
#include <memory>
#include <vector>

#include "ocpn_types.h"
#include "bbox.h"
#include "LLRegion.h"

class wxGenericProgressDialog;
class ChartBase;

//    A small class used in an array to describe chart directories
class ChartDirInfo
{
public:
    wxString    fullpath;
    wxString    magic_number;
};

WX_DECLARE_OBJARRAY(ChartDirInfo, ArrayOfCDI);

///////////////////////////////////////////////////////////////////////

static const int DB_VERSION_PREVIOUS = 17;
static const int DB_VERSION_CURRENT = 18;

class ChartDatabase;
class ChartGroupArray;

struct ChartTableEntry_onDisk_18
{
    int         EntryOffset;
    int         ChartType;
    int         ChartFamily;
    float       LatMax;
    float       LatMin;
    float       LonMax;
    float       LonMin;
    
    int         Scale;
    int         edition_date;
    int         file_date;
    
    int         nPlyEntries;
    int         nAuxPlyEntries;
    
    float       skew;
    int         ProjectionType;
    bool        bValid;
    
    int         nNoCovrPlyEntries;
};


struct ChartTableEntry_onDisk_17
{
    int         EntryOffset;
    int         ChartType;
    float       LatMax;
    float       LatMin;
    float       LonMax;
    float       LonMin;

    int         Scale;
    int         edition_date;
    int         file_date;

    int         nPlyEntries;
    int         nAuxPlyEntries;

    float       skew;
    int         ProjectionType;
    bool        bValid;

    int         nNoCovrPlyEntries;
};


struct ChartTableEntry_onDisk_16
{
      int         EntryOffset;
      int         ChartType;
      float       LatMax;
      float       LatMin;
      float       LonMax;
      float       LonMin;

      int         Scale;
      int         edition_date;
      int         file_date;

      int         nPlyEntries;
      int         nAuxPlyEntries;

      float       skew;
      int         ProjectionType;
      bool        bValid;
};


struct ChartTableEntry_onDisk_15
{
    int         EntryOffset;
    int         ChartType;
    float       LatMax;
    float       LatMin;
    float       LonMax;
    float       LonMin;

    int         Scale;
    time_t      edition_date;
    time_t      file_date;

    int         nPlyEntries;
    int         nAuxPlyEntries;

    bool        bValid;
};

struct ChartTableEntry_onDisk_14
{
    int         EntryOffset;
    int         ChartType;
    char        ChartID[16];
    float       LatMax;
    float       LatMin;
    float       LonMax;
    float       LonMin;
    char        *pFullPath;
    int         Scale;
    time_t      edition_date;
    float       *pPlyTable;
    int         nPlyEntries;
    int         nAuxPlyEntries;
    float       **pAuxPlyTable;
    int         *pAuxCntTable;
    bool        bValid;
};

struct ChartTableHeader
{
    ChartTableHeader() {}
    ChartTableHeader(int dirEntries, int tableEntries) :
                nTableEntries(tableEntries), nDirEntries(dirEntries) {}

    void Read(wxInputStream &is);
    void Write(wxOutputStream &os);
    bool CheckValid();
    int GetDirEntries() const { return nDirEntries; }
    int GetTableEntries() const { return nTableEntries; }
    char *GetDBVersionString(){ return dbVersion; }

private:
    // NOTE: on-disk structure - cannot add, remove, or reorder!
    char dbVersion[4];
    int nTableEntries;
    int nDirEntries;
};

struct ChartTableEntry
{
    ChartTableEntry() { Clear(); }
    ChartTableEntry(ChartBase &theChart, wxString& utf8Path);
    ~ChartTableEntry();

    bool IsEqualTo(const ChartTableEntry &cte) const;
    bool IsEarlierThan(const ChartTableEntry &cte) const;
    bool Read(const ChartDatabase *pDb, wxInputStream &is);
    bool Write(const ChartDatabase *pDb, wxOutputStream &os);
    void Clear();
    void Disable();
    void ReEnable();
    
    void SetValid(bool valid) { bValid = valid; }
    time_t GetFileTime() const { return file_date; }

    int GetnPlyEntries() const { return nPlyEntries; }
    float *GetpPlyTable() const { return pPlyTable; }

    int GetnAuxPlyEntries() const { return nAuxPlyEntries; }
    float *GetpAuxPlyTableEntry(int index) const { return pAuxPlyTable[index];}
    int GetAuxCntTableEntry(int index) const { return pAuxCntTable[index];}

    int GetnNoCovrPlyEntries() const { return nNoCovrPlyEntries; }
    float *GetpNoCovrPlyTableEntry(int index) const { return pNoCovrPlyTable[index];}
    int GetNoCovrCntTableEntry(int index) const { return pNoCovrCntTable[index];}
    
    const LLBBox &GetBBox() const { return m_bbox; } 
    
    char *GetpFullPath() const { return pFullPath; }
    float GetLonMax() const { return LonMax; }
    float GetLonMin() const { return LonMin; }
    float GetLatMax() const { return LatMax; }
    float GetLatMin() const { return LatMin; }
    int GetScale() const { return Scale; }
    int GetChartType() const  { return ChartType; }
    int GetChartFamily() const  { return ChartFamily; }
    int GetChartProjectionType() const { return ProjectionType; }
    float GetChartSkew() const { return Skew; }

    bool GetbValid(){ return bValid;}
    void SetEntryOffset(int n) { EntryOffset = n;}
    const wxString *GetpFileName(void) const { return m_pfilename; }
    wxString *GetpsFullPath(void) const { return m_psFullPath; }
    wxString GetFullSystemPath() const { return m_fullSystemPath; }
    
    const std::vector<int> &GetGroupArray(void) const { return m_GroupArray; }
    void ClearGroupArray(void) { m_GroupArray.clear(); }
    void AddIntToGroupArray( int val ) { m_GroupArray.push_back( val ); }
    void SetAvailable(bool avail ){ m_bavail = avail;}

    std::vector<float> GetReducedPlyPoints();
    std::vector<float> GetReducedAuxPlyPoints( int iTable);

    LLRegion quilt_candidate_region;

    void        SetScale(int scale);
    bool	Scale_eq( int b ) const { return abs ( Scale - b) <= rounding; }
    bool        Scale_ge( int b ) const { return  Scale_eq( b ) || Scale > b; }
    bool        Scale_gt( int b ) const { return  Scale > b && !Scale_eq( b ); }

  private:
    int         EntryOffset;
    int         ChartType;
    int         ChartFamily;
    float       LatMax;
    float       LatMin;
    float       LonMax;
    float       LonMin;
    char        *pFullPath;
    int		rounding;
    int         Scale;
    time_t      edition_date;
    time_t      file_date;
    float       *pPlyTable;
    int         nPlyEntries;
    int         nAuxPlyEntries;
    float       **pAuxPlyTable;
    int         *pAuxCntTable;
    float       Skew;
    int         ProjectionType;
    bool        bValid;
    int         nNoCovrPlyEntries;
    int         *pNoCovrCntTable;
    float       **pNoCovrPlyTable;
    
    std::vector<int> m_GroupArray;
    wxString    *m_pfilename;             // a helper member, not on disk
    wxString    *m_psFullPath;
    wxString    m_fullSystemPath;
    
    LLBBox m_bbox;
    bool        m_bavail;
    
    std::vector<float> m_reducedPlyPoints;
    
    std::vector<std::vector<float> > m_reducedAuxPlyPointsVector;
};

enum
{
      BUILTIN_DESCRIPTOR       = 0,
      PLUGIN_DESCRIPTOR
};

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

      ChartClassDescriptor(wxString classn, wxString mask, int type)
      : m_class_name(classn), m_search_mask(mask), m_descriptor_type(type) {};

      wxString    m_class_name;
      wxString    m_search_mask;
      int         m_descriptor_type;
};


///////////////////////////////////////////////////////////////////////
// Chart Database
///////////////////////////////////////////////////////////////////////

WX_DECLARE_OBJARRAY(ChartTableEntry, ChartTable);
WX_DECLARE_OBJARRAY(ChartClassDescriptor, ArrayOfChartClassDescriptor);

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

    bool Create(ArrayOfCDI& dir_array, wxGenericProgressDialog *pprog);
    bool Update(ArrayOfCDI& dir_array, bool bForce, wxGenericProgressDialog *pprog);

    bool Read(const wxString &filePath);
    bool Write(const wxString &filePath);

    bool AddSingleChart( wxString &fullpath, bool b_force_full_search = true );
    bool RemoveSingleChart( wxString &ChartFullPath );
    
    const wxString & GetDBFileName() const { return m_DBFileName; }
    ArrayOfCDI& GetChartDirArray(){ return m_dir_array; }
    wxArrayString &GetChartDirArrayString(){ return m_chartDirs; }
    void SetChartDirArray( ArrayOfCDI array ){ m_dir_array = array; }
    bool CompareChartDirArray( ArrayOfCDI& test_array );
    wxString GetMagicNumberCached(wxString dir);
    
    void UpdateChartClassDescriptorArray(void);

    int GetChartTableEntries() const { return active_chartTable.size(); }
    const ChartTableEntry &GetChartTableEntry(int index) const;
    ChartTableEntry *GetpChartTableEntry(int index) const;
    inline ChartTable &GetChartTable(){ return active_chartTable; }
    
    bool IsValid() const { return bValid; }
    int DisableChart(wxString& PathToDisable);
    bool GetCentroidOfLargestScaleChart(double *clat, double *clon, ChartFamilyEnum family);
    int GetDBChartType(int dbIndex);
    int GetDBChartFamily(int dbIndex);
    float GetDBChartSkew(int dbIndex);
    int GetDBChartProj(int dbIndex);
    int GetDBChartScale(int dbIndex);

    bool GetDBBoundingBox(int dbindex, LLBBox &box);
    const LLBBox &GetDBBoundingBox(int dbIndex);
    
    int  GetnAuxPlyEntries(int dbIndex);
    int  GetDBPlyPoint(int dbIndex, int plyindex, float *lat, float *lon);
    int  GetDBAuxPlyPoint(int dbIndex, int plyindex, int iAuxPly, float *lat, float *lon);
    int  GetVersion(){ return m_dbversion; }
    wxString GetFullChartInfo(ChartBase *pc, int dbIndex, int *char_width, int *line_count);
    int FinddbIndex(wxString PathToFind);
    wxString GetDBChartFileName(int dbIndex);
    void ApplyGroupArray(ChartGroupArray *pGroupArray);
    bool IsChartAvailable( int dbIndex );
    ChartTable    active_chartTable;
    std::map <wxString, int> active_chartTable_pathindex;
    
    std::vector<float> GetReducedPlyPoints(int dbIndex);
    std::vector<float> GetReducedAuxPlyPoints(int dbIndex, int iTable);

    bool IsBusy(){ return m_b_busy; }

protected:
    virtual ChartBase *GetChart(const wxChar *theFilePath, ChartClassDescriptor &chart_desc) const;
    int AddChartDirectory(const wxString &theDir, bool bshow_prog);
    void SetValid(bool valid) { bValid = valid; }
    ChartTableEntry *CreateChartTableEntry(const wxString &filePath, wxString &utf8Path, ChartClassDescriptor &chart_desc);

    ArrayOfChartClassDescriptor    m_ChartClassDescriptorArray;
    ArrayOfCDI    m_dir_array;
    bool              m_b_busy;

private:
    bool IsChartDirUsed(const wxString &theDir);

    int SearchDirAndAddCharts(wxString& dir_name_base, ChartClassDescriptor &chart_desc, wxGenericProgressDialog *pprog);

    int TraverseDirAndAddCharts(ChartDirInfo& dir_info, wxGenericProgressDialog *pprog, wxString& dir_magic, bool bForce);
    bool DetectDirChange(const wxString & dir_path, const wxString & prog_label, const wxString & magic, wxString &new_magic, wxGenericProgressDialog *pprog);

    bool AddChart( wxString &chartfilename, ChartClassDescriptor &chart_desc, wxGenericProgressDialog *pprog,
                   int isearch, bool bthis_dir_in_dB );

    bool Check_CM93_Structure(wxString dir_name);

    bool          bValid;
    wxArrayString m_chartDirs;
    int           m_dbversion;

    ChartTableEntry           m_ChartTableEntryDummy;   // used for return value if database is not valid
    wxString      m_DBFileName;
    
    int           m_pdifile;
    int           m_pdnFile;
    
    int         m_nentries;

    LLBBox m_dummy_bbox;
};


//-------------------------------------------------------------------------------------------
//    Chart Group Structure Definitions
//-------------------------------------------------------------------------------------------
class ChartGroupElement;
class ChartGroup;

WX_DEFINE_ARRAY_PTR(ChartGroup*, ChartGroupArray);

class ChartGroupElement
{
  // ChartGroupElements need nothing special to delete since
  // m_missing_name_array is a wxArrayString which manages
  // memory for the strings cleanly without need for a .Clear.
public:
      wxString      m_element_name;
      wxArrayString m_missing_name_array;
};

class ChartGroup
{
public:
      wxString                                         m_group_name;
      std::vector<std::unique_ptr<ChartGroupElement>>  m_element_array;
};


#endif

