/*-*-C++-*-*/
/******************************************************************************
 * $Id$
 *
 * Project:  TIGER/Line Translator
 * Purpose:  Main declarations for Tiger translator.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 1999, Frank Warmerdam
 * Copyright (c) 2008-2011, Even Rouault <even dot 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 OGR_TIGER_H_INCLUDED
#define OGR_TIGER_H_INCLUDED

#include "cpl_conv.h"
#include "ogrsf_frmts.h"

class OGRTigerDataSource;

/*
** TIGER Versions
**
** 0000           TIGER/Line Precensus Files, 1990
** 0002           TIGER/Line Initial Voting District Codes Files, 1990
** 0003           TIGER/Line Files, 1990
** 0005           TIGER/Line Files, 1992
** 0021           TIGER/Line Files, 1994
** 0024           TIGER/Line Files, 1995
** 0697 to 1098   TIGER/Line Files, 1997
** 1298 to 0499   TIGER/Line Files, 1998
** 0600 to 0800   TIGER/Line Files, 1999
** 1000 to 1100   TIGER/Line Files, Redistricting Census 2000
** 0301 to 0801   TIGER/Line Files, Census 2000
**
** 0302 to 0502   TIGER/Line Files, UA 2000
** ????    ????
**
** 0602  & higher TIGER/Line Files, 2002
** ????    ????
*/

typedef enum
{
    TIGER_1990_Precensus = 0,
    TIGER_1990 = 1,
    TIGER_1992 = 2,
    TIGER_1994 = 3,
    TIGER_1995 = 4,
    TIGER_1997 = 5,
    TIGER_1998 = 6,
    TIGER_1999 = 7,
    TIGER_2000_Redistricting = 8,
    TIGER_2000_Census = 9,
    TIGER_UA2000 = 10,
    TIGER_2002 = 11,
    TIGER_2003 = 12,
    TIGER_2004 = 13,
    TIGER_Unknown
} TigerVersion;

TigerVersion TigerClassifyVersion(int);
const char *TigerVersionString(TigerVersion);

/*****************************************************************************/
/* The TigerFieldInfo and TigerRecordInfo structures hold information about  */
/* the schema of a TIGER record type.  In each layer implementation file     */
/* there are statically initialized variables of these types that describe    */
/* the record types associated with that layer.  In the case where different */
/* TIGER versions have different schemas, there is a                         */
/* TigerFieldInfo/TigerRecordInfo for each version, and the constructor      */
/* for the layer chooses a pointer to the correct set based on the version.  */
/*****************************************************************************/

typedef struct TigerFieldInfo
{
    char pszFieldName[11];  // name of the field
    char cFmt;              // format of the field ('L' or 'R')
    char cType;             // type of the field ('A' or 'N')
    char OGRtype;        // OFTType of the field (OFTInteger, OFTString, ...?)
    unsigned char nBeg;  // beginning column number for field
    unsigned char nEnd;  // ending column number for field
    unsigned char nLen;  // length of field

    int bDefine : 1;  // whether to add this field to the FeatureDefn
    int bSet : 1;     // whether to set this field in GetFeature()
} TigerFieldInfo;

typedef struct TigerRecordInfo
{
    const TigerFieldInfo *pasFields;
    unsigned char nFieldCount;
    unsigned char nRecordLength;
} TigerRecordInfo;

// OGR_TIGER_RECBUF_LEN should be a number that is larger than the
// longest possible record length for any record type; it is used to
// create arrays to hold the records.  At the time of this writing the
// longest record (RT1) has length 228, but I'm choosing 500 because
// it is a good round number and will allow for growth without having
// to modify this file.  The code never holds more than a few records
// in memory at a time, so having OGR_TIGER_RECBUF_LEN be much larger
// than is really necessary won't affect the amount of memory required
// in a substantial way.
// mbp Fri Dec 20 19:19:59 2002
// Note: OGR_TIGER_RECBUF_LEN should also be larger than 255, since
// TigerRecordInfo::nRecordLength fits on unsigned char.
#define OGR_TIGER_RECBUF_LEN 500

/************************************************************************/
/*                            TigerFileBase                             */
/************************************************************************/

class TigerFileBase CPL_NON_FINAL
{
  protected:
    OGRTigerDataSource *poDS;

    char *pszModule;
    char *pszShortModule;
    VSILFILE *fpPrimary;

    OGRFeatureDefn *poFeatureDefn;

    int nFeatures;
    int nRecordLength;

    int OpenFile(const char *, const char *);
    void EstablishFeatureCount();

    static int EstablishRecordLength(VSILFILE *);

    void SetupVersion();

    int nVersionCode;
    TigerVersion nVersion;

  public:
    explicit TigerFileBase(const TigerRecordInfo *psRTInfoIn = nullptr,
                           const char *m_pszFileCodeIn = nullptr);
    virtual ~TigerFileBase();

