/**********************************************************************
 * $Id: mitab.h ad25ca25caa01bbc7d125b216c1119939330bc90 2018-12-04 22:17:28 +0300 drons $
 *
 * Name:     mitab.h
 * Project:  MapInfo TAB Read/Write library
 * Language: C++
 * Purpose:  Header file containing public definitions for the library.
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
 *
 **********************************************************************
 * Copyright (c) 1999-2005, Daniel Morissette
 * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#ifndef MITAB_H_INCLUDED_
#define MITAB_H_INCLUDED_

#include "mitab_priv.h"
#include "ogr_feature.h"
#include "ogr_featurestyle.h"
#include "ogrsf_frmts.h"

#include <set>

/*---------------------------------------------------------------------
 * Current version of the MITAB library... always useful!
 *--------------------------------------------------------------------*/
#define MITAB_VERSION      "2.0.0-dev (2008-10)"
#define MITAB_VERSION_INT  2000000  /* version x.y.z -> xxxyyyzzz */

#ifndef ROUND_INT
#  define ROUND_INT(dX) static_cast<int>((dX) < 0.0 ? (dX)-0.5 : (dX)+0.5 )
#endif

#define MITAB_AREA(x1, y1, x2, y2)  ((static_cast<double>(x2)-(x1))*(static_cast<double>(y2)-(y1)))

class TABFeature;

/*---------------------------------------------------------------------
 * Codes for the GetFileClass() in the IMapInfoFile-derived  classes
 *--------------------------------------------------------------------*/
typedef enum
{
    TABFC_IMapInfoFile = 0,
    TABFC_TABFile,
    TABFC_TABView,
    TABFC_TABSeamless,
    TABFC_MIFFile
} TABFileClass;

/*---------------------------------------------------------------------
 *                      class IMapInfoFile
 *
 * Virtual base class for the TABFile and MIFFile classes.
 *
 * This is the definition of the public interface methods that should
 * be available for any type of MapInfo dataset.
 *--------------------------------------------------------------------*/

class IMapInfoFile : public OGRLayer
{
    CPL_DISALLOW_COPY_ASSIGN(IMapInfoFile)

  protected:
    GIntBig             m_nCurFeatureId;
    TABFeature         *m_poCurFeature;
    GBool               m_bBoundsSet;

    char                *m_pszCharset;

    TABFeature*         CreateTABFeature(OGRFeature *poFeature);

  public:
    IMapInfoFile() ;
    virtual ~IMapInfoFile();

    virtual TABFileClass GetFileClass() {return TABFC_IMapInfoFile;}

    virtual int Open(const char *pszFname, const char* pszAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr );

    virtual int Open(const char *pszFname, TABAccess eAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr ) = 0;
    virtual int Close() = 0;

    virtual int SetQuickSpatialIndexMode(CPL_UNUSED GBool bQuickSpatialIndexMode=TRUE) {return -1;}

    virtual const char *GetTableName() = 0;

    ///////////////
    // Static method to detect file type, create an object to read that
    // file and open it.
    static IMapInfoFile *SmartOpen(const char *pszFname,
                                   GBool bUpdate = FALSE,
                                   GBool bTestOpenNoError = FALSE);

    ///////////////
    //  OGR methods for read support
    virtual void        ResetReading() override = 0;
    virtual GIntBig     GetFeatureCount (int bForce) override = 0;
    virtual OGRFeature *GetNextFeature() override;
    virtual OGRFeature *GetFeature(GIntBig nFeatureId) override;
    virtual OGRErr      ICreateFeature(OGRFeature *poFeature) override;
    virtual int         TestCapability( const char * pszCap ) override =0;
    virtual OGRErr      GetExtent(OGREnvelope *psExtent, int bForce) override =0;
    virtual OGRErr      GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) override
                { return OGRLayer::GetExtent(iGeomField, psExtent, bForce); }

    ///////////////
    // Read access specific stuff
    //
    virtual GIntBig GetNextFeatureId(GIntBig nPrevId) = 0;
    virtual TABFeature *GetFeatureRef(GIntBig nFeatureId) = 0;
    virtual OGRFeatureDefn *GetLayerDefn() override = 0;

    virtual TABFieldType GetNativeFieldType(int nFieldId) = 0;

    virtual int GetBounds(double &dXMin, double &dYMin,
                          double &dXMax, double &dYMax,
                          GBool bForce = TRUE ) = 0;

    virtual OGRSpatialReference *GetSpatialRef() override = 0;

    virtual int GetFeatureCountByType(int &numPoints, int &numLines,
                                      int &numRegions, int &numTexts,
                                      GBool bForce = TRUE ) = 0;

    virtual GBool IsFieldIndexed(int nFieldId) = 0;
    virtual GBool IsFieldUnique(int nFieldId) = 0;

    ///////////////
    // Write access specific stuff
    //
    GBool       IsBoundsSet()            {return m_bBoundsSet;}
    virtual int SetBounds(double dXMin, double dYMin,
                          double dXMax, double dYMax) = 0;
    virtual int SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
                            TABFieldType *paeMapInfoNativeFieldTypes = nullptr)=0;
    virtual int AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
                               int nWidth=0, int nPrecision=0,
                               GBool bIndexed=FALSE, GBool bUnique=FALSE,
                               int bApproxOK = TRUE) = 0;
    virtual OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK = TRUE ) override;

    virtual int SetSpatialRef(OGRSpatialReference *poSpatialRef) = 0;

    virtual OGRErr CreateFeature(TABFeature *poFeature) = 0;

    virtual int SetFieldIndexed(int nFieldId) = 0;

    virtual int SetCharset(const char* charset);

    virtual const char* GetCharset() const;

    static const char* CharsetToEncoding( const char* );
    static const char* EncodingToCharset( const char* );

    void SetEncoding( const char* );
    const char* GetEncoding() const;
    int TestUtf8Capability() const;
    ///////////////
    // semi-private.
    virtual int  GetProjInfo(TABProjInfo *poPI) = 0;
    virtual int  SetProjInfo(TABProjInfo *poPI) = 0;
    virtual int  SetMIFCoordSys(const char *pszMIFCoordSys) = 0;

    static int GetTABType( OGRFieldDefn *poField, TABFieldType* peTABType,
                           int *pnWidth, int *pnPrecision);

#ifdef DEBUG
    virtual void Dump(FILE *fpOut = nullptr) = 0;
#endif
};

/*---------------------------------------------------------------------
 *                      class TABFile
 *
 * The main class for TAB datasets.  External programs should use this
 * class to open a TAB dataset and read/write features from/to it.
 *
 *--------------------------------------------------------------------*/
class TABFile final : public IMapInfoFile
{
    CPL_DISALLOW_COPY_ASSIGN(TABFile)

  private:
    char        *m_pszFname;
    TABAccess   m_eAccessMode;
    char        **m_papszTABFile;
    int         m_nVersion;
    int         *m_panIndexNo;
    TABTableType m_eTableType;  // NATIVE (.DAT) or DBF

    TABDATFile  *m_poDATFile;   // Attributes file
    TABMAPFile  *m_poMAPFile;   // Object Geometry file
    TABINDFile  *m_poINDFile;   // Attributes index file

    OGRFeatureDefn *m_poDefn;
    std::set<CPLString> m_oSetFields{};
    OGRSpatialReference *m_poSpatialRef;
    int         bUseSpatialTraversal;

    int         m_nLastFeatureId;

    GIntBig    *m_panMatchingFIDs;
    int         m_iMatchingFID;

    int         m_bNeedTABRewrite;

    int         m_bLastOpWasRead;
    int         m_bLastOpWasWrite;
    ///////////////
    // Private Read access specific stuff
    //
    int         ParseTABFileFirstPass(GBool bTestOpenNoError);
    int         ParseTABFileFields();

     ///////////////
    // Private Write access specific stuff
    //
    int         WriteTABFile();

