/******************************************************************************
 * $Id$
 *
 * Project:  OpenGIS Simple Features Reference Implementation
 * Purpose:  Defines GeoJSON reader within OGR OGRGeoJSON Driver.
 * Author:   Mateusz Loskot, mateusz@loskot.net
 *
 ******************************************************************************
 * Copyright (c) 2007, Mateusz Loskot
 * Copyright (c) 2010-2013, 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_GEOJSONREADER_H_INCLUDED
#define OGR_GEOJSONREADER_H_INCLUDED

#include "cpl_json_header.h"
#include "cpl_string.h"
#include "ogr_core.h"
#include "ogrsf_frmts.h"

#include "ogrgeojsonutils.h"
#include "directedacyclicgraph.hpp"

#include <utility>
#include <map>
#include <set>
#include <vector>

/************************************************************************/
/*                         FORWARD DECLARATIONS                         */
/************************************************************************/

class OGRGeometry;
class OGRRawPoint;
class OGRPoint;
class OGRMultiPoint;
class OGRLineString;
class OGRMultiLineString;
class OGRLinearRing;
class OGRPolygon;
class OGRMultiPolygon;
class OGRGeometryCollection;
class OGRFeature;
class OGRGeoJSONLayer;
class OGRSpatialReference;

/************************************************************************/
/*                           GeoJSONObject                              */
/************************************************************************/

struct GeoJSONObject
{
    enum Type
    {
        eUnknown = wkbUnknown,  // non-GeoJSON properties
        ePoint = wkbPoint,
        eLineString = wkbLineString,
        ePolygon = wkbPolygon,
        eMultiPoint = wkbMultiPoint,
        eMultiLineString = wkbMultiLineString,
        eMultiPolygon = wkbMultiPolygon,
        eGeometryCollection = wkbGeometryCollection,
        eFeature,
        eFeatureCollection
    };

    enum CoordinateDimension
    {
        eMinCoordinateDimension = 2,
        eMaxCoordinateDimension = 3
    };
};

/************************************************************************/
/*                        OGRGeoJSONBaseReader                          */
/************************************************************************/

class OGRGeoJSONBaseReader
{
  public:
    OGRGeoJSONBaseReader();

    void SetPreserveGeometryType(bool bPreserve);
    void SetSkipAttributes(bool bSkip);
    void SetFlattenNestedAttributes(bool bFlatten, char chSeparator);
    void SetStoreNativeData(bool bStoreNativeData);
    void SetArrayAsString(bool bArrayAsString);
    void SetDateAsString(bool bDateAsString);

    bool GenerateFeatureDefn(
        std::map<std::string, int> &oMapFieldNameToIdx,
        std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
        gdal::DirectedAcyclicGraph<int, std::string> &dag, OGRLayer *poLayer,
        json_object *poObj);
    void FinalizeLayerDefn(OGRLayer *poLayer, CPLString &osFIDColumn);

    OGRGeometry *ReadGeometry(json_object *poObj,
                              OGRSpatialReference *poLayerSRS);
    OGRFeature *ReadFeature(OGRLayer *poLayer, json_object *poObj,
                            const char *pszSerializedObj);

    bool ExtentRead() const;

    OGREnvelope3D GetExtent3D() const;

  protected:
    bool bGeometryPreserve_ = true;
    bool bAttributesSkip_ = false;
    bool bFlattenNestedAttributes_ = false;
    char chNestedAttributeSeparator_ = 0;
    bool bStoreNativeData_ = false;
    bool bArrayAsString_ = false;
    bool bDateAsString_ = false;

  private:
    std::set<int> aoSetUndeterminedTypeFields_;

    // bFlatten... is a tri-state boolean with -1 being unset.
    int bFlattenGeocouchSpatiallistFormat = -1;

    bool bFoundGeocouchId = false;
    bool bFoundRev = false;
    bool bFoundTypeFeature = false;
    bool bIsGeocouchSpatiallistFormat = false;
    bool bFeatureLevelIdAsAttribute_ = false;
    bool bFeatureLevelIdAsFID_ = false;
    bool m_bNeedFID64 = false;

    bool m_bFirstGeometry = true;
    OGREnvelope3D m_oEnvelope3D;
    // Becomes true when extent has been read from data
    bool m_bExtentRead = false;
    OGRwkbGeometryType m_eLayerGeomType = wkbUnknown;