    TigerVersion GetVersion()
    {
        return nVersion;
    }
    int GetVersionCode()
    {
        return nVersionCode;
    }

    virtual const char *GetShortModule()
    {
        return pszShortModule;
    }
    virtual const char *GetModule()
    {
        return pszModule;
    }

    virtual int GetFeatureCount()
    {
        return nFeatures;
    }

    OGRFeatureDefn *GetFeatureDefn()
    {
        return poFeatureDefn;
    }

    static const char *GetField(const char *, int, int);
    static void SetField(OGRFeature *, const char *, const char *, int, int);

    virtual bool SetModule(const char *pszModule);
    virtual OGRFeature *GetFeature(int nRecordId);

  protected:
    static void AddFieldDefns(const TigerRecordInfo *psRTInfo,
                              OGRFeatureDefn *poFeatureDefn);

    static void SetFields(const TigerRecordInfo *psRTInfo,
                          OGRFeature *poFeature, char *achRecord);

    const TigerRecordInfo *psRTInfo;
    const char *m_pszFileCode;
};

/************************************************************************/
/*                          TigerCompleteChain                          */
/************************************************************************/

class TigerCompleteChain final : public TigerFileBase
{
    VSILFILE *fpShape;
    int *panShapeRecordId;

    VSILFILE *fpRT3;
    bool bUsingRT3;
    int nRT1RecOffset;

    int GetShapeRecordId(int, int);
    bool AddShapePoints(int, int, OGRLineString *, int);

    void AddFieldDefnsPre2002();
    OGRFeature *GetFeaturePre2002(int);

    OGRFeature *GetFeature2002(int);
    void AddFieldDefns2002();

    const TigerRecordInfo *psRT1Info;
    const TigerRecordInfo *psRT2Info;
    const TigerRecordInfo *psRT3Info;

  public:
    TigerCompleteChain(OGRTigerDataSource *, const char *);
    virtual ~TigerCompleteChain();

    virtual bool SetModule(const char *) override;

    virtual OGRFeature *GetFeature(int) override;
};

/************************************************************************/
/*                    TigerAltName (Type 4 records)                     */
/************************************************************************/

class TigerAltName final : public TigerFileBase
{
  public:
    TigerAltName(OGRTigerDataSource *, const char *);

    virtual OGRFeature *GetFeature(int) override;
};

/************************************************************************/
/*                    TigerFeatureIds (Type 5 records)                  */
/************************************************************************/

