/**************************************************************************
 *   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
 *
 * BSB chart management
 */

#ifndef _CHARTIMG_H_
#define _CHARTIMG_H_

#include "model/georef.h"  // for GeoRef type

#include "chartbase.h"
#include "chartdb.h"
#include "ocpn_region.h"
#include "viewport.h"

typedef enum ScaleTypeEnum {
  RENDER_LODEF = 0,
  RENDER_HIDEF,
} _ScaleTypeEnum;

class WXDLLEXPORT ChartImg;

//-----------------------------------------------------------------------------
//    Constants, etc.
//-----------------------------------------------------------------------------

typedef enum PaletteDir { PaletteFwd, PaletteRev } _PaletteDir;

typedef enum BSB_Color_Capability {
  COLOR_RGB_DEFAULT = 0,  // Default corresponds to bsb entries "RGB"
  DAY,
  DUSK,
  NIGHT,
  NIGHTRED,
  GRAY,
  PRC,
  PRG,
  N_BSB_COLORS
} _BSB_Color_Capability;

//-----------------------------------------------------------------------------
//    Fwd Refs
//-----------------------------------------------------------------------------

class ChartKAP;
class ViewPort;
class PixelCache;
class ocpnBitmap;

class wxFFileInputStream;

//-----------------------------------------------------------------------------
//    Helper classes
//-----------------------------------------------------------------------------

class Refpoint {
public:
  int bXValid;
  int bYValid;
  float xr;
  float yr;
  float latr;
  float lonr;
  float xpl_error;
  float xlp_error;
  float ypl_error;
  float ylp_error;
};

struct TileOffsetCache {
  int offset;  // offset from start of line pointer
  int pixel;   // offset from current pixel
};

class CachedLine {
public:
  unsigned char *pPix;
  TileOffsetCache *pTileOffset;  // entries for random access

  bool bValid;
  int size;
};

class opncpnPalette {
public:
  opncpnPalette();
  ~opncpnPalette();

  int *FwdPalette;
  int *RevPalette;
  int nFwd;
  int nRev;
};

/**
 * Base class for BSB (Maptech/NOS) format nautical charts.
 *
 * Provides core functionality for handling BSB format raster nautical charts.
 * Implements chart rendering, coordinate transformations, and utility
 * functions.
 *
 * Key features include:
 * - Chart initialization and loading
 * - Rendering of chart views on different devices (DC, OpenGL)
 * - Coordinate conversions between lat/lon and chart pixels
 * - Color scheme management
 * - Chart scaling and zooming
 * - Caching mechanisms for improved performance
 */
class ChartBaseBSB : public ChartBase {
public:
  //    Public methods

  ChartBaseBSB();
  virtual ~ChartBaseBSB();
  void FreeLineCacheRows(int start = 0, int end = -1);
  bool HaveLineCacheRow(int row);

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

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

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

  virtual int latlong_to_pix_vp(double lat, double lon, double &pixx,
                                double &pixy, ViewPort &vp);
  virtual int vp_pix_to_latlong(ViewPort &vp, double pixx, double pixy,
                                double *lat, double *lon);

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

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

  virtual bool AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed);
  /**
   * Find the nearest preferred viewport scale (in pixels/meter) for this chart.
   *
   * The function aims to find a scale within 5% of the requested scale while
   * staying between 1/0.01x and 64x of the chart's native scale.
   *
   * @param target_scale_ppm Desired viewport scale in physical pixels per
   * meter.
   * @return Closest preferred scale in physical pixels per meter
   */
  virtual double GetNearestPreferredScalePPM(double target_scale_ppm);

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

  virtual bool GetChartExtent(Extent *pext);

  void SetColorScheme(ColorScheme cs, bool bApplyImmediate);

  wxImage *GetImage();

  void SetVPRasterParms(const ViewPort &vpt);

  virtual void ComputeSourceRectangle(const ViewPort &vp, wxRect *pSourceRect);
  virtual double GetRasterScaleFactor(const ViewPort &vp);
  virtual bool GetChartBits(wxRect &source, unsigned char *pPix, int sub_samp);
  virtual int GetSize_X() { return Size_X; }
  virtual int GetSize_Y() { return Size_Y; }

  virtual void latlong_to_chartpix(double lat, double lon, double &pixx,
                                   double &pixy);
  virtual void chartpix_to_latlong(double pixx, double pixy, double *plat,
                                   double *plon);

  double GetPPM() { return m_ppm_avg; }