    CPL_DISALLOW_COPY_ASSIGN(OGRGeoJSONBaseReader)
};

/************************************************************************/
/*                           OGRGeoJSONReader                           */
/************************************************************************/

class OGRGeoJSONDataSource;
class OGRGeoJSONReaderStreamingParser;

class OGRGeoJSONReader : public OGRGeoJSONBaseReader
{
  public:
    OGRGeoJSONReader();
    ~OGRGeoJSONReader();

    OGRErr Parse(const char *pszText);
    void ReadLayers(OGRGeoJSONDataSource *poDS);
    void ReadLayer(OGRGeoJSONDataSource *poDS, const char *pszName,
                   json_object *poObj);
    bool FirstPassReadLayer(OGRGeoJSONDataSource *poDS, VSILFILE *fp,
                            bool &bTryStandardReading);

    json_object *GetJSonObject()
    {
        return poGJObject_;
    }

    void ResetReading();
    OGRFeature *GetNextFeature(OGRGeoJSONLayer *poLayer);
    OGRFeature *GetFeature(OGRGeoJSONLayer *poLayer, GIntBig nFID);
    bool IngestAll(OGRGeoJSONLayer *poLayer);

    VSILFILE *GetFP()
    {
        return fp_;
    }

    bool CanEasilyAppend() const
    {
        return bCanEasilyAppend_;
    }

    bool FCHasBBOX() const
    {
        return bFCHasBBOX_;
    }

  private:
    friend class OGRGeoJSONReaderStreamingParser;

    json_object *poGJObject_;
    OGRGeoJSONReaderStreamingParser *poStreamingParser_;
    bool bFirstSeg_;
    bool bJSonPLikeWrapper_;
    VSILFILE *fp_;
    bool bCanEasilyAppend_;
    bool bFCHasBBOX_;
    bool bOriginalIdModifiedEmitted_ = false;

    size_t nBufferSize_;
    GByte *pabyBuffer_;

    GIntBig nTotalFeatureCount_;
    GUIntBig nTotalOGRFeatureMemEstimate_;

    std::map<GIntBig, std::pair<vsi_l_offset, vsi_l_offset>>
        oMapFIDToOffsetSize_;
    //
    // Copy operations not supported.
    //
    CPL_DISALLOW_COPY_ASSIGN(OGRGeoJSONReader)

    //
    // Translation utilities.
    //
    bool GenerateLayerDefn(OGRGeoJSONLayer *poLayer, json_object *poGJObject);

    static bool AddFeature(OGRGeoJSONLayer *poLayer, OGRGeometry *poGeometry);
    static bool AddFeature(OGRGeoJSONLayer *poLayer, OGRFeature *poFeature);

    void ReadFeatureCollection(OGRGeoJSONLayer *poLayer, json_object *poObj);
    size_t SkipPrologEpilogAndUpdateJSonPLikeWrapper(size_t nRead);
};

void OGRGeoJSONGenerateFeatureDefnDealWithID(
    json_object *poObj, json_object *poObjProps, int &nPrevFieldIdx,
    std::map<std::string, int> &oMapFieldNameToIdx,
    std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
    gdal::DirectedAcyclicGraph<int, std::string> &dag,
    bool &bFeatureLevelIdAsFID, bool &bFeatureLevelIdAsAttribute,
    bool &bNeedFID64);

void OGRGeoJSONReaderSetField(OGRLayer *poLayer, OGRFeature *poFeature,
                              int nField, const char *pszAttrPrefix,
                              json_object *poVal, bool bFlattenNestedAttributes,
                              char chNestedAttributeSeparator);
void OGRGeoJSONReaderAddOrUpdateField(
    std::vector<int> &retIndices,
    std::map<std::string, int> &oMapFieldNameToIdx,
    std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
    const char *pszKey, json_object *poVal, bool bFlattenNestedAttributes,
    char chNestedAttributeSeparator, bool bArrayAsString, bool bDateAsString,
    std::set<int> &aoSetUndeterminedTypeFields);

/************************************************************************/
/*                 GeoJSON Parsing Utilities                            */
/************************************************************************/

lh_entry *OGRGeoJSONFindMemberEntryByName(json_object *poObj,
                                          const char *pszName);
json_object *OGRGeoJSONFindMemberByName(json_object *poObj,
                                        const char *pszName);