  public:
    TABFile();
    virtual ~TABFile();

    virtual TABFileClass GetFileClass() override {return TABFC_TABFile;}

    virtual int Open(const char *pszFname, const char* pszAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr ) override
            { return IMapInfoFile::Open(pszFname, pszAccess, bTestOpenNoError, pszCharset); }
    virtual int Open(const char *pszFname, TABAccess eAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr ) override
            { return Open(pszFname, eAccess, bTestOpenNoError, 512, pszCharset); }

    virtual int Open(const char *pszFname, TABAccess eAccess,
                     GBool bTestOpenNoError,
                     int nBlockSizeForCreate,
                     const char* pszCharset );

    virtual int Close() override;

    virtual int SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode=TRUE) override;

    virtual const char *GetTableName() override
                            {return m_poDefn?m_poDefn->GetName():"";}

    virtual void        ResetReading() override;
    virtual int         TestCapability( const char * pszCap ) override;
    virtual GIntBig     GetFeatureCount (int bForce) override;
    virtual OGRErr      GetExtent(OGREnvelope *psExtent, int bForce) override;
    virtual OGRErr      GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) override
                { return OGRLayer::GetExtent(iGeomField, psExtent, bForce); }

    /* Implement OGRLayer's SetFeature() for random write, only with TABFile */
    virtual OGRErr      ISetFeature( OGRFeature * ) override;
    virtual OGRErr      DeleteFeature(GIntBig nFeatureId) override;

    virtual OGRErr      DeleteField( int iField ) override;
    virtual OGRErr      ReorderFields( int* panMap ) override;
    virtual OGRErr      AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) override;

    virtual OGRErr      SyncToDisk() override;

    ///////////////
    // Read access specific stuff
    //

    int         GetNextFeatureId_Spatial( int nPrevId );

    virtual GIntBig GetNextFeatureId(GIntBig nPrevId) override;
    virtual TABFeature *GetFeatureRef(GIntBig nFeatureId) override;
    virtual OGRFeatureDefn *GetLayerDefn() override;

    virtual TABFieldType GetNativeFieldType(int nFieldId) override;

    virtual int GetBounds(double &dXMin, double &dYMin,
                          double &dXMax, double &dYMax,
                          GBool bForce = TRUE ) override;

    virtual OGRSpatialReference *GetSpatialRef() override;

    static OGRSpatialReference* GetSpatialRefFromTABProj(const TABProjInfo& sTABProj);
    static int                  GetTABProjFromSpatialRef(const OGRSpatialReference* poSpatialRef,
                                                         TABProjInfo& sTABProj, int& nParmCount);

    virtual int GetFeatureCountByType(int &numPoints, int &numLines,
                                      int &numRegions, int &numTexts,
                                      GBool bForce = TRUE) override;

    virtual GBool IsFieldIndexed(int nFieldId) override;
    virtual GBool IsFieldUnique(int /*nFieldId*/) override   {return FALSE;}

    virtual int GetVersion() { return m_nVersion; }

    ///////////////
    // Write access specific stuff
    //
    virtual int SetBounds(double dXMin, double dYMin,
                          double dXMax, double dYMax) override;
    virtual int SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
                            TABFieldType *paeMapInfoNativeFieldTypes = nullptr) override;
    virtual int AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
                               int nWidth=0, int nPrecision=0,
                               GBool bIndexed=FALSE, GBool bUnique=FALSE,
                               int bApproxOK = TRUE) override;
    virtual int SetSpatialRef(OGRSpatialReference *poSpatialRef) override;

    virtual OGRErr CreateFeature(TABFeature *poFeature) override;

    virtual int SetFieldIndexed(int nFieldId) override;

    ///////////////
    // semi-private.
    virtual int  GetProjInfo(TABProjInfo *poPI) override
            { return m_poMAPFile->GetHeaderBlock()->GetProjInfo( poPI ); }
    virtual int  SetProjInfo(TABProjInfo *poPI) override;
    virtual int  SetMIFCoordSys(const char *pszMIFCoordSys) override;

    int         GetFieldIndexNumber(int nFieldId);
    TABINDFile  *GetINDFileRef();

    TABMAPFile  *GetMAPFileRef() { return m_poMAPFile; }

    int         WriteFeature(TABFeature *poFeature);
    virtual int SetCharset(const char* pszCharset) override;
#ifdef DEBUG
    virtual void Dump(FILE *fpOut = nullptr) override;
#endif
};

/*---------------------------------------------------------------------
 *                      class TABView
 *
 * TABView is used to handle special type of .TAB files that are
 * composed of a number of .TAB datasets linked through some indexed
 * fields.
 *
 * NOTE: The current implementation supports only TABViews composed
 *       of 2 TABFiles linked through an indexed field of integer type.
 *       It is unclear if any other type of views could exist anyways.
 *--------------------------------------------------------------------*/
class TABView final : public IMapInfoFile
{
    CPL_DISALLOW_COPY_ASSIGN(TABView)

  private:
    char        *m_pszFname;
    TABAccess   m_eAccessMode;
    char        **m_papszTABFile;
    char        *m_pszVersion;

    char        **m_papszTABFnames;
    TABFile     **m_papoTABFiles;
    int         m_numTABFiles;
    int         m_nMainTableIndex; // The main table is the one that also
                                   // contains the geometries
    char        **m_papszFieldNames;
    char        **m_papszWhereClause;

    TABRelation *m_poRelation;
    GBool       m_bRelFieldsCreated;

    ///////////////
    // Private Read access specific stuff
    //
    int         ParseTABFile(const char *pszDatasetPath,
                             GBool bTestOpenNoError = FALSE);

    int         OpenForRead(const char *pszFname,
                            GBool bTestOpenNoError = FALSE );

    ///////////////
    // Private Write access specific stuff
    //
    int         OpenForWrite(const char *pszFname );
    int         WriteTABFile();

  public:
    TABView();
    virtual ~TABView();

    virtual TABFileClass GetFileClass() override {return TABFC_TABView;}

    virtual int Open(const char *pszFname, const char* pszAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr ) override { return IMapInfoFile::Open(pszFname, pszAccess, bTestOpenNoError, pszCharset); }
    virtual int Open(const char *pszFname, TABAccess eAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr ) override;
    virtual int Close() override;

    virtual int SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode=TRUE) override;

    virtual const char *GetTableName() override
           {return m_poRelation?m_poRelation->GetFeatureDefn()->GetName():"";}

    virtual void        ResetReading() override;
    virtual int         TestCapability( const char * pszCap ) override;
    virtual GIntBig     GetFeatureCount (int bForce) override;
    virtual OGRErr      GetExtent(OGREnvelope *psExtent, int bForce) override;
    virtual OGRErr      GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) override
                { return OGRLayer::GetExtent(iGeomField, psExtent, bForce); }

    ///////////////
    // Read access specific stuff
    //

    virtual GIntBig GetNextFeatureId(GIntBig nPrevId) override;
    virtual TABFeature *GetFeatureRef(GIntBig nFeatureId) override;
    virtual OGRFeatureDefn *GetLayerDefn() override;

    virtual TABFieldType GetNativeFieldType(int nFieldId) override;

    virtual int GetBounds(double &dXMin, double &dYMin,
                          double &dXMax, double &dYMax,
                          GBool bForce = TRUE ) override;

    virtual OGRSpatialReference *GetSpatialRef() override;

    virtual int GetFeatureCountByType(int &numPoints, int &numLines,
                                      int &numRegions, int &numTexts,
                                      GBool bForce = TRUE) override;

    virtual GBool IsFieldIndexed(int nFieldId) override;
    virtual GBool IsFieldUnique(int nFieldId) override;

    ///////////////
    // Write access specific stuff
    //
    virtual int SetBounds(double dXMin, double dYMin,
                          double dXMax, double dYMax) override;
    virtual int SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
                           TABFieldType *paeMapInfoNativeFieldTypes=nullptr) override;
    virtual int AddFieldNative(const char *pszName,
                               TABFieldType eMapInfoType,
                               int nWidth=0, int nPrecision=0,
                               GBool bIndexed=FALSE, GBool bUnique=FALSE,
                               int bApproxOK = TRUE) override;
    virtual int SetSpatialRef(OGRSpatialReference *poSpatialRef) override;

    virtual OGRErr CreateFeature(TABFeature *poFeature) override;

    virtual int SetFieldIndexed(int nFieldId) override;

    ///////////////
    // semi-private.
    virtual int  GetProjInfo(TABProjInfo *poPI) override
            { return m_nMainTableIndex!=-1?
                     m_papoTABFiles[m_nMainTableIndex]->GetProjInfo(poPI):-1; }
    virtual int  SetProjInfo(TABProjInfo *poPI) override
            { return m_nMainTableIndex!=-1?
                     m_papoTABFiles[m_nMainTableIndex]->SetProjInfo(poPI):-1; }
    virtual int  SetMIFCoordSys(const char * /*pszMIFCoordSys*/) override {return -1;}
    virtual int SetCharset(const char* pszCharset) override;

