/******************************************************************************
 *
 * Project:  OpenGIS Simple Features Reference Implementation
 * Purpose:  Defines OGRLayerPool and OGRProxiedLayer class
 * Author:   Even Rouault, even dot rouault at spatialys.com
 *
 ******************************************************************************
 * Copyright (c) 2012-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 DOXYGEN_SKIP

#include "ogrlayerpool.h"
#include "ogr_recordbatch.h"

/************************************************************************/
/*                      OGRAbstractProxiedLayer()                       */
/************************************************************************/

OGRAbstractProxiedLayer::OGRAbstractProxiedLayer(OGRLayerPool *poPoolIn)
    : poPrevLayer(nullptr), poNextLayer(nullptr), poPool(poPoolIn)
{
    CPLAssert(poPoolIn != nullptr);
}

/************************************************************************/
/*                     ~OGRAbstractProxiedLayer()                       */
/************************************************************************/

OGRAbstractProxiedLayer::~OGRAbstractProxiedLayer()
{
    /* Remove us from the list of LRU layers if necessary */
    poPool->UnchainLayer(this);
}

/************************************************************************/
/*                            OGRLayerPool()                            */
/************************************************************************/

OGRLayerPool::OGRLayerPool(int nMaxSimultaneouslyOpenedIn)
    : poMRULayer(nullptr), poLRULayer(nullptr), nMRUListSize(0),
      nMaxSimultaneouslyOpened(nMaxSimultaneouslyOpenedIn)
{
}

/************************************************************************/
/*                           ~OGRLayerPool()                            */
/************************************************************************/

OGRLayerPool::~OGRLayerPool()
{
    CPLAssert(poMRULayer == nullptr);
    CPLAssert(poLRULayer == nullptr);
    CPLAssert(nMRUListSize == 0);
}

/************************************************************************/
/*                          SetLastUsedLayer()                          */
/************************************************************************/

void OGRLayerPool::SetLastUsedLayer(OGRAbstractProxiedLayer *poLayer)
{
    /* If we are already the MRU layer, nothing to do */
    if (poLayer == poMRULayer)
        return;

    // CPLDebug("OGR", "SetLastUsedLayer(%s)", poLayer->GetName());

    if (poLayer->poPrevLayer != nullptr || poLayer->poNextLayer != nullptr)
    {
        /* Remove current layer from its current place in the list */
        UnchainLayer(poLayer);
    }
    else if (nMRUListSize == nMaxSimultaneouslyOpened)
    {
        /* If we have reached the maximum allowed number of layers */
        /* simultaneously opened, then close the LRU one that */
        /* was still active until now */
        CPLAssert(poLRULayer != nullptr);

        poLRULayer->CloseUnderlyingLayer();
        UnchainLayer(poLRULayer);
    }

    /* Put current layer on top of MRU list */
    CPLAssert(poLayer->poPrevLayer == nullptr);
    CPLAssert(poLayer->poNextLayer == nullptr);
    poLayer->poNextLayer = poMRULayer;
    if (poMRULayer != nullptr)
    {
        CPLAssert(poMRULayer->poPrevLayer == nullptr);
        poMRULayer->poPrevLayer = poLayer;
    }
    poMRULayer = poLayer;
    if (poLRULayer == nullptr)
        poLRULayer = poLayer;
    nMRUListSize++;
}

/************************************************************************/
/*                           UnchainLayer()                             */
/************************************************************************/

void OGRLayerPool::UnchainLayer(OGRAbstractProxiedLayer *poLayer)
{
    OGRAbstractProxiedLayer *poPrevLayer = poLayer->poPrevLayer;
    OGRAbstractProxiedLayer *poNextLayer = poLayer->poNextLayer;

    CPLAssert(poPrevLayer == nullptr || poPrevLayer->poNextLayer == poLayer);
    CPLAssert(poNextLayer == nullptr || poNextLayer->poPrevLayer == poLayer);

    if (poPrevLayer != nullptr || poNextLayer != nullptr ||
        poLayer == poMRULayer)
        nMRUListSize--;

    if (poLayer == poMRULayer)
        poMRULayer = poNextLayer;
    if (poLayer == poLRULayer)
        poLRULayer = poPrevLayer;
    if (poPrevLayer != nullptr)
        poPrevLayer->poNextLayer = poNextLayer;
    if (poNextLayer != nullptr)
        poNextLayer->poPrevLayer = poPrevLayer;
    poLayer->poPrevLayer = nullptr;
    poLayer->poNextLayer = nullptr;
}