protected:
  //    Methods

  wxRect GetSourceRect() { return Rsrc; }

  virtual bool GetAndScaleData(unsigned char *ppn, size_t data_size,
                               wxRect &source, int source_stride, wxRect &dest,
                               int dest_stride, double scale_factor,
                               ScaleTypeEnum scale_type);
  bool RenderViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint);

  bool IsCacheValid() { return cached_image_ok; }
  void InvalidateCache() { cached_image_ok = 0; }
  bool IsRenderCacheable(wxRect &source, wxRect &dest);

  void CreatePaletteEntry(char *buffer, int palette_index);
  PaletteDir GetPaletteDir(void);
  int *GetPalettePtr(BSB_Color_Capability);

  /**
   * Find closest valid scale that's a power of 2 multiple of chart's native
   * scale.
   *
   * @param target_scale Target scale in physical pixels per meter.
   * @param scale_factor_min Minimum allowed scale multiplier (e.g., 0.01 allows
   * zoom in to 100x).
   * @param scale_factor_max Maximum allowed scale multiplier (e.g., 64 allows
   * zoom out to 1/64x).
   * @return Valid scale in physical pixels per meter.
   */
  double GetClosestValidNaturalScalePPM(double target_scale,
                                        double scale_factor_min,
                                        double scale_factor_max);

  virtual void InvalidateLineCache();
  virtual bool CreateLineIndex(void);

  virtual wxBitmap *CreateThumbnail(int tnx, int tny, ColorScheme cs);
  virtual int BSBGetScanline(unsigned char *pLineBuf, int y, int xs, int xl,
                             int sub_samp);

  bool GetViewUsingCache(wxRect &source, wxRect &dest, const OCPNRegion &Region,
                         ScaleTypeEnum scale_type);
  bool GetView(wxRect &source, wxRect &dest, ScaleTypeEnum scale_type);

  virtual int BSBScanScanline(wxInputStream *pinStream);
  virtual int ReadBSBHdrLine(wxInputStream *, char *, int);
  virtual int AnalyzeRefpoints(bool b_testSolution = true);
  virtual bool AnalyzeSkew(void);

  virtual bool SetMinMax(void);

  InitReturn PreInit(const wxString &name, ChartInitFlag init_flags,
                     ColorScheme cs);
  InitReturn PostInit(void);

  double AdjustLongitude(double lon);

  //    Protected Data
  PixelCache *pPixCache;

  int Size_X;  // Chart native pixel dimensions
  int Size_Y;
  int m_Chart_DU;
  double m_cph;
  double m_proj_parameter;  // Mercator:               Projection Latitude
                            // Transverse Mercator:    Central Meridian
  double m_dx;              // Pixel scale factors, from KAP header
  double m_dy;

  wxString m_bsb_ver;
  bool m_b_SHOM;
  bool m_b_apply_dtm;

  int m_datum_index;
  double m_dtm_lat;
  double m_dtm_lon;

  wxRect cache_rect;
  wxRect cache_rect_scaled;
  bool cached_image_ok;
  ScaleTypeEnum cache_scale_method;
  double m_cached_scale_ppm;
  wxRect m_last_vprect;

  wxRect Rsrc;  // Current chart source rectangle
  double m_raster_scale_factor;

  int nRefpoint;
  Refpoint *pRefTable;

  int nColorSize;
  int *pline_table;  // pointer to Line offset table

  CachedLine *pLineCache;

  wxInputStream *ifs_hdr;
  wxInputStream *ifss_bitmap;
  wxBufferedInputStream *ifs_bitmap;

  wxString *pBitmapFilePath;

  unsigned char *ifs_buf;
  unsigned char *ifs_bufend;
  int ifs_bufsize;
  unsigned char *ifs_lp;
  int ifs_file_offset;
  int nFileOffsetDataStart;
  int m_nLineOffset;

  GeoRef cPoints;

  double wpx[12], wpy[12], pwx[12], pwy[12];  // Embedded georef coefficients
  int wpx_type, wpy_type, pwx_type, pwy_type;
  int n_wpx, n_wpy, n_pwx, n_pwy;
  bool bHaveEmbeddedGeoref;

  opncpnPalette *pPalettes[N_BSB_COLORS];

  BSB_Color_Capability m_mapped_color_index;

  //    Integer digital scale value above which bilinear scaling is not allowed,
  //      and subsampled scaling must be performed
  int m_bilinear_limit;

  bool bUseLineCache;

  float m_LonMax;
  float m_LonMin;
  float m_LatMax;
  float m_LatMin;

  int *pPalette;
  PaletteDir palette_direction;

  bool bGeoErrorSent;

  double m_ppm_avg;  // Calculated true scale factor of the 1X chart,
                     // pixels per meter
  OCPNRegion m_last_region;

  int m_b_cdebug;

  double m_proj_lat, m_proj_lon;

  ViewPort m_vp_render_last;

  wxCriticalSection m_critSect;
  wxULongLong m_filesize;
};

