/**********************************************************************
 *
 * 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>
 *
 * SPDX-License-Identifier: MIT
 **********************************************************************/

#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 CPL_NON_FINAL : public OGRLayer
{
    CPL_DISALLOW_COPY_ASSIGN(IMapInfoFile)

  protected:
    GDALDataset *m_poDS = nullptr;
    GIntBig m_nCurFeatureId;
    TABFeature *m_poCurFeature;
    GBool m_bBoundsSet;

    char *m_pszCharset;
    bool m_bStrictLaundering = true;
    std::set<CPLString> m_oSetFields{};
    TABFeature *CreateTABFeature(OGRFeature *poFeature);

  public:
    IMapInfoFile(GDALDataset *poDS);
    ~IMapInfoFile() override;

    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(GDALDataset *poDS, const char *pszFname,
                                   GBool bUpdate = FALSE,
                                   GBool bTestOpenNoError = FALSE);

    ///////////////
    //  OGR methods for read support
    void ResetReading() override = 0;
    GIntBig GetFeatureCount(int bForce) override = 0;
    OGRFeature *GetNextFeature() override;
    OGRFeature *GetFeature(GIntBig nFeatureId) override;
    OGRErr ICreateFeature(OGRFeature *poFeature) override;
    int TestCapability(const char *pszCap) const override = 0;
    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
                      bool bForce) override = 0;

    GDALDataset *GetDataset() override
    {
        return m_poDS;
    }

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

    virtual TABFieldType GetNativeFieldType(int nFieldId) = 0;

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

    const OGRSpatialReference *GetSpatialRef() const 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(const 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;
    virtual void SetStrictLaundering(bool);
    int TestUtf8Capability() const;
    CPLString NormalizeFieldName(const char *pszName) 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(const 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;
    mutable 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:
    explicit TABFile(GDALDataset *poDS);
    ~TABFile() override;

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

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

    int Close() override;

    virtual int
    SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode = TRUE) override;

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

    void ResetReading() override;
    int TestCapability(const char *pszCap) const override;
    GIntBig GetFeatureCount(int bForce) override;
    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
                      bool bForce) override;

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

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

    OGRErr SyncToDisk() override;

    CPLErr SetMetadataItem(const char *pszName, const char *pszValue,
                           const char *pszDomain = "") override;

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

    int GetNextFeatureId_Spatial(int nPrevId);

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

    TABFieldType GetNativeFieldType(int nFieldId) override;

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

    const OGRSpatialReference *GetSpatialRef() const override;

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

    GBool IsFieldIndexed(int nFieldId) override;

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

    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;
    int SetSpatialRef(OGRSpatialReference *poSpatialRef) override;

    OGRErr CreateFeature(TABFeature *poFeature) override;

    int SetFieldIndexed(int nFieldId) override;

    ///////////////
    // semi-private.
    int GetProjInfo(TABProjInfo *poPI) override
    {
        return m_poMAPFile->GetHeaderBlock()->GetProjInfo(poPI);
    }

    int SetProjInfo(TABProjInfo *poPI) override;
    int SetMIFCoordSys(const char *pszMIFCoordSys) override;

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

    TABMAPFile *GetMAPFileRef()
    {
        return m_poMAPFile;
    }

    int WriteFeature(TABFeature *poFeature);
    int SetCharset(const char *pszCharset) override;
    void SetStrictLaundering(bool bStrictLaundering) override;
#ifdef DEBUG
    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:
    explicit TABView(GDALDataset *poDS);
    ~TABView() override;

    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;
    int Close() override;

    virtual int
    SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode = TRUE) override;

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

    void ResetReading() override;
    int TestCapability(const char *pszCap) const override;
    GIntBig GetFeatureCount(int bForce) override;
    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
                      bool bForce) override;

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

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

    TABFieldType GetNativeFieldType(int nFieldId) override;

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

    const OGRSpatialReference *GetSpatialRef() const override;

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

    GBool IsFieldIndexed(int nFieldId) override;
    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;
    int SetSpatialRef(OGRSpatialReference *poSpatialRef) override;

    OGRErr CreateFeature(TABFeature *poFeature) override;

    int SetFieldIndexed(int nFieldId) override;

    ///////////////
    // semi-private.
    int GetProjInfo(TABProjInfo *poPI) override
    {
        return m_nMainTableIndex != -1
                   ? m_papoTABFiles[m_nMainTableIndex]->GetProjInfo(poPI)
                   : -1;
    }

    int SetProjInfo(TABProjInfo *poPI) override
    {
        return m_nMainTableIndex != -1
                   ? m_papoTABFiles[m_nMainTableIndex]->SetProjInfo(poPI)
                   : -1;
    }

    int SetMIFCoordSys(const char * /*pszMIFCoordSys*/) override
    {
        return -1;
    }

    int SetCharset(const char *pszCharset) override;

#ifdef DEBUG
    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:
    explicit TABSeamless(GDALDataset *poDS);
    ~TABSeamless() override;

    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;
    int Close() override;

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

    virtual OGRErr ISetSpatialFilter(int iGeomField,
                                     const OGRGeometry *poGeom) override;

    void ResetReading() override;
    int TestCapability(const char *pszCap) const override;
    GIntBig GetFeatureCount(int bForce) override;
    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
                      bool bForce) override;

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

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

    TABFieldType GetNativeFieldType(int nFieldId) override;

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

    const OGRSpatialReference *GetSpatialRef() const override;

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

    GBool IsFieldIndexed(int nFieldId) override;
    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;
    }

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

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

    ///////////////
    // semi-private.
    int GetProjInfo(TABProjInfo *poPI) override
    {
        return m_poIndexTable ? m_poIndexTable->GetProjInfo(poPI) : -1;
    }

    int SetProjInfo(CPL_UNUSED TABProjInfo *poPI) override
    {
        return -1;
    }

    int SetMIFCoordSys(const char * /*pszMIFCoordSys*/) override
    {
        return -1;
    }