class TigerFeatureIds final : public TigerFileBase
{
  public:
    TigerFeatureIds(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                    TigerZipCodes (Type 6 records)                    */
/************************************************************************/

class TigerZipCodes final : public TigerFileBase
{
  public:
    TigerZipCodes(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                    TigerPoint                                        */
/* This is an abstract base class for TIGER layers with point geometry. */
/* Since much of the implementation of these layers is similar, I've    */
/* put it into this base class to avoid duplication in the actual       */
/* layer classes.  mbp Sat Jan  4 16:41:19 2003.                        */
/************************************************************************/

class TigerPoint CPL_NON_FINAL : public TigerFileBase
{
  protected:
    explicit TigerPoint(const TigerRecordInfo *psRTInfoIn = nullptr,
                        const char *m_pszFileCodeIn = nullptr);

  public:
    virtual OGRFeature *GetFeature(int nFID) override
    {
        return TigerFileBase::GetFeature(nFID);
    } /* to avoid -Woverloaded-virtual warnings */
    OGRFeature *GetFeature(int nRecordId, int nX0, int nX1, int nY0, int nY1);
};

/************************************************************************/
/*                   TigerLandmarks (Type 7 records)                    */
/************************************************************************/

class TigerLandmarks final : public TigerPoint
{
  public:
    TigerLandmarks(OGRTigerDataSource *, const char *);

    virtual OGRFeature *GetFeature(int) override;
};

/************************************************************************/
/*                   TigerAreaLandmarks (Type 8 records)                */
/************************************************************************/

class TigerAreaLandmarks final : public TigerFileBase
{
  public:
    TigerAreaLandmarks(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                   TigerKeyFeatures (Type 9 records)                  */
/************************************************************************/

class TigerKeyFeatures final : public TigerFileBase
{
  public:
    TigerKeyFeatures(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                   TigerPolygon (Type A&S records)                    */
/************************************************************************/

class TigerPolygon final : public TigerFileBase
{
  private:
    const TigerRecordInfo *psRTAInfo;
    const TigerRecordInfo *psRTSInfo;

    VSILFILE *fpRTS;
    bool bUsingRTS;
    int nRTSRecLen;

  public:
    TigerPolygon(OGRTigerDataSource *, const char *);
    virtual ~TigerPolygon();

    virtual bool SetModule(const char *) override;

    virtual OGRFeature *GetFeature(int) override;
};

/************************************************************************/
/*                    TigerPolygonCorrections (Type B records)          */
/************************************************************************/

class TigerPolygonCorrections final : public TigerFileBase
{
  public:
    TigerPolygonCorrections(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                  TigerEntityNames (Type C records)                   */
/************************************************************************/

class TigerEntityNames final : public TigerFileBase
{
  public:
    TigerEntityNames(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                    TigerPolygonEconomic (Type E records)             */
/************************************************************************/

class TigerPolygonEconomic final : public TigerFileBase
{
  public:
    TigerPolygonEconomic(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                  TigerIDHistory (Type H records)                     */
/************************************************************************/

class TigerIDHistory final : public TigerFileBase
{
  public:
    TigerIDHistory(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                   TigerPolyChainLink (Type I records)                */
/************************************************************************/

class TigerPolyChainLink final : public TigerFileBase
{
  public:
    TigerPolyChainLink(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                TigerSpatialMetadata (Type M records)                 */
/************************************************************************/

class TigerSpatialMetadata final : public TigerFileBase
{
  public:
    TigerSpatialMetadata(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                   TigerPIP (Type P records)                          */
/************************************************************************/

class TigerPIP final : public TigerPoint
{
  public:
    TigerPIP(OGRTigerDataSource *, const char *);

    virtual OGRFeature *GetFeature(int) override;
};

/************************************************************************/
/*                   TigerTLIDRange (Type R records)                    */
/************************************************************************/

class TigerTLIDRange final : public TigerFileBase
{
  public:
    TigerTLIDRange(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                    TigerZeroCellID (Type T records)                  */
/************************************************************************/

class TigerZeroCellID final : public TigerFileBase
{
  public:
    TigerZeroCellID(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                    TigerOverUnder (Type U records)                   */
/************************************************************************/

class TigerOverUnder final : public TigerPoint
{
  public:
    TigerOverUnder(OGRTigerDataSource *, const char *);

    virtual OGRFeature *GetFeature(int) override;
};

/************************************************************************/
/*                    TigerZipPlus4 (Type Z records)                    */
/************************************************************************/

class TigerZipPlus4 final : public TigerFileBase
{
  public:
    TigerZipPlus4(OGRTigerDataSource *, const char *);
};

/************************************************************************/
/*                            OGRTigerLayer                             */
/************************************************************************/

class OGRTigerLayer final : public OGRLayer
{
    TigerFileBase *poReader;

    OGRTigerDataSource *poDS;

    int nFeatureCount;
    int *panModuleFCount;
    int *panModuleOffset;

    int iLastFeatureId;
    int iLastModule;

  public:
    OGRTigerLayer(OGRTigerDataSource *poDS, TigerFileBase *);
    virtual ~OGRTigerLayer();

    void ResetReading() override;
    OGRFeature *GetNextFeature() override;
    OGRFeature *GetFeature(GIntBig nFeatureId) override;

    OGRFeatureDefn *GetLayerDefn() override;

    GIntBig GetFeatureCount(int) override;

    int TestCapability(const char *) override;
};

/************************************************************************/
/*                          OGRTigerDataSource                          */
/************************************************************************/

class OGRTigerDataSource final : public OGRDataSource
{
    char *pszName;

    int nLayers;
    OGRTigerLayer **papoLayers;

    OGRSpatialReference *poSpatialRef;

    char **papszOptions;

    char *pszPath;

    int nModules;
    char **papszModules;

    int nVersionCode;
    TigerVersion nVersion;

    TigerVersion TigerCheckVersion(TigerVersion, const char *);

    CPL_DISALLOW_COPY_ASSIGN(OGRTigerDataSource)

  public:
    OGRTigerDataSource();
    virtual ~OGRTigerDataSource();

    TigerVersion GetVersion() const
    {
        return nVersion;
    }
    int GetVersionCode() const
    {
        return nVersionCode;
    }

    const char *GetOption(const char *);

    int Open(const char *pszName, int bTestOpen = FALSE,
             char **papszFileList = nullptr);

    const char *GetName() override
    {
        return pszName;
    }
    int GetLayerCount() override;
    OGRLayer *GetLayer(int) override;
    OGRLayer *GetLayer(const char *pszLayerName);

    void AddLayer(OGRTigerLayer *);
    int TestCapability(const char *) override;

    OGRSpatialReference *DSGetSpatialRef()
    {
        return poSpatialRef;
    }

    const char *GetDirPath()
    {
        return pszPath;
    }
    char *BuildFilename(const char *pszModule, const char *pszExtension);

    int GetModuleCount() const
    {
        return nModules;
    }
    const char *GetModule(int);
    bool CheckModule(const char *pszModule);
    void AddModule(const char *pszModule);
};

#endif /* ndef OGR_TIGER_H_INCLUDED */