#ifdef DEBUG
    virtual void Dump(FILE *fpOut = nullptr) override;
#endif
};

/*---------------------------------------------------------------------
 *                      class TABSeamless
 *
 * TABSeamless is used to handle seamless .TAB files that are
 * composed of a main .TAB file in which each feature is the MBR of
 * a base table.
 *
 * TABSeamless are supported for read access only.
 *--------------------------------------------------------------------*/
class TABSeamless final : public IMapInfoFile
{
    CPL_DISALLOW_COPY_ASSIGN(TABSeamless)

  private:
    char        *m_pszFname;
    char        *m_pszPath;
    TABAccess   m_eAccessMode;
    OGRFeatureDefn *m_poFeatureDefnRef;

    TABFile     *m_poIndexTable;
    int         m_nTableNameField;
    int         m_nCurBaseTableId;
    TABFile     *m_poCurBaseTable;
    GBool       m_bEOF;

    ///////////////
    // Private Read access specific stuff
    //
    int         OpenForRead(const char *pszFname,
                            GBool bTestOpenNoError = FALSE );
    int         OpenBaseTable(TABFeature *poIndexFeature,
                              GBool bTestOpenNoError = FALSE);
    int         OpenBaseTable(int nTableId, GBool bTestOpenNoError = FALSE);
    int         OpenNextBaseTable(GBool bTestOpenNoError =FALSE);
    static GIntBig     EncodeFeatureId(int nTableId, int nBaseFeatureId);
    static int         ExtractBaseTableId(GIntBig nEncodedFeatureId);
    static int         ExtractBaseFeatureId(GIntBig nEncodedFeatureId);

  public:
    TABSeamless();
    virtual ~TABSeamless();

    virtual TABFileClass GetFileClass() override {return TABFC_TABSeamless;}

    virtual int Open(const char *pszFname, const char* pszAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr ) override { return IMapInfoFile::Open(pszFname, pszAccess, bTestOpenNoError, pszCharset); }
    virtual int Open(const char *pszFname, TABAccess eAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr ) override;
    virtual int Close() override;

    virtual const char *GetTableName() override
           {return m_poFeatureDefnRef?m_poFeatureDefnRef->GetName():"";}

    virtual void        SetSpatialFilter( OGRGeometry * ) override;
    virtual void        SetSpatialFilter( int iGeomField, OGRGeometry *poGeom ) override
                { OGRLayer::SetSpatialFilter(iGeomField, poGeom); }

    virtual void        ResetReading() override;
    virtual int         TestCapability( const char * pszCap ) override;
    virtual GIntBig     GetFeatureCount (int bForce) override;
    virtual OGRErr      GetExtent(OGREnvelope *psExtent, int bForce) override;
    virtual OGRErr      GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) override
                { return OGRLayer::GetExtent(iGeomField, psExtent, bForce); }

    ///////////////
    // Read access specific stuff
    //

    virtual GIntBig GetNextFeatureId(GIntBig nPrevId) override;
    virtual TABFeature *GetFeatureRef(GIntBig nFeatureId) override;
    virtual OGRFeatureDefn *GetLayerDefn() override;

    virtual TABFieldType GetNativeFieldType(int nFieldId) override;

    virtual int GetBounds(double &dXMin, double &dYMin,
                          double &dXMax, double &dYMax,
                          GBool bForce = TRUE ) override;

    virtual OGRSpatialReference *GetSpatialRef() override;

    virtual int GetFeatureCountByType(int &numPoints, int &numLines,
                                      int &numRegions, int &numTexts,
                                      GBool bForce = TRUE) override;

    virtual GBool IsFieldIndexed(int nFieldId) override;
    virtual GBool IsFieldUnique(int nFieldId) override;

    ///////////////
    // Write access specific stuff
    //
    virtual int SetBounds(CPL_UNUSED double dXMin, CPL_UNUSED double dYMin,
                          CPL_UNUSED double dXMax, CPL_UNUSED double dYMax) override   {return -1;}
    virtual int SetFeatureDefn(CPL_UNUSED OGRFeatureDefn *poFeatureDefn,
                               CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes=nullptr) override
                                                        {return -1;}
    virtual int AddFieldNative(CPL_UNUSED const char *pszName,
                               CPL_UNUSED TABFieldType eMapInfoType,
                               CPL_UNUSED int nWidth=0,
                               CPL_UNUSED int nPrecision=0,
                               CPL_UNUSED GBool bIndexed=FALSE,
                               CPL_UNUSED GBool bUnique=FALSE,
                               CPL_UNUSED int bApproxOK = TRUE) override {return -1;}

    virtual int SetSpatialRef(CPL_UNUSED OGRSpatialReference *poSpatialRef) override {return -1;}

    virtual OGRErr CreateFeature(CPL_UNUSED TABFeature *poFeature) override
                                        {return OGRERR_UNSUPPORTED_OPERATION;}

    virtual int SetFieldIndexed(CPL_UNUSED int nFieldId) override {return -1;}

    ///////////////
    // semi-private.
    virtual int  GetProjInfo(TABProjInfo *poPI) override
            { return m_poIndexTable?m_poIndexTable->GetProjInfo(poPI):-1; }
    virtual int SetProjInfo(CPL_UNUSED TABProjInfo *poPI) override         { return -1; }
    virtual int SetMIFCoordSys(const char * /*pszMIFCoordSys*/) override {return -1;}

#ifdef DEBUG
    virtual void Dump(FILE *fpOut = nullptr) override;
#endif
};

/*---------------------------------------------------------------------
 *                      class MIFFile
 *
 * The main class for (MID/MIF) datasets.  External programs should use this
 * class to open a (MID/MIF) dataset and read/write features from/to it.
 *
 *--------------------------------------------------------------------*/
class MIFFile final : public IMapInfoFile
{
    CPL_DISALLOW_COPY_ASSIGN(MIFFile)

  private:
    char        *m_pszFname;
    TABAccess    m_eAccessMode;
    int          m_nVersion;   /* Dataset version: 300, 450, 600, 900, etc. */
    char        *m_pszDelimiter;
    char        *m_pszUnique;
    char        *m_pszIndex;
    char        *m_pszCoordSys;

    TABFieldType *m_paeFieldType;
    GBool       *m_pabFieldIndexed;
    GBool       *m_pabFieldUnique;

    double       m_dfXMultiplier;
    double       m_dfYMultiplier;
    double       m_dfXDisplacement;
    double       m_dfYDisplacement;

    /* these are the projection bounds, possibly much broader than extents */
    double      m_dXMin;
    double      m_dYMin;
    double      m_dXMax;
    double      m_dYMax;

    /* extents, as cached by MIFFile::PreParseFile() */
    int         m_bExtentsSet;
    OGREnvelope m_sExtents{};