/**
 * Represents a KAP format chart, derived from ChartBaseBSB.
 */
class ChartKAP : public ChartBaseBSB {
public:
  //    Methods
  ChartKAP();
  ~ChartKAP();

  InitReturn Init(const wxString &name, ChartInitFlag init_flags);
};

// ----------------------------------------------------------------------------
// ChartGEO
// ----------------------------------------------------------------------------

class ChartGEO : public ChartBaseBSB {
public:
  //    Methods
  ChartGEO();
  ~ChartGEO();

  InitReturn Init(const wxString &name, ChartInitFlag init_flags);
};

// ----------------------------------------------------------------------------
// ChartPlugInWrapper
//    This class is a wrapper/interface to PlugIn charts(PlugInChartBase) as
//    defined in ocpn_plugin.h
// ----------------------------------------------------------------------------

class PlugInChartBase;  // found in ocpn_plugin.h

/**
 * Wrapper class for plugin-based charts.
 * Serves as an interface between the main application and plugin-provided
 * charts. Allows seamless integration of charts from external plugins into the
 * main chart handling system.
 */
class ChartPlugInWrapper : public ChartBaseBSB {
public:
  ChartPlugInWrapper();
  ChartPlugInWrapper(const wxString &chart_class);
  virtual ~ChartPlugInWrapper();

  virtual wxString GetFileSearchMask(void);

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

  //    Accessors
  virtual ThumbData *GetThumbData(int tnx, int tny, float lat, float lon);
  virtual ThumbData *GetThumbData();
  virtual bool UpdateThumbData(double lat, double lon);

  double GetNormalScaleMin(double canvas_scale_factor, bool b_allow_overzoom);
  double GetNormalScaleMax(double canvas_scale_factor, int canvas_width);

  virtual bool GetChartExtent(Extent *pext);

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

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

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

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

  virtual void SetColorScheme(ColorScheme cs, bool bApplyImmediate);

  virtual double GetNearestPreferredScalePPM(double target_scale_ppm);

  virtual PlugInChartBase *GetPlugInChart(void) { return m_ppicb; }

  virtual int GetCOVREntries();
  virtual int GetCOVRTablePoints(int iTable);
  virtual int GetCOVRTablenPoints(int iTable);
  virtual float *GetCOVRTableHead(int iTable);

  virtual int GetNoCOVREntries();
  virtual int GetNoCOVRTablePoints(int iTable);
  virtual int GetNoCOVRTablenPoints(int iTable);
  virtual float *GetNoCOVRTableHead(int iTable);

  //    The following set of methods apply to BSB (i.e. Raster) type PlugIn
  //    charts only and need not be implemented if the ChartFamily is not
  //    CHART_FAMILY_RASTER
  virtual void ComputeSourceRectangle(const ViewPort &vp, wxRect *pSourceRect);
  virtual double GetRasterScaleFactor(const ViewPort &vp);
  virtual bool GetChartBits(wxRect &source, unsigned char *pPix, int sub_samp);
  virtual int GetSize_X();
  virtual int GetSize_Y();
  virtual void latlong_to_chartpix(double lat, double lon, double &pixx,
                                   double &pixy);
  virtual void chartpix_to_latlong(double pixx, double pixy, double *plat,
                                   double *plon);

  //  Added for API V 1.14, with PlugInChartBaseExtended
  virtual bool RenderRegionViewOnDCNoText(wxMemoryDC &dc,
                                          const ViewPort &VPoint,
                                          const OCPNRegion &Region);

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

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

  virtual bool RenderRegionViewOnGLTextOnly(const wxGLContext &glc,
                                            const ViewPort &VPoint,
                                            const OCPNRegion &RectRegion);

  virtual void ClearPLIBTextList();

private:
  PlugInChartBase *m_ppicb;
  wxObject *m_ppo;
  wxCriticalSection m_critSect;
  bool m_overlayENC;
  wxMask *m_pMask;
};

#endif