/************************************************************************/
/*                          OGRProxiedLayer()                           */
/************************************************************************/

OGRProxiedLayer::OGRProxiedLayer(OGRLayerPool *poPoolIn,
                                 OpenLayerFunc pfnOpenLayerIn,
                                 FreeUserDataFunc pfnFreeUserDataIn,
                                 void *pUserDataIn)
    : OGRAbstractProxiedLayer(poPoolIn), pfnOpenLayer(pfnOpenLayerIn),
      pfnFreeUserData(pfnFreeUserDataIn), pUserData(pUserDataIn),
      poUnderlyingLayer(nullptr), poFeatureDefn(nullptr), poSRS(nullptr)
{
    CPLAssert(pfnOpenLayerIn != nullptr);
}

/************************************************************************/
/*                         ~OGRProxiedLayer()                           */
/************************************************************************/

OGRProxiedLayer::~OGRProxiedLayer()
{
    delete poUnderlyingLayer;

    if (poSRS)
        poSRS->Release();

    if (poFeatureDefn)
        poFeatureDefn->Release();

    if (pfnFreeUserData != nullptr)
        pfnFreeUserData(pUserData);
}

/************************************************************************/
/*                       OpenUnderlyingLayer()                          */
/************************************************************************/

int OGRProxiedLayer::OpenUnderlyingLayer()
{
    CPLDebug("OGR", "OpenUnderlyingLayer(%p)", this);
    CPLAssert(poUnderlyingLayer == nullptr);
    poPool->SetLastUsedLayer(this);
    poUnderlyingLayer = pfnOpenLayer(pUserData);
    if (poUnderlyingLayer == nullptr)
    {
        CPLError(CE_Failure, CPLE_FileIO, "Cannot open underlying layer");
    }
    return poUnderlyingLayer != nullptr;
}

/************************************************************************/
/*                         CloseUnderlyingLayer()                       */
/************************************************************************/

void OGRProxiedLayer::CloseUnderlyingLayer()
{
    CPLDebug("OGR", "CloseUnderlyingLayer(%p)", this);
    delete poUnderlyingLayer;
    poUnderlyingLayer = nullptr;
}

/************************************************************************/
/*                          GetUnderlyingLayer()                        */
/************************************************************************/

OGRLayer *OGRProxiedLayer::GetUnderlyingLayer()
{
    if (poUnderlyingLayer == nullptr)
    {
        //  If the open fails, poUnderlyingLayer will still be a nullptr
        // and the user will be warned by the open call.
        // coverity[check_return]
        OpenUnderlyingLayer();
    }
    return poUnderlyingLayer;
}

/************************************************************************/
/*                          GetSpatialFilter()                          */
/************************************************************************/

OGRGeometry *OGRProxiedLayer::GetSpatialFilter()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return nullptr;
    return poUnderlyingLayer->GetSpatialFilter();
}

/************************************************************************/
/*                          SetSpatialFilter()                          */
/************************************************************************/

void OGRProxiedLayer::SetSpatialFilter(OGRGeometry *poGeom)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return;
    poUnderlyingLayer->SetSpatialFilter(poGeom);
}

/************************************************************************/
/*                          SetSpatialFilter()                          */
/************************************************************************/

void OGRProxiedLayer::SetSpatialFilter(int iGeomField, OGRGeometry *poGeom)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return;
    poUnderlyingLayer->SetSpatialFilter(iGeomField, poGeom);
}
/************************************************************************/
/*                          SetAttributeFilter()                        */
/************************************************************************/

OGRErr OGRProxiedLayer::SetAttributeFilter(const char *poAttrFilter)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->SetAttributeFilter(poAttrFilter);
}

/************************************************************************/
/*                            ResetReading()                            */
/************************************************************************/

void OGRProxiedLayer::ResetReading()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return;
    poUnderlyingLayer->ResetReading();
}

/************************************************************************/
/*                           GetNextFeature()                           */
/************************************************************************/