GeoJSONObject::Type OGRGeoJSONGetType(json_object *poObj);

json_object CPL_DLL *json_ex_get_object_by_path(json_object *poObj,
                                                const char *pszPath);

json_object CPL_DLL *CPL_json_object_object_get(struct json_object *obj,
                                                const char *key);

bool CPL_DLL OGRJSonParse(const char *pszText, json_object **ppoObj,
                          bool bVerboseError = true);

bool OGRGeoJSONUpdateLayerGeomType(bool &bFirstGeom,
                                   OGRwkbGeometryType eGeomType,
                                   OGRwkbGeometryType &eLayerGeomType);

// Get the 3D extent from the geometry coordinates of a feature
bool OGRGeoJSONGetExtent3D(json_object *poObj, OGREnvelope3D *poEnvelope);

/************************************************************************/
/*                 GeoJSON Geometry Translators                         */
/************************************************************************/

OGRwkbGeometryType OGRGeoJSONGetOGRGeometryType(json_object *poObj);

bool OGRGeoJSONReadRawPoint(json_object *poObj, OGRPoint &point);
OGRGeometry CPL_DLL *
OGRGeoJSONReadGeometry(json_object *poObj,
                       OGRSpatialReference *poParentSRS = nullptr);
OGRPoint *OGRGeoJSONReadPoint(json_object *poObj);
OGRMultiPoint *OGRGeoJSONReadMultiPoint(json_object *poObj);
OGRLineString *OGRGeoJSONReadLineString(json_object *poObj, bool bRaw = false);
OGRMultiLineString *OGRGeoJSONReadMultiLineString(json_object *poObj);
OGRLinearRing *OGRGeoJSONReadLinearRing(json_object *poObj);
OGRPolygon *OGRGeoJSONReadPolygon(json_object *poObj, bool bRaw = false);
OGRMultiPolygon *OGRGeoJSONReadMultiPolygon(json_object *poObj);
OGRGeometryCollection *
OGRGeoJSONReadGeometryCollection(json_object *poObj,
                                 OGRSpatialReference *poSRS = nullptr);
OGRSpatialReference *OGRGeoJSONReadSpatialReference(json_object *poObj);

/************************************************************************/
/*                          OGRESRIJSONReader                           */
/************************************************************************/

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

    OGRErr Parse(const char *pszText);
    void ReadLayers(OGRGeoJSONDataSource *poDS, GeoJSONSourceType eSourceType);

    json_object *GetJSonObject()
    {
        return poGJObject_;
    }

  private:
    json_object *poGJObject_;
    OGRGeoJSONLayer *poLayer_;

    //
    // Copy operations not supported.
    //
    OGRESRIJSONReader(OGRESRIJSONReader const &);
    OGRESRIJSONReader &operator=(OGRESRIJSONReader const &);

    //
    // Translation utilities.
    //
    bool GenerateLayerDefn();
    bool ParseField(json_object *poObj);
    bool AddFeature(OGRFeature *poFeature);

    OGRFeature *ReadFeature(json_object *poObj);
    OGRGeoJSONLayer *ReadFeatureCollection(json_object *poObj);
};

OGRGeometry *OGRESRIJSONReadGeometry(json_object *poObj);
OGRSpatialReference *OGRESRIJSONReadSpatialReference(json_object *poObj);
OGRwkbGeometryType OGRESRIJSONGetGeometryType(json_object *poObj);
OGRPoint *OGRESRIJSONReadPoint(json_object *poObj);
OGRGeometry *OGRESRIJSONReadLineString(json_object *poObj);
OGRGeometry *OGRESRIJSONReadPolygon(json_object *poObj);
OGRMultiPoint *OGRESRIJSONReadMultiPoint(json_object *poObj);

/************************************************************************/
/*                          OGRTopoJSONReader                           */
/************************************************************************/

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

    OGRErr Parse(const char *pszText, bool bLooseIdentification);
    void ReadLayers(OGRGeoJSONDataSource *poDS);

  private:
    json_object *poGJObject_;

    //
    // Copy operations not supported.
    //
    OGRTopoJSONReader(OGRTopoJSONReader const &);
    OGRTopoJSONReader &operator=(OGRTopoJSONReader const &);
};

#endif /* OGR_GEOJSONUTILS_H_INCLUDED */