    int         m_nPoints;
    int         m_nLines;
    int         m_nRegions;
    int         m_nTexts;

    int         m_nPreloadedId;  // preloaded mif line is for this feature id
    MIDDATAFile  *m_poMIDFile;   // Mid file
    MIDDATAFile  *m_poMIFFile;   // Mif File

    OGRFeatureDefn *m_poDefn;
    std::set<CPLString> m_oSetFields{};
    OGRSpatialReference *m_poSpatialRef;

    int         m_nFeatureCount;
    int         m_nWriteFeatureId;
    int         m_nAttribute;

    ///////////////
    // Private Read access specific stuff
    //
    int         ReadFeatureDefn();
    int         ParseMIFHeader(int* pbIsEmpty);
    void        PreParseFile();
    int         AddFields(const char *pszLine);
    int         GotoFeature(int nFeatureId);
    int         NextFeature();

    ///////////////
    // Private Write access specific stuff
    //
    GBool       m_bPreParsed;
    GBool       m_bHeaderWrote;

    int         WriteMIFHeader();
    void UpdateExtents(double dfX,double dfY);

  public:
    MIFFile();
    virtual ~MIFFile();

    virtual TABFileClass GetFileClass() override {return TABFC_MIFFile;}

    virtual int Open(const char *pszFname, const char* pszAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr ) override { return IMapInfoFile::Open(pszFname, pszAccess, bTestOpenNoError, pszCharset); }
    virtual int Open(const char *pszFname, TABAccess eAccess,
                     GBool bTestOpenNoError = FALSE,
                     const char* pszCharset = nullptr ) override;
    virtual int Close() override;

    virtual const char *GetTableName() override
                           {return m_poDefn?m_poDefn->GetName():"";}

    virtual int         TestCapability( const char * pszCap ) override ;
    virtual GIntBig     GetFeatureCount (int bForce) override;
    virtual void        ResetReading() override;
    virtual OGRErr      GetExtent(OGREnvelope *psExtent, int bForce) override;
    virtual OGRErr      GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) override
                { return OGRLayer::GetExtent(iGeomField, psExtent, bForce); }

    ///////////////
    // Read access specific stuff
    //

    virtual GIntBig GetNextFeatureId(GIntBig nPrevId) override;
    virtual TABFeature *GetFeatureRef(GIntBig nFeatureId) override;
    virtual OGRFeatureDefn *GetLayerDefn() override;

    virtual TABFieldType GetNativeFieldType(int nFieldId) override;

    virtual int GetBounds(double &dXMin, double &dYMin,
                          double &dXMax, double &dYMax,
                          GBool bForce = TRUE ) override;

    virtual OGRSpatialReference *GetSpatialRef() override;

    virtual int GetFeatureCountByType(int &numPoints, int &numLines,
                                      int &numRegions, int &numTexts,
                                      GBool bForce = TRUE) override;

    virtual GBool IsFieldIndexed(int nFieldId) override;
    virtual GBool IsFieldUnique(int nFieldId) override;

    virtual int GetVersion() { return m_nVersion; }

    ///////////////
    // Write access specific stuff
    //
    virtual int SetBounds(double dXMin, double dYMin,
                          double dXMax, double dYMax) override;
    virtual int SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
                            TABFieldType *paeMapInfoNativeFieldTypes = nullptr) override;
    virtual int AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
                               int nWidth=0, int nPrecision=0,
                               GBool bIndexed=FALSE, GBool bUnique=FALSE,
                               int bApproxOK = TRUE) override;
    /* TODO */
    virtual int SetSpatialRef(OGRSpatialReference *poSpatialRef) override;

    virtual OGRErr CreateFeature(TABFeature *poFeature) override;

    virtual int SetFieldIndexed(int nFieldId) override;

    ///////////////
    // semi-private.
    virtual int  GetProjInfo(TABProjInfo * /*poPI*/) override{return -1;}
    /*  { return m_poMAPFile->GetHeaderBlock()->GetProjInfo( poPI ); }*/
    virtual int  SetProjInfo(TABProjInfo * /*poPI*/) override{return -1;}
    /*  { return m_poMAPFile->GetHeaderBlock()->SetProjInfo( poPI ); }*/
    virtual int  SetMIFCoordSys(const char * pszMIFCoordSys) override;
    virtual int SetCharset(const char* pszCharset) override;

#ifdef DEBUG
    virtual void Dump(FILE * /*fpOut*/ = nullptr) override {}
#endif
};

/*---------------------------------------------------------------------
 * Define some error codes specific to this lib.
 *--------------------------------------------------------------------*/
#define TAB_WarningFeatureTypeNotSupported     501
#define TAB_WarningInvalidFieldName            502
#define TAB_WarningBoundsOverflow              503

/*---------------------------------------------------------------------
 * Codes for the feature classes
 *--------------------------------------------------------------------*/
typedef enum
{
    TABFCNoGeomFeature = 0,
    TABFCPoint = 1,
    TABFCFontPoint = 2,
    TABFCCustomPoint = 3,
    TABFCText = 4,
    TABFCPolyline = 5,
    TABFCArc = 6,
    TABFCRegion = 7,
    TABFCRectangle = 8,
    TABFCEllipse = 9,
    TABFCMultiPoint = 10,
    TABFCCollection = 11,
    TABFCDebugFeature
} TABFeatureClass;

/*---------------------------------------------------------------------
 * Definitions for text attributes
 *--------------------------------------------------------------------*/
typedef enum TABTextJust_t
{
    TABTJLeft = 0,      // Default: Left Justification
    TABTJCenter,
    TABTJRight
} TABTextJust;

typedef enum TABTextSpacing_t
{
    TABTSSingle = 0,    // Default: Single spacing
    TABTS1_5,           // 1.5
    TABTSDouble
} TABTextSpacing;

typedef enum TABTextLineType_t
{
    TABTLNoLine = 0,    // Default: No line
    TABTLSimple,
    TABTLArrow
} TABTextLineType;

typedef enum TABFontStyle_t     // Can be OR'ed
{                               // except box and halo are mutually exclusive
    TABFSNone       = 0,
    TABFSBold       = 0x0001,
    TABFSItalic     = 0x0002,
    TABFSUnderline  = 0x0004,
    TABFSStrikeout  = 0x0008,
    TABFSOutline    = 0x0010,
    TABFSShadow     = 0x0020,
    TABFSInverse    = 0x0040,
    TABFSBlink      = 0x0080,
    TABFSBox        = 0x0100,   // See note about box vs halo below.
    TABFSHalo       = 0x0200,   // MIF uses 256, see MIF docs, App.A
    TABFSAllCaps    = 0x0400,   // MIF uses 512
    TABFSExpanded   = 0x0800    // MIF uses 1024
} TABFontStyle;

/* TABFontStyle enum notes:
 *
 * The enumeration values above correspond to the values found in a .MAP
 * file. However, they differ a little from what is found in a MIF file:
 * Values 0x01 to 0x80 are the same in .MIF and .MAP files.
 * Values 0x200 to 0x800 in .MAP are 0x100 to 0x400 in .MIF
 *
 * What about TABFSBox (0x100) ?
 * TABFSBox is stored just like the other styles in .MAP files but it is not
 * explicitly stored in a MIF file.
 * If a .MIF FONT() clause contains the optional BG color, then this implies
 * that either Halo or Box was set.  Thus if TABFSHalo (value 256 in MIF)
 * is not set in the style, then this implies that TABFSBox should be set.
 */

typedef enum TABCustSymbStyle_t // Can be OR'ed
{
    TABCSNone       = 0,        // Transparent BG, use default colors
    TABCSBGOpaque   = 0x01,     // White pixels are opaque
    TABCSApplyColor = 0x02      // non-white pixels drawn using symbol color
} TABCustSymbStyle;

