/**************************************************************************
 *   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, see <https://www.gnu.org/licenses/>. *
 **************************************************************************/

/**
 * \file
 *
 * S57 Chart Object
 */

#ifndef S57CHART_H_
#define S57CHART_H_

#include <memory>
#include <unordered_map>

#include <wx/wx.h>
#include <wx/dir.h>
#include <wx/dynarray.h>
#include <wx/filename.h>
#include <wx/stream.h>
#include <wx/wfstream.h>

#include "gdal/ogrsf_frmts.h"

#include "model/gui_vars.h"

#include "chartbase.h"  // ChartBase
#include "chartimg.h"
#include "ocpn_pixel.h"
#include "ocpn_region.h"
#include "ogr_s57.h"
#include "s52s57.h"  // ObjRazRules
#include "s57.h"
#include "s57_light.h"
#include "s57_object_desc.h"
#include "s57_sector.h"
#include "viewport.h"

extern bool chain_broken_mssage_shown; /**< Global instance */

class ChartCanvas;  // circular

enum {
  BUILD_SENC_OK,
  BUILD_SENC_NOK_RETRY,
  BUILD_SENC_NOK_PERMANENT,
  BUILD_SENC_PENDING
};

WX_DECLARE_OBJARRAY(S57Obj, ArrayOfS57Obj);

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);

/**
 * Represents an S57 format electronic navigational chart in OpenCPN.
 *
 * S57 is an international standard for encoding and exchanging digital
 * hydrographic data. These vector charts contain maritime information including
 * depths, buoys, lights, and other navigational features.
 *
 * Key features of S57 charts and this class:
 * - Vector data: Allows for smooth scaling and rotation without loss of quality
 * - Rich feature set: Includes detailed information about various maritime
 * objects
 * - Layered display: Supports showing/hiding different types of information
 * - SENC support: Can create and use System ENC (SENC) files for faster loading
 * - Render options: Supports various rendering modes including OpenGL
 * - Object queries: Allows for detailed queries of chart objects
 */
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() {
    return m_ve_hash;
  }
  virtual std::unordered_map<unsigned, VC_Element *> &Get_vc_hash() {
    return m_vc_hash;
  }

  virtual void ForceEdgePriorityEvaluate();

  float *GetLineVertexBuffer() { return m_line_vertex_buffer; }

  void ClearRenderedTextCache();

  double GetCalculatedSafetyContour() { 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();
  virtual void BuildDepthContourArray();
  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();
  InitReturn PostInit(ChartInitFlag flags, ColorScheme cs);

  char GetUsageChar() { 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; }

protected:
  void AssembleLineGeometry();

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

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

  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();

  bool BuildThumbnail(const wxString &bmpname);
  bool CreateHeaderDataFromENC();
  bool CreateHeaderDataFromSENC();
  bool CreateHeaderDataFromoSENC();
  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 ChangeThumbColor(ColorScheme cs);
  void LoadThumb();
  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  // S57CHART_H_