OGRFeature *OGRProxiedLayer::GetNextFeature()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return nullptr;
    return poUnderlyingLayer->GetNextFeature();
}

/************************************************************************/
/*                            GDALDataset()                             */
/************************************************************************/

GDALDataset *OGRProxiedLayer::GetDataset()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return nullptr;
    return poUnderlyingLayer->GetDataset();
}

/************************************************************************/
/*                          GetArrowStream()                            */
/************************************************************************/

bool OGRProxiedLayer::GetArrowStream(struct ArrowArrayStream *out_stream,
                                     CSLConstList papszOptions)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
    {
        memset(out_stream, 0, sizeof(*out_stream));
        return false;
    }
    return poUnderlyingLayer->GetArrowStream(out_stream, papszOptions);
}

/************************************************************************/
/*                           SetNextByIndex()                           */
/************************************************************************/

OGRErr OGRProxiedLayer::SetNextByIndex(GIntBig nIndex)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->SetNextByIndex(nIndex);
}

/************************************************************************/
/*                             GetFeature()                             */
/************************************************************************/

OGRFeature *OGRProxiedLayer::GetFeature(GIntBig nFID)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return nullptr;
    return poUnderlyingLayer->GetFeature(nFID);
}

/************************************************************************/
/*                             ISetFeature()                             */
/************************************************************************/

OGRErr OGRProxiedLayer::ISetFeature(OGRFeature *poFeature)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->SetFeature(poFeature);
}

/************************************************************************/
/*                            ICreateFeature()                           */
/************************************************************************/

OGRErr OGRProxiedLayer::ICreateFeature(OGRFeature *poFeature)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->CreateFeature(poFeature);
}

/************************************************************************/
/*                            IUpsertFeature()                          */
/************************************************************************/

OGRErr OGRProxiedLayer::IUpsertFeature(OGRFeature *poFeature)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->UpsertFeature(poFeature);
}

/************************************************************************/
/*                           DeleteFeature()                            */
/************************************************************************/

OGRErr OGRProxiedLayer::DeleteFeature(GIntBig nFID)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->DeleteFeature(nFID);
}

/************************************************************************/
/*                             GetName()                                */
/************************************************************************/

const char *OGRProxiedLayer::GetName()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return "";
    return poUnderlyingLayer->GetName();
}

/************************************************************************/
/*                            GetGeomType()                             */
/************************************************************************/

OGRwkbGeometryType OGRProxiedLayer::GetGeomType()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return wkbUnknown;
    return poUnderlyingLayer->GetGeomType();
}

/************************************************************************/
/*                            GetLayerDefn()                            */
/************************************************************************/

OGRFeatureDefn *OGRProxiedLayer::GetLayerDefn()
{
    if (poFeatureDefn != nullptr)
        return poFeatureDefn;

    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
    {
        poFeatureDefn = new OGRFeatureDefn("");
    }
    else
    {
        poFeatureDefn = poUnderlyingLayer->GetLayerDefn();
    }

    poFeatureDefn->Reference();

    return poFeatureDefn;
}

/************************************************************************/
/*                            GetSpatialRef()                           */
/************************************************************************/

OGRSpatialReference *OGRProxiedLayer::GetSpatialRef()
{
    if (poSRS != nullptr)
        return poSRS;
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return nullptr;
    OGRSpatialReference *poRet = poUnderlyingLayer->GetSpatialRef();
    if (poRet != nullptr)
    {
        poSRS = poRet;
        poSRS->Reference();
    }
    return poRet;
}

/************************************************************************/
/*                          GetFeatureCount()                           */
/************************************************************************/

GIntBig OGRProxiedLayer::GetFeatureCount(int bForce)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return 0;
    return poUnderlyingLayer->GetFeatureCount(bForce);
}

/************************************************************************/
/*                             GetExtent()                              */
/************************************************************************/

OGRErr OGRProxiedLayer::GetExtent(int iGeomField, OGREnvelope *psExtent,
                                  int bForce)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->GetExtent(iGeomField, psExtent, bForce);
}

/************************************************************************/
/*                             GetExtent()                              */
/************************************************************************/