/*=====================================================================
  Base classes to be used to add supported drawing tools to each feature type
 =====================================================================*/

class ITABFeaturePen
{
  protected:
    int         m_nPenDefIndex;
    TABPenDef   m_sPenDef;
  public:
    ITABFeaturePen();
    virtual ~ITABFeaturePen() {}
    int         GetPenDefIndex() const {return m_nPenDefIndex;}
    TABPenDef  *GetPenDefRef() {return &m_sPenDef;}
    const TABPenDef  *GetPenDefRef() const {return &m_sPenDef;}

    GByte       GetPenWidthPixel() const;
    double      GetPenWidthPoint() const;
    int         GetPenWidthMIF() const;
    GByte       GetPenPattern() const {return m_sPenDef.nLinePattern;}
    GInt32      GetPenColor() const   {return m_sPenDef.rgbColor;}

    void        SetPenWidthPixel(GByte val);
    void        SetPenWidthPoint(double val);
    void        SetPenWidthMIF(int val);

    void        SetPenPattern(GByte val) {m_sPenDef.nLinePattern=val;}
    void        SetPenColor(GInt32 clr)  {m_sPenDef.rgbColor = clr;}

    const char *GetPenStyleString() const;
    void        SetPenFromStyleString(const char *pszStyleString);

    void        DumpPenDef(FILE *fpOut = nullptr);
};

class ITABFeatureBrush
{
  protected:
    int         m_nBrushDefIndex;
    TABBrushDef m_sBrushDef;
  public:
    ITABFeatureBrush();
    virtual ~ITABFeatureBrush() {}
    int         GetBrushDefIndex() const {return m_nBrushDefIndex;}
    TABBrushDef *GetBrushDefRef() {return &m_sBrushDef;}
    const TABBrushDef *GetBrushDefRef() const {return &m_sBrushDef;}

    GInt32      GetBrushFGColor() const     {return m_sBrushDef.rgbFGColor;}
    GInt32      GetBrushBGColor() const     {return m_sBrushDef.rgbBGColor;}
    GByte       GetBrushPattern() const     {return m_sBrushDef.nFillPattern;}
    GByte       GetBrushTransparent() const {return m_sBrushDef.bTransparentFill;}

    void        SetBrushFGColor(GInt32 clr)  { m_sBrushDef.rgbFGColor = clr;}
    void        SetBrushBGColor(GInt32 clr)  { m_sBrushDef.rgbBGColor = clr;}
    void        SetBrushPattern(GByte val)   { m_sBrushDef.nFillPattern=val;}
    void        SetBrushTransparent(GByte val)
                                          {m_sBrushDef.bTransparentFill=val;}

    const char *GetBrushStyleString() const;
    void        SetBrushFromStyleString(const char *pszStyleString);

    void        DumpBrushDef(FILE *fpOut = nullptr);
};

class ITABFeatureFont
{
  protected:
    int         m_nFontDefIndex;
    TABFontDef  m_sFontDef;
  public:
    ITABFeatureFont();
    virtual ~ITABFeatureFont() {}
    int         GetFontDefIndex() const {return m_nFontDefIndex;}
    TABFontDef *GetFontDefRef() {return &m_sFontDef;}
    const TABFontDef *GetFontDefRef() const {return &m_sFontDef;}

    const char *GetFontNameRef() const {return m_sFontDef.szFontName;}

    void        SetFontName(const char *pszName);

    void        DumpFontDef(FILE *fpOut = nullptr);
};

class ITABFeatureSymbol
{
  protected:
    int         m_nSymbolDefIndex;
    TABSymbolDef m_sSymbolDef;
  public:
    ITABFeatureSymbol();
    virtual ~ITABFeatureSymbol() {}
    int         GetSymbolDefIndex() const {return m_nSymbolDefIndex;}
    TABSymbolDef *GetSymbolDefRef() {return &m_sSymbolDef;}
    const TABSymbolDef *GetSymbolDefRef() const {return &m_sSymbolDef;}

    GInt16      GetSymbolNo() const    {return m_sSymbolDef.nSymbolNo;}
    GInt16      GetSymbolSize() const  {return m_sSymbolDef.nPointSize;}
    GInt32      GetSymbolColor() const {return m_sSymbolDef.rgbColor;}

    void        SetSymbolNo(GInt16 val)     { m_sSymbolDef.nSymbolNo = val;}
    void        SetSymbolSize(GInt16 val)   { m_sSymbolDef.nPointSize = val;}
    void        SetSymbolColor(GInt32 clr)  { m_sSymbolDef.rgbColor = clr;}

    virtual const char *GetSymbolStyleString(double dfAngle = 0.0) const;
    void        SetSymbolFromStyleString(const char *pszStyleString);
    virtual void SetSymbolFromStyle(OGRStyleSymbol* poSymbolStyle);

    void        DumpSymbolDef(FILE *fpOut = nullptr);
};

/*=====================================================================
                        Feature Classes
 =====================================================================*/

/*---------------------------------------------------------------------
 *                      class TABFeature
 *
 * Extend the OGRFeature to support MapInfo specific extensions related
 * to geometry types, representation strings, etc.
 *
 * TABFeature will be used as a base class for all the feature classes.
 *
 * This class will also be used to instantiate objects with no Geometry
 * (i.e. type TAB_GEOM_NONE) which is a valid case in MapInfo.
 *
 * The logic to read/write the object from/to the .DAT and .MAP files is also
 * implemented as part of this class and derived classes.
 *--------------------------------------------------------------------*/
class TABFeature : public OGRFeature
{
  protected:
    TABGeomType m_nMapInfoType;

    double      m_dXMin;
    double      m_dYMin;
    double      m_dXMax;
    double      m_dYMax;

    GBool       m_bDeletedFlag;

    void        CopyTABFeatureBase(TABFeature *poDestFeature);

    // Compr. Origin is set for TAB files by ValidateCoordType()
    GInt32      m_nXMin;
    GInt32      m_nYMin;
    GInt32      m_nXMax;
    GInt32      m_nYMax;
    GInt32      m_nComprOrgX;
    GInt32      m_nComprOrgY;

    virtual int UpdateMBR(TABMAPFile *poMapFile = nullptr);

  public:
    explicit TABFeature(OGRFeatureDefn *poDefnIn );
    virtual ~TABFeature();

    static  TABFeature     *CreateFromMapInfoType(int nMapInfoType,
                                                  OGRFeatureDefn *poDefn);

    virtual TABFeature     *CloneTABFeature(OGRFeatureDefn *pNewDefn = nullptr);
    virtual TABFeatureClass GetFeatureClass() { return TABFCNoGeomFeature; }
    virtual TABGeomType     GetMapInfoType()  { return m_nMapInfoType; }
    virtual TABGeomType     ValidateMapInfoType(CPL_UNUSED TABMAPFile *poMapFile = nullptr)
                                                {m_nMapInfoType=TAB_GEOM_NONE;
                                                 return m_nMapInfoType;}
    GBool       IsRecordDeleted() { return m_bDeletedFlag; }
    void        SetRecordDeleted(GBool bDeleted) { m_bDeletedFlag=bDeleted; }

    /*-----------------------------------------------------------------
     * TAB Support
     *----------------------------------------------------------------*/