#ifdef DEBUG
    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;
    mutable 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);

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

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

  public:
    explicit MIFFile(GDALDataset *poDS);
    ~MIFFile() override;

    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;
    int Close() override;

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

    int TestCapability(const char *pszCap) const override;
    GIntBig GetFeatureCount(int bForce) override;
    void ResetReading() override;
    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
                      bool bForce) override;

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

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

    TABFieldType GetNativeFieldType(int nFieldId) override;

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

    const OGRSpatialReference *GetSpatialRef() const override;

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

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

    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 */
    int SetSpatialRef(OGRSpatialReference *poSpatialRef) override;

    OGRErr CreateFeature(TABFeature *poFeature) override;

    int SetFieldIndexed(int nFieldId) override;

    ///////////////
    // semi-private.
    int GetProjInfo(TABProjInfo * /*poPI*/) override
    {
        return -1;
    }

    /*  { return m_poMAPFile->GetHeaderBlock()->GetProjInfo( poPI ); }*/
    int SetProjInfo(TABProjInfo * /*poPI*/) override
    {
        return -1;
    }

    /*  { return m_poMAPFile->GetHeaderBlock()->SetProjInfo( poPI ); }*/
    int SetMIFCoordSys(const char *pszMIFCoordSys) override;
    int SetCharset(const char *pszCharset) override;
    void SetStrictLaundering(bool bStrictLaundering) override;
#ifdef DEBUG
    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 /* non final */
{
  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 /* non final */
{
  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 /* non final */
{
  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 /* non final */
{
  protected:
    int m_nSymbolDefIndex;
    TABSymbolDef m_sSymbolDef;

  public:
    ITABFeatureSymbol();

    virtual ~ITABFeatureSymbol() = default;

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

    static TABFeatureClass GetSymbolFeatureClass(const char *pszStyleString);
    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 /* non final*/ : 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(const OGRFeatureDefn *poDefnIn);
    ~TABFeature() override;

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

    virtual TABFeature *
    CloneTABFeature(const 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(const OGRFeatureDefn *poDefnIn);
    ~TABPoint() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCPoint;
    }

    virtual TABGeomType
    ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    const char *GetStyleString() const override;

    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(const OGRFeatureDefn *poDefnIn);
    ~TABFontPoint() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCFontPoint;
    }

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    virtual const char *
    GetSymbolStyleString(double dfAngle = 0.0) const override;
    const char *GetStyleString() const override;
    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(const OGRFeatureDefn *poDefnIn);
    ~TABCustomPoint() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCCustomPoint;
    }

    virtual TABFeature *
    CloneTABFeature(const 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;

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

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

    const char *GetSymbolNameRef() const
    {
        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(const OGRFeatureDefn *poDefnIn);
    ~TABPolyline() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCPolyline;
    }

    virtual TABGeomType
    ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    const char *GetStyleString() const override;

    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(const OGRFeatureDefn *poDefnIn);
    ~TABRegion() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCRegion;
    }

    virtual TABGeomType
    ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    const char *GetStyleString() const override;

    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:
    int UpdateMBR(TABMAPFile *poMapFile = nullptr) override;

  public:
    explicit TABRectangle(const OGRFeatureDefn *poDefnIn);
    ~TABRectangle() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCRectangle;
    }

    virtual TABGeomType
    ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    const char *GetStyleString() const override;

    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:
    int UpdateMBR(TABMAPFile *poMapFile = nullptr) override;

  public:
    explicit TABEllipse(const OGRFeatureDefn *poDefnIn);
    ~TABEllipse() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCEllipse;
    }

    virtual TABGeomType
    ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    const char *GetStyleString() const override;

    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

    int UpdateMBR(TABMAPFile *poMapFile = nullptr) override;

  public:
    explicit TABArc(const OGRFeatureDefn *poDefnIn);
    ~TABArc() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCArc;
    }

    virtual TABGeomType
    ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    const char *GetStyleString() const override;

    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;

    int UpdateMBR(TABMAPFile *poMapFile = nullptr) override;

  public:
    explicit TABText(const OGRFeatureDefn *poDefnIn);
    ~TABText() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCText;
    }

    virtual TABGeomType
    ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    const char *GetStyleString() const override;

    void DumpMIF(FILE *fpOut = nullptr) override;

    void SetLabelFromStyleString(const char *pszStyleString);

    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(const OGRFeatureDefn *poDefnIn);
    ~TABMultiPoint() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCMultiPoint;
    }

    virtual TABGeomType
    ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    const char *GetStyleString() const override;

    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(const OGRFeatureDefn *poDefnIn);
    ~TABCollection() override;

    TABFeatureClass GetFeatureClass() override
    {
        return TABFCCollection;
    }

    virtual TABGeomType
    ValidateMapInfoType(TABMAPFile *poMapFile = nullptr) override;

    virtual TABFeature *
    CloneTABFeature(const 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;

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

    const char *GetStyleString() const override;

    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(const OGRFeatureDefn *poDefnIn);
    ~TABDebugFeature() override;

    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;

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

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

#endif /* MITAB_H_INCLUDED_ */