OGRErr OGRProxiedLayer::GetExtent(OGREnvelope *psExtent, int bForce)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->GetExtent(psExtent, bForce);
}

/************************************************************************/
/*                           TestCapability()                           */
/************************************************************************/

int OGRProxiedLayer::TestCapability(const char *pszCapability)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return FALSE;
    return poUnderlyingLayer->TestCapability(pszCapability);
}

/************************************************************************/
/*                            CreateField()                             */
/************************************************************************/

OGRErr OGRProxiedLayer::CreateField(OGRFieldDefn *poField, int bApproxOK)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->CreateField(poField, bApproxOK);
}

/************************************************************************/
/*                            DeleteField()                             */
/************************************************************************/

OGRErr OGRProxiedLayer::DeleteField(int iField)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->DeleteField(iField);
}

/************************************************************************/
/*                            ReorderFields()                           */
/************************************************************************/

OGRErr OGRProxiedLayer::ReorderFields(int *panMap)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->ReorderFields(panMap);
}

/************************************************************************/
/*                           AlterFieldDefn()                           */
/************************************************************************/

OGRErr OGRProxiedLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
                                       int nFlagsIn)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->AlterFieldDefn(iField, poNewFieldDefn, nFlagsIn);
}

/************************************************************************/
/*                         AlterGeomFieldDefn()                         */
/************************************************************************/

OGRErr OGRProxiedLayer::AlterGeomFieldDefn(
    int iGeomField, const OGRGeomFieldDefn *poNewGeomFieldDefn, int nFlagsIn)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->AlterGeomFieldDefn(iGeomField, poNewGeomFieldDefn,
                                                 nFlagsIn);
}

/************************************************************************/
/*                            SyncToDisk()                              */
/************************************************************************/

OGRErr OGRProxiedLayer::SyncToDisk()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->SyncToDisk();
}

/************************************************************************/
/*                           GetStyleTable()                            */
/************************************************************************/

OGRStyleTable *OGRProxiedLayer::GetStyleTable()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return nullptr;
    return poUnderlyingLayer->GetStyleTable();
}

/************************************************************************/
/*                       SetStyleTableDirectly()                        */
/************************************************************************/

void OGRProxiedLayer::SetStyleTableDirectly(OGRStyleTable *poStyleTable)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return;
    return poUnderlyingLayer->SetStyleTableDirectly(poStyleTable);
}

/************************************************************************/
/*                           SetStyleTable()                            */
/************************************************************************/

void OGRProxiedLayer::SetStyleTable(OGRStyleTable *poStyleTable)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return;
    return poUnderlyingLayer->SetStyleTable(poStyleTable);
}

/************************************************************************/
/*                          StartTransaction()                          */
/************************************************************************/

OGRErr OGRProxiedLayer::StartTransaction()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->StartTransaction();
}

/************************************************************************/
/*                          CommitTransaction()                         */
/************************************************************************/

OGRErr OGRProxiedLayer::CommitTransaction()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->CommitTransaction();
}

/************************************************************************/
/*                        RollbackTransaction()                         */
/************************************************************************/

OGRErr OGRProxiedLayer::RollbackTransaction()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->RollbackTransaction();
}

/************************************************************************/
/*                            GetFIDColumn()                            */
/************************************************************************/

const char *OGRProxiedLayer::GetFIDColumn()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return "";
    return poUnderlyingLayer->GetFIDColumn();
}

/************************************************************************/
/*                          GetGeometryColumn()                         */
/************************************************************************/

const char *OGRProxiedLayer::GetGeometryColumn()
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return "";
    return poUnderlyingLayer->GetGeometryColumn();
}

/************************************************************************/
/*                          SetIgnoredFields()                          */
/************************************************************************/

OGRErr OGRProxiedLayer::SetIgnoredFields(const char **papszFields)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->SetIgnoredFields(papszFields);
}

/************************************************************************/
/*                              Rename()                                */
/************************************************************************/

OGRErr OGRProxiedLayer::Rename(const char *pszNewName)
{
    if (poUnderlyingLayer == nullptr && !OpenUnderlyingLayer())
        return OGRERR_FAILURE;
    return poUnderlyingLayer->Rename(pszNewName);
}

#endif /* #ifndef DOXYGEN_SKIP */