    virtual int ReadRecordFromDATFile(TABDATFile *poDATFile);
    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr);

    virtual int WriteRecordToDATFile(TABDATFile *poDATFile,
                                     TABINDFile *poINDFile, int *panIndexNo);
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr);
    GBool       ValidateCoordType(TABMAPFile * poMapFile);
    void        ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr,
                                        GInt32 nComprOrgX, GInt32 nComprOrgY,
                                        GInt32 nXMin, GInt32 nYMin,
                                        GInt32 nXMax, GInt32 nYMax);

    /*-----------------------------------------------------------------
     * Mid/Mif Support
     *----------------------------------------------------------------*/

    virtual int ReadRecordFromMIDFile(MIDDATAFile *fp);
    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp);

    virtual int WriteRecordToMIDFile(MIDDATAFile *fp);
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp);

    void ReadMIFParameters(MIDDATAFile *fp);
    void WriteMIFParameters(MIDDATAFile *fp);

    /*-----------------------------------------------------------------
     *----------------------------------------------------------------*/

    void        SetMBR(double dXMin, double dYMin,
                       double dXMax, double dYMax);
    void        GetMBR(double &dXMin, double &dYMin,
                       double &dXMax, double &dYMax);
    void        SetIntMBR(GInt32 nXMin, GInt32 nYMin,
                          GInt32 nXMax, GInt32 nYMax);
    void        GetIntMBR(GInt32 &nXMin, GInt32 &nYMin,
                          GInt32 &nXMax, GInt32 &nYMax);

    virtual void DumpMID(FILE *fpOut = nullptr);
    virtual void DumpMIF(FILE *fpOut = nullptr);
};

/*---------------------------------------------------------------------
 *                      class TABPoint
 *
 * Feature class to handle old style MapInfo point symbols:
 *
 *     TAB_GEOM_SYMBOL_C        0x01
 *     TAB_GEOM_SYMBOL          0x02
 *
 * Feature geometry will be a OGRPoint
 *
 * The symbol number is in the range [31..67], with 31=None and corresponds
 * to one of the 35 predefined "Old MapInfo Symbols"
 *
 * NOTE: This class is also used as a base class for the other point
 * symbol types TABFontPoint and TABCustomPoint.
 *--------------------------------------------------------------------*/
class TABPoint: public TABFeature,
                public ITABFeatureSymbol
{
    CPL_DISALLOW_COPY_ASSIGN(TABPoint)

  public:
    explicit TABPoint(OGRFeatureDefn *poDefnIn);
    virtual ~TABPoint();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCPoint; }
    virtual TABGeomType     ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    double      GetX();
    double      GetY();

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;
};

/*---------------------------------------------------------------------
 *                      class TABFontPoint
 *
 * Feature class to handle MapInfo Font Point Symbol types:
 *
 *     TAB_GEOM_FONTSYMBOL_C    0x28
 *     TAB_GEOM_FONTSYMBOL      0x29
 *
 * Feature geometry will be a OGRPoint
 *
 * The symbol number refers to a character code in the specified Windows
 * Font (e.g. "Windings").
 *--------------------------------------------------------------------*/
class TABFontPoint final : public TABPoint,
                    public ITABFeatureFont
{
    CPL_DISALLOW_COPY_ASSIGN(TABFontPoint)

  protected:
    double      m_dAngle;
    GInt16      m_nFontStyle;           // Bold/shadow/halo/etc.

  public:
    explicit TABFontPoint(OGRFeatureDefn *poDefnIn);
    virtual ~TABFontPoint();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCFontPoint; }

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetSymbolStyleString(double dfAngle = 0.0) const override;
    virtual const char *GetStyleString() const override;
    virtual void SetSymbolFromStyle(OGRStyleSymbol* poSymbolStyle) override;

    GBool       QueryFontStyle(TABFontStyle eStyleToQuery);
    void        ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStatus);

    int         GetFontStyleMIFValue();
    void        SetFontStyleMIFValue(int nStyle);
    int         GetFontStyleTABValue()           {return m_nFontStyle;}
    void        SetFontStyleTABValue(int nStyle){m_nFontStyle=static_cast<GInt16>(nStyle);}

    // GetSymbolAngle(): Return angle in degrees counterclockwise
    double      GetSymbolAngle() const {return m_dAngle;}
    void        SetSymbolAngle(double dAngle);
};

/*---------------------------------------------------------------------
 *                      class TABCustomPoint
 *
 * Feature class to handle MapInfo Custom Point Symbol (Bitmap) types:
 *
 *     TAB_GEOM_CUSTOMSYMBOL_C  0x2b
 *     TAB_GEOM_CUSTOMSYMBOL    0x2c
 *
 * Feature geometry will be a OGRPoint
 *
 * The symbol name is the name of a BMP file stored in the "CustSymb"
 * directory (e.g. "arrow.BMP").  The symbol number has no meaning for
 * this symbol type.
 *--------------------------------------------------------------------*/
class TABCustomPoint final : public TABPoint,
                      public ITABFeatureFont
{
  protected:
    GByte       m_nCustomStyle;         // Show BG/Apply Color

  public:
    GByte       m_nUnknown_;

  public:
    explicit TABCustomPoint(OGRFeatureDefn *poDefnIn);
    virtual ~TABCustomPoint();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCCustomPoint; }

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    const char *GetSymbolNameRef()      { return GetFontNameRef(); }
    void        SetSymbolName(const char *pszName) {SetFontName(pszName);}

    GByte       GetCustomSymbolStyle()              {return m_nCustomStyle;}
    void        SetCustomSymbolStyle(GByte nStyle)  {m_nCustomStyle = nStyle;}
};

/*---------------------------------------------------------------------
 *                      class TABPolyline
 *
 * Feature class to handle the various MapInfo line types:
 *
 *     TAB_GEOM_LINE_C         0x04
 *     TAB_GEOM_LINE           0x05
 *     TAB_GEOM_PLINE_C        0x07
 *     TAB_GEOM_PLINE          0x08
 *     TAB_GEOM_MULTIPLINE_C   0x25
 *     TAB_GEOM_MULTIPLINE     0x26
 *     TAB_GEOM_V450_MULTIPLINE_C 0x31
 *     TAB_GEOM_V450_MULTIPLINE   0x32
 *
 * Feature geometry can be either a OGRLineString or a OGRMultiLineString
 *--------------------------------------------------------------------*/
class TABPolyline final : public TABFeature,
                   public ITABFeaturePen
{
  private:
    GBool       m_bCenterIsSet;
    double      m_dCenterX;
    double      m_dCenterY;
    GBool       m_bWriteTwoPointLineAsPolyline;

  public:
    explicit TABPolyline(OGRFeatureDefn *poDefnIn);
    virtual ~TABPolyline();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCPolyline; }
    virtual TABGeomType     ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    /* 2 methods to simplify access to rings in a multiple polyline
     */
    int                 GetNumParts();
    OGRLineString      *GetPartRef(int nPartIndex);

    GBool       TwoPointLineAsPolyline();
    void        TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline);

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;

    int         GetCenter(double &dX, double &dY);
    void        SetCenter(double dX, double dY);

    // MapInfo-specific attributes... made available through public vars
    // for now.
    GBool       m_bSmooth;
};

/*---------------------------------------------------------------------
 *                      class TABRegion
 *
 * Feature class to handle the MapInfo region types:
 *
 *     TAB_GEOM_REGION_C         0x0d
 *     TAB_GEOM_REGION           0x0e
 *     TAB_GEOM_V450_REGION_C    0x2e
 *     TAB_GEOM_V450_REGION      0x2f
 *
 * Feature geometry will be returned as OGRPolygon (with a single ring)
 * or OGRMultiPolygon (for multiple rings).
 *
 * REGIONs with multiple rings are returned as OGRMultiPolygon instead of
 * as OGRPolygons since OGRPolygons require that the first ring be the
 * outer ring, and the other all be inner rings, but this is not guaranteed
 * inside MapInfo files.  However, when writing features, OGRPolygons with
 * multiple rings will be accepted without problem.
 *--------------------------------------------------------------------*/
