/***************************************************************************
 *
 * Project:  OpenCPN
 * Purpose:  S57 Chart Object
 * Author:   David Register
 *
 ***************************************************************************
 *   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 __S57CHART_H__
#define __S57CHART_H__

#include <wx/wx.h>
#include <wx/progdlg.h>
#include "s52s57.h"
#include "chartbase.h"
#include "wx/dir.h"
#include "wx/filename.h"
#include "wx/file.h"
#include "wx/stream.h"
#include "wx/wfstream.h"
#include "gdal/ogrsf_frmts.h"

#include "iso8211.h"

#include "gdal/gdal.h"
#include "s57RegistrarMgr.h"
#include "S57ClassRegistrar.h"
#include "S57Light.h"
#include "S57Sector.h"
#include "OCPNRegion.h"
#include "ocpndc.h"
#include "viewport.h"
#include "SencManager.h"
#include <memory>
#include "ocpn_plugin.h"
#include <unordered_map>

// ----------------------------------------------------------------------------
// Useful Prototypes
// ----------------------------------------------------------------------------
class ChartCanvas;

// ----------------------------------------------------------------------------
// S57 Utility Prototypes
// ----------------------------------------------------------------------------
extern "C" bool s57_GetChartExtent(const wxString &FullPath, Extent *pext);

void s57_DrawExtendedLightSectors(ocpnDC &temp_dc, ViewPort &VPoint,
                                  std::vector<s57Sector_t> &sectorlegs);
void s57_DrawExtendedLightSectorsGL(ocpnDC &temp_dc, ViewPort &VPoint,
                                  std::vector<s57Sector_t> &sectorlegs);
bool s57_CheckExtendedLightSectors(ChartCanvas *cc, int mx, int my,
                                   ViewPort &VPoint,
                                   std::vector<s57Sector_t> &sectorlegs);
bool s57_GetVisibleLightSectors(ChartCanvas *cc, double lat, double lon,
                                ViewPort &viewport,
                                std::vector<s57Sector_t> &sectorlegs);

//----------------------------------------------------------------------------
// Constants
//----------------------------------------------------------------------------

enum {
  BUILD_SENC_OK,
  BUILD_SENC_NOK_RETRY,
  BUILD_SENC_NOK_PERMANENT,
  BUILD_SENC_PENDING
};

//----------------------------------------------------------------------------
// Fwd Defns
//----------------------------------------------------------------------------

class ChartBase;
class ViewPort;
class ocpnBitmap;
class PixelCache;
class S57ObjectDesc;
class S57Reader;
class OGRS57DataSource;
class S57ClassRegistrar;
class S57Obj;
class VE_Element;
class VC_Element;
class connector_segment;
class ChartPlugInWrapper;

#include <wx/dynarray.h>

// Declare the Array of S57Obj
WX_DECLARE_OBJARRAY(S57Obj, ArrayOfS57Obj);

WX_DECLARE_LIST(ObjRazRules, ListOfObjRazRules);

//----------------------------------------------------------------------------
// s57 Chart object class
//----------------------------------------------------------------------------
class s57chart : public ChartBase {
public:
  s57chart();
  ~s57chart();

  virtual InitReturn Init(const wxString &name, ChartInitFlag flags);

  //    Accessors

  virtual ThumbData *GetThumbData(int tnx, int tny, float lat, float lon);
  virtual ThumbData *GetThumbData() { return pThumbData; }
  bool UpdateThumbData(double lat, double lon);

  virtual int GetNativeScale() { return m_Chart_Scale; }
  virtual double GetNormalScaleMin(double canvas_scale_factor,
                                   bool b_allow_overzoom);
  virtual double GetNormalScaleMax(double canvas_scale_factor,
                                   int canvas_width);

  void SetNativeScale(int s) { m_Chart_Scale = s; }

  virtual bool RenderRegionViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint,
                                    const OCPNRegion &Region);
  virtual bool RenderOverlayRegionViewOnDC(wxMemoryDC &dc,
                                           const ViewPort &VPoint,
                                           const OCPNRegion &Region);

  virtual bool RenderRegionViewOnDCNoText(wxMemoryDC &dc,
                                          const ViewPort &VPoint,
                                          const OCPNRegion &Region);
  virtual bool RenderRegionViewOnDCTextOnly(wxMemoryDC &dc,
                                            const ViewPort &VPoint,
                                            const OCPNRegion &Region);

  virtual void GetValidCanvasRegion(const ViewPort &VPoint,
                                    OCPNRegion *pValidRegion);
  virtual LLRegion GetValidRegion();

  virtual void GetPointPix(ObjRazRules *rzRules, float rlat, float rlon,
                           wxPoint *r);
  virtual void GetPointPix(ObjRazRules *rzRules, wxPoint2DDouble *en,
                           wxPoint *r, int nPoints);
  virtual void GetPixPoint(int pixx, int pixy, double *plat, double *plon,
                           ViewPort *vpt);

  virtual void SetVPParms(const ViewPort &vpt);

  virtual bool AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed);
  //      virtual bool IsRenderDelta(ViewPort &vp_last, ViewPort &vp_proposed);

  virtual double GetNearestPreferredScalePPM(double target_scale_ppm) {
    return target_scale_ppm;
  }

  void SetFullExtent(Extent &ext);
  bool GetChartExtent(Extent *pext);

  void SetColorScheme(ColorScheme cs, bool bApplyImmediate = true);
  virtual void UpdateLUPs(s57chart *pOwner);

  int _insertRules(S57Obj *obj, LUPrec *LUP, s57chart *pOwner);

  virtual ListOfObjRazRules *GetObjRuleListAtLatLon(
      float lat, float lon, float select_radius, ViewPort *VPoint,
      int selection_mask = MASK_ALL);
  bool DoesLatLonSelectObject(float lat, float lon, float select_radius,
                              S57Obj *obj);
  bool IsPointInObjArea(float lat, float lon, float select_radius, S57Obj *obj);
  virtual ListOfObjRazRules *GetLightsObjRuleListVisibleAtLatLon(
      float lat, float lon, ViewPort *VPoint);

  wxString GetObjectAttributeValueAsString(S57Obj *obj, int iatt,
                                           wxString curAttrName);
  static wxString GetAttributeValueAsString(S57attVal *pAttrVal,
                                            wxString AttrName);
  static bool CompareLights(const S57Light *l1, const S57Light *l2);
  wxString CreateObjDescriptions(ListOfObjRazRules *rule);
  static wxString GetAttributeDecode(wxString &att, int ival);

  int BuildRAZFromSENCFile(const wxString &SENCPath);
  static void GetChartNameFromTXT(const wxString &FullPath, wxString &Name);
  wxString buildSENCName(const wxString &name);

  //    DEPCNT VALDCO array access
  bool GetNearestSafeContour(double safe_cnt, double &next_safe_cnt);

  virtual std::list<S57Obj*> *GetAssociatedObjects(S57Obj *obj);

  virtual std::unordered_map<unsigned, VE_Element *> &Get_ve_hash(void) {
    return m_ve_hash;
  }
  virtual std::unordered_map<unsigned, VC_Element *> &Get_vc_hash(void) {
    return m_vc_hash;
  }

  virtual void ForceEdgePriorityEvaluate(void);

  float *GetLineVertexBuffer(void) { return m_line_vertex_buffer; }

  void ClearRenderedTextCache();

  double GetCalculatedSafetyContour(void) { return m_next_safe_cnt; }

  virtual bool RenderRegionViewOnGL(const wxGLContext &glc,
                                    const ViewPort &VPoint,
                                    const OCPNRegion &RectRegion,
                                    const LLRegion &Region);
  virtual bool RenderOverlayRegionViewOnGL(const wxGLContext &glc,
                                           const ViewPort &VPoint,
                                           const OCPNRegion &RectRegion,
                                           const LLRegion &Region);
  virtual bool RenderRegionViewOnGLNoText(const wxGLContext &glc,
                                          const ViewPort &VPoint,
                                          const OCPNRegion &RectRegion,
                                          const LLRegion &Region);
  virtual bool RenderViewOnGLTextOnly(const wxGLContext &glc,
                                      const ViewPort &VPoint);

  // Public data
  // Todo Accessors here
  //  Object arrays used by S52PLIB TOPMAR rendering logic
  wxArrayPtrVoid *pFloatingATONArray;
  wxArrayPtrVoid *pRigidATONArray;

  double ref_lat, ref_lon;  // Common reference point, derived from FullExtent
  double m_LOD_meters;
  Extent m_FullExtent;
  bool m_bExtentSet;
  bool m_bLinePrioritySet;

  //  SM Projection parms, stored as convenience to expedite pixel conversions
  double m_easting_vp_center, m_northing_vp_center;
  double m_pixx_vp_center, m_pixy_vp_center;
  double m_view_scale_ppm;

  //    Last ViewPort succesfully rendered, stored as an aid to calculating
  //    pixel cache address offsets and regions
  ViewPort m_last_vp;
  OCPNRegion m_last_Region;

  virtual bool IsCacheValid() { return (pDIB != nullptr); }
  virtual void InvalidateCache();
  virtual bool RenderViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint);

  virtual void ClearDepthContourArray(void);
  virtual void BuildDepthContourArray(void);
  int ValidateAndCountUpdates(const wxFileName file000, const wxString CopyDir,
                              wxString &LastUpdateDate, bool b_copyfiles);
  static int GetUpdateFileArray(const wxFileName file000,
                                wxArrayString *UpFiles, wxDateTime date000,
                                wxString edtn000);
  wxString GetISDT(void);
  InitReturn PostInit(ChartInitFlag flags, ColorScheme cs);

  char GetUsageChar(void) { return m_usage_char; }
  static bool IsCellOverlayType(const wxString &pFullPath);

  bool m_b2pointLUPS;
  bool m_b2lineLUPS;
  bool m_RAZBuilt;

  chart_context *m_this_chart_context;

  int FindOrCreateSenc(const wxString &name, bool b_progress = true);
  void DisableBackgroundSENC() { m_disableBackgroundSENC = true; }
  void EnableBackgroundSENC() { m_disableBackgroundSENC = false; }

  SENCThreadStatus m_SENCthreadStatus;

protected:
  void AssembleLineGeometry(void);

  ObjRazRules *razRules[PRIO_NUM][LUPNAME_NUM];
  double m_next_safe_cnt;

private:
  int GetLineFeaturePointArray(S57Obj *obj, void **ret_array);
  void SetSafetyContour(void);

  bool DoRenderViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint,
                        RenderTypeEnum option, bool force_new_view);

  bool DoRenderRegionViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint,
                              const OCPNRegion &Region, bool b_overlay);

  int DCRenderRect(wxMemoryDC &dcinput, const ViewPort &vp, wxRect *rect);
  bool DCRenderLPB(wxMemoryDC &dcinput, const ViewPort &vp, wxRect *rect);
  bool DCRenderText(wxMemoryDC &dcinput, const ViewPort &vp);

  int BuildSENCFile(const wxString &FullPath000, const wxString &SENCFileName,
                    bool b_progress = true);

  void SetLinePriorities(void);

  bool BuildThumbnail(const wxString &bmpname);
  bool CreateHeaderDataFromENC(void);
  bool CreateHeaderDataFromSENC(void);
  bool CreateHeaderDataFromoSENC(void);
  bool GetBaseFileAttr(const wxString &file000);

  void ResetPointBBoxes(const ViewPort &vp_last, const ViewPort &vp_this);

  //    Access to raw ENC DataSet
  bool InitENCMinimal(const wxString &FullPath);
  int GetENCScale();
  OGRFeature *GetChartFirstM_COVR(int &catcov);
  OGRFeature *GetChartNextM_COVR(int &catcov);

  void FreeObjectsAndRules();
  const char *getName(OGRFeature *feature);

  bool DoRenderOnGL(const wxGLContext &glc, const ViewPort &VPoint);
  bool DoRenderOnGLText(const wxGLContext &glc, const ViewPort &VPoint);
  bool DoRenderRegionViewOnGL(const wxGLContext &glc, const ViewPort &VPoint,
                              const OCPNRegion &RectRegion,
                              const LLRegion &Region, bool b_overlay);

  void BuildLineVBO(void);

  void ChangeThumbColor(ColorScheme cs);
  void LoadThumb();
  bool s57_ProcessExtendedLightSectors(ChartCanvas *cc,
                                       ChartPlugInWrapper *target_plugin_chart,
                                       s57chart *Chs57,
                                       ListOfObjRazRules *rule_list,
                                       std::list<S57Obj*> *pi_rule_list,
                                       std::vector<s57Sector_t> &sectorlegs);
  void CreateChartContext();
  void PopulateObjectsWithContext();

  // Private Data
  char *hdr_buf;
  char *mybuf_ptr;
  int hdr_len;
  wxString m_SENCFileName;

  wxArrayString *m_tmpup_array;
  PixelCache *pDIB;

  wxBitmap *m_pCloneBM;
  wxMask *m_pMask;

  bool bGLUWarningSent;

  wxBitmap *m_pDIBThumbDay;
  wxBitmap *m_pDIBThumbDim;
  wxBitmap *m_pDIBThumbOrphan;
  bool m_bneed_new_thumbnail;

  bool m_bbase_file_attr_known;
  wxDateTime m_date000;  // extracted from DSID:ISDT
  wxString m_edtn000;    // extracted from DSID:EDTN
  int m_nGeoRecords;     // extracted from DSSI:NOGR
  int m_native_scale;    // extracted from DSPM:CSCL

  //  Raw ENC DataSet members
  OGRS57DataSource *m_pENCDS;

  //  DEPCNT VALDCO array members
  int m_nvaldco;
  int m_nvaldco_alloc;
  double *m_pvaldco_array;

  float *m_line_vertex_buffer;
  size_t m_vbo_byte_length;

  bool m_blastS57TextRender;
  wxString m_lastColorScheme;
  wxRect m_last_vprect;
  long m_plib_state_hash;
  bool m_btex_mem;
  char m_usage_char;


  int m_LineVBO_name;

  std::unordered_map<unsigned, VE_Element *> m_ve_hash;
  std::unordered_map<unsigned, VC_Element *> m_vc_hash;
  std::vector<connector_segment *> m_pcs_vector;
  std::vector<VE_Element *> m_pve_vector;

  wxString m_TempFilePath;
  bool m_disableBackgroundSENC;

protected:
  sm_parms vp_transform;
};

#endif