class TABRegion final : public TABFeature,
                 public ITABFeaturePen,
                 public ITABFeatureBrush
{
  private:
    GBool       m_bSmooth;
    GBool       m_bCenterIsSet;
    double      m_dCenterX;
    double      m_dCenterY;

    int     ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs,
                            TABMAPFile *poMAPFile);
    static int     AppendSecHdrs(OGRPolygon *poPolygon,
                          TABMAPCoordSecHdr * &pasSecHdrs,
                          TABMAPFile *poMAPFile,
                          int &iLastRing);

  public:
    explicit TABRegion(OGRFeatureDefn *poDefnIn);
    virtual ~TABRegion();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCRegion; }
    virtual TABGeomType     ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    /* 2 methods to make the REGION's geometry look like a single collection
     * of OGRLinearRings
     */
    int                 GetNumRings();
    OGRLinearRing      *GetRingRef(int nRequestedRingIndex);
    GBool               IsInteriorRing(int nRequestedRingIndex);

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;

    int         GetCenter(double &dX, double &dY);
    void        SetCenter(double dX, double dY);
};

/*---------------------------------------------------------------------
 *                      class TABRectangle
 *
 * Feature class to handle the MapInfo rectangle types:
 *
 *     TAB_GEOM_RECT_C         0x13
 *     TAB_GEOM_RECT           0x14
 *     TAB_GEOM_ROUNDRECT_C    0x16
 *     TAB_GEOM_ROUNDRECT      0x17
 *
 * A rectangle is defined by the coords of its 2 opposite corners (the MBR)
 * Its corners can optionally be rounded, in which case a X and Y rounding
 * radius will be defined.
 *
 * Feature geometry will be OGRPolygon
 *--------------------------------------------------------------------*/
class TABRectangle final : public TABFeature,
                    public ITABFeaturePen,
                    public ITABFeatureBrush
{
  private:
    virtual int UpdateMBR(TABMAPFile *poMapFile = nullptr) override;

  public:
    explicit TABRectangle(OGRFeatureDefn *poDefnIn);
    virtual ~TABRectangle();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCRectangle; }
    virtual TABGeomType     ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;

    // MapInfo-specific attributes... made available through public vars
    // for now.
    GBool       m_bRoundCorners;
    double      m_dRoundXRadius;
    double      m_dRoundYRadius;
};

/*---------------------------------------------------------------------
 *                      class TABEllipse
 *
 * Feature class to handle the MapInfo ellipse types:
 *
 *     TAB_GEOM_ELLIPSE_C      0x19
 *     TAB_GEOM_ELLIPSE        0x1a
 *
 * An ellipse is defined by the coords of its 2 opposite corners (the MBR)
 *
 * Feature geometry can be either an OGRPoint defining the center of the
 * ellipse, or an OGRPolygon defining the ellipse itself.
 *
 * When an ellipse is read, the returned geometry is a OGRPolygon representing
 * the ellipse with 2 degrees line segments.
 *
 * In the case of the OGRPoint, then the X/Y Radius MUST be set, but.
 * However with an OGRPolygon, if the X/Y radius are not set (== 0) then
 * the MBR of the polygon will be used to define the ellipse parameters
 * and the center of the MBR is used as the center of the ellipse...
 * (i.e. the polygon vertices themselves will be ignored).
 *--------------------------------------------------------------------*/
class TABEllipse final : public TABFeature,
                  public ITABFeaturePen,
                  public ITABFeatureBrush
{
  private:
    virtual int UpdateMBR(TABMAPFile *poMapFile = nullptr) override;

  public:
    explicit TABEllipse(OGRFeatureDefn *poDefnIn);
    virtual ~TABEllipse();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCEllipse; }
    virtual TABGeomType     ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;

    // MapInfo-specific attributes... made available through public vars
    // for now.
    double      m_dCenterX;
    double      m_dCenterY;
    double      m_dXRadius;
    double      m_dYRadius;
};

/*---------------------------------------------------------------------
 *                      class TABArc
 *
 * Feature class to handle the MapInfo arc types:
 *
 *     TAB_GEOM_ARC_C      0x0a
 *     TAB_GEOM_ARC        0x0b
 *
 * In MapInfo, an arc is defined by the coords of the MBR corners of its
 * defining ellipse, which in this case is different from the arc's MBR,
 * and a start and end angle in degrees.
 *
 * Feature geometry can be either an OGRLineString or an OGRPoint.
 *
 * In any case, X/Y radius X/Y center, and start/end angle (in degrees
 * counterclockwise) MUST be set.
 *
 * When an arc is read, the returned geometry is an OGRLineString
 * representing the arc with 2 degrees line segments.
 *--------------------------------------------------------------------*/
class TABArc final : public TABFeature,
              public ITABFeaturePen
{
  private:
    double      m_dStartAngle;  // In degrees, counterclockwise,
    double      m_dEndAngle;    // starting at 3 o'clock

    virtual int UpdateMBR(TABMAPFile *poMapFile = nullptr) override;

  public:
    explicit TABArc(OGRFeatureDefn *poDefnIn);
    virtual ~TABArc();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCArc; }
    virtual TABGeomType     ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;

    double      GetStartAngle() { return m_dStartAngle; }
    double      GetEndAngle()   { return m_dEndAngle; }
    void        SetStartAngle(double dAngle);
    void        SetEndAngle(double dAngle);

    // MapInfo-specific attributes... made available through public vars
    // for now.
    double      m_dCenterX;
    double      m_dCenterY;
    double      m_dXRadius;
    double      m_dYRadius;
};

/*---------------------------------------------------------------------
 *                      class TABText
 *
 * Feature class to handle the MapInfo text types:
 *
 *     TAB_GEOM_TEXT_C         0x10
 *     TAB_GEOM_TEXT           0x11
 *
 * Feature geometry is an OGRPoint corresponding to the lower-left
 * corner of the text MBR BEFORE ROTATION.
 *
 * Text string, and box height/width (box before rotation is applied)
 * are required in a valid text feature and MUST be set.
 * Text angle and other styles are optional.
 *--------------------------------------------------------------------*/
class TABText final : public TABFeature,
               public ITABFeatureFont,
               public ITABFeaturePen
{
    CPL_DISALLOW_COPY_ASSIGN(TABText)

  protected:
    char        *m_pszString;

    double      m_dAngle;
    double      m_dHeight;
    mutable double      m_dWidth;
    double      m_dfLineEndX;
    double      m_dfLineEndY;
    GBool       m_bLineEndSet;
    void        UpdateTextMBR();

    GInt32      m_rgbForeground;
    GInt32      m_rgbBackground;
    GInt32      m_rgbOutline;
    GInt32      m_rgbShadow;

    GInt16      m_nTextAlignment;       // Justification/Vert.Spacing/arrow
    GInt16      m_nFontStyle;           // Bold/italic/underlined/shadow/...

    const char *GetLabelStyleString() const;

    virtual int UpdateMBR(TABMAPFile *poMapFile = nullptr) override;

  public:
    explicit TABText(OGRFeatureDefn *poDefnIn);
    virtual ~TABText();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCText; }
    virtual TABGeomType     ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;

    const char *GetTextString() const;
    double      GetTextAngle() const;
    double      GetTextBoxHeight() const;
    double      GetTextBoxWidth() const;
    GInt32      GetFontFGColor() const;
    GInt32      GetFontBGColor() const;
    GInt32      GetFontOColor() const;
    GInt32      GetFontSColor() const;
    void        GetTextLineEndPoint(double &dX, double &dY);

    TABTextJust GetTextJustification() const;
    TABTextSpacing  GetTextSpacing() const;
    TABTextLineType GetTextLineType() const;
    GBool       QueryFontStyle(TABFontStyle eStyleToQuery) const;

    void        SetTextString(const char *pszStr);
    void        SetTextAngle(double dAngle);
    void        SetTextBoxHeight(double dHeight);
    void        SetTextBoxWidth(double dWidth);
    void        SetFontFGColor(GInt32 rgbColor);
    void        SetFontBGColor(GInt32 rgbColor);
    void        SetFontOColor(GInt32 rgbColor);
    void        SetFontSColor(GInt32 rgbColor);
    void        SetTextLineEndPoint(double dX, double dY);

    void        SetTextJustification(TABTextJust eJust);
    void        SetTextSpacing(TABTextSpacing eSpacing);
    void        SetTextLineType(TABTextLineType eLineType);
    void        ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStatus);

    int         GetFontStyleMIFValue() const;
    void        SetFontStyleMIFValue(int nStyle, GBool bBGColorSet=FALSE);
    GBool       IsFontBGColorUsed() const;
    GBool       IsFontOColorUsed() const;
    GBool       IsFontSColorUsed() const;
    GBool       IsFontBold() const;
    GBool       IsFontItalic() const;
    GBool       IsFontUnderline() const;
    int         GetFontStyleTABValue() const {return m_nFontStyle;}
    void        SetFontStyleTABValue(int nStyle){m_nFontStyle=static_cast<GInt16>(nStyle);}
};

/*---------------------------------------------------------------------
 *                      class TABMultiPoint
 *
 * Feature class to handle MapInfo Multipoint features:
 *
 *     TAB_GEOM_MULTIPOINT_C        0x34
 *     TAB_GEOM_MULTIPOINT          0x35
 *
 * Feature geometry will be a OGRMultiPoint
 *
 * The symbol number is in the range [31..67], with 31=None and corresponds
 * to one of the 35 predefined "Old MapInfo Symbols"
 *--------------------------------------------------------------------*/
class TABMultiPoint final : public TABFeature,
                     public ITABFeatureSymbol
{
  private:
    // We call it center, but it is more like a label point
    // Its value default to be the location of the first point
    GBool       m_bCenterIsSet;
    double      m_dCenterX;
    double      m_dCenterY;

  public:
    explicit TABMultiPoint(OGRFeatureDefn *poDefnIn);
    virtual ~TABMultiPoint();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCMultiPoint; }
    virtual TABGeomType     ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    int         GetXY(int i, double &dX, double &dY);
    int         GetNumPoints();

    int         GetCenter(double &dX, double &dY);
    void        SetCenter(double dX, double dY);

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;
};

/*---------------------------------------------------------------------
 *
 *                      class TABCollection
 *
 * Feature class to handle MapInfo Collection features:
 *
 *     TAB_GEOM_COLLECTION_C        0x37
 *     TAB_GEOM_COLLECTION          0x38
 *
 * Feature geometry will be a OGRCollection
 *
 * **** IMPORTANT NOTE: ****
 *
 * The current implementation does not allow setting the Geometry via
 * OGRFeature::SetGeometry*(). The geometries must be set via the
 * TABCollection::SetRegion/Pline/MpointDirectly() methods which will take
 * care of keeping the OGRFeature's geometry in sync.
 *
 * If we ever want to support creating collections via the OGR interface then
 * something should be added in TABCollection::WriteGeometryToMapFile(), or
 * perhaps in ValidateMapInfoType(), or even better in a custom
 * TABCollection::SetGeometry*()... but then this last option may not work
 * unless OGRFeature::SetGeometry*() are made virtual in OGR.
 *
 *--------------------------------------------------------------------*/
class TABCollection final : public TABFeature,
                     public ITABFeatureSymbol
{
    CPL_DISALLOW_COPY_ASSIGN(TABCollection)

  private:
    TABRegion       *m_poRegion;
    TABPolyline     *m_poPline;
    TABMultiPoint   *m_poMpoint;

    void    EmptyCollection();
    static int     ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
                            GBool bComprCoord,
                            GInt32 nComprOrgX, GInt32 nComprOrgY,
                            GInt32 &pnMinX, GInt32 &pnMinY,
                            GInt32 &pnMaxX, GInt32 &pnMaxY,
                            GInt32 &pnLabelX, GInt32 &pnLabelY );
    static int     WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
                             GBool bComprCoord,
                             GInt32 nMinX, GInt32 nMinY,
                             GInt32 nMaxX, GInt32 nMaxY,
                             GInt32 nLabelX, GInt32 nLabelY );
    int     SyncOGRGeometryCollection(GBool bSyncRegion,
                                      GBool bSyncPline,
                                      GBool bSyncMpoint);

  public:
    explicit TABCollection(OGRFeatureDefn *poDefnIn);
    virtual ~TABCollection();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCCollection; }
    virtual TABGeomType     ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = nullptr ) override;

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual const char *GetStyleString() const override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;

    TABRegion           *GetRegionRef()         {return m_poRegion; }
    TABPolyline         *GetPolylineRef()       {return m_poPline; }
    TABMultiPoint       *GetMultiPointRef()     {return m_poMpoint; }

    int                 SetRegionDirectly(TABRegion *poRegion);
    int                 SetPolylineDirectly(TABPolyline *poPline);
    int                 SetMultiPointDirectly(TABMultiPoint *poMpoint);
};

/*---------------------------------------------------------------------
 *                      class TABDebugFeature
 *
 * Feature class to use for testing purposes... this one does not
 * correspond to any MapInfo type... it is just used to dump info about
 * feature types that are not implemented yet.
 *--------------------------------------------------------------------*/
class TABDebugFeature final : public TABFeature
{
  private:
    GByte       m_abyBuf[512];
    int         m_nSize;
    int         m_nCoordDataPtr;  // -1 if none
    int         m_nCoordDataSize;

  public:
    explicit TABDebugFeature(OGRFeatureDefn *poDefnIn);
    virtual ~TABDebugFeature();

    virtual TABFeatureClass GetFeatureClass() override { return TABFCDebugFeature; }

    virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                        GBool bCoordDataOnly=FALSE,
                                        TABMAPCoordBlock **ppoCoordBlock=nullptr) override;
    virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *,
                                       GBool bCoordDataOnly=FALSE,
                                       TABMAPCoordBlock **ppoCoordBlock=nullptr) override;

    virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp) override;
    virtual int WriteGeometryToMIFFile(MIDDATAFile *fp) override;

    virtual void DumpMIF(FILE *fpOut = nullptr) override;
};

/* -------------------------------------------------------------------- */
/*      Some stuff related to spatial reference system handling.        */
/*                                                                      */
/*      In GDAL we make use of the coordsys transformation from         */
/*      other places (sometimes even from plugins), so we               */
/*      deliberately export these two functions from the DLL.           */
/* -------------------------------------------------------------------- */

char CPL_DLL *MITABSpatialRef2CoordSys( const OGRSpatialReference * );
OGRSpatialReference CPL_DLL * MITABCoordSys2SpatialRef( const char * );

bool MITABExtractCoordSysBounds( const char * pszCoordSys,
                                 double &dXMin, double &dYMin,
                                 double &dXMax, double &dYMax );
int MITABCoordSys2TABProjInfo(const char * pszCoordSys, TABProjInfo *psProj);

typedef struct {
    int         nDatumEPSGCode;
    int         nMapInfoDatumID;
    const char  *pszOGCDatumName;
    int         nEllipsoid;
    double      dfShiftX;
    double      dfShiftY;
    double      dfShiftZ;
    double      dfDatumParm0; /* RotX */
    double      dfDatumParm1; /* RotY */
    double      dfDatumParm2; /* RotZ */
    double      dfDatumParm3; /* Scale Factor */
    double      dfDatumParm4; /* Prime Meridian */
} MapInfoDatumInfo;

typedef struct
{
    int         nMapInfoId;
    const char *pszMapinfoName;
    double      dfA; /* semi major axis in meters */
    double      dfInvFlattening; /* Inverse flattening */
} MapInfoSpheroidInfo;

/*---------------------------------------------------------------------
 * The following are used for coordsys bounds lookup
 *--------------------------------------------------------------------*/

bool    MITABLookupCoordSysBounds(TABProjInfo *psCS,
                                  double &dXMin, double &dYMin,
                                  double &dXMax, double &dYMax,
                                  bool bOnlyUserTable = false);
int     MITABLoadCoordSysTable(const char *pszFname);
void    MITABFreeCoordSysTable();
bool    MITABCoordSysTableLoaded();  // TODO(schwehr): Unused?

#endif /* MITAB_H_INCLUDED_ */
