/******************************************************************************
 * $Id: ogr_sxflayer.cpp  $
 *
 * Project:  SXF Translator
 * Purpose:  Definition of classes for OGR SXF Layers.
 * Author:   Ben Ahmed Daho Ali, bidandou(at)yahoo(dot)fr
 *           Dmitry Baryshnikov, polimax@mail.ru
 *           Alexandr Lisovenko, alexander.lisovenko@gmail.com
 *
 ******************************************************************************
 * Copyright (c) 2011, Ben Ahmed Daho Ali
 * Copyright (c) 2013, NextGIS
 * Copyright (c) 2014, Even Rouault <even dot rouault at mines-paris dot org>
 *
 * 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.
 ****************************************************************************/
#define  _USE_MATH_DEFINES

#include "ogr_sxf.h"
#include "cpl_conv.h"
#include "cpl_string.h"
#include "ogr_p.h"
#include "ogr_srs_api.h"
#include "cpl_multiproc.h"

CPL_CVSID("$Id: ogrsxflayer.cpp $");

/************************************************************************/
/*                        OGRSXFLayer()                                 */
/************************************************************************/

OGRSXFLayer::OGRSXFLayer(VSILFILE* fp, void** hIOMutex, GByte nID, const char* pszLayerName, int nVer, const SXFMapDescription&  sxfMapDesc) : OGRLayer()
{
    sFIDColumn_ = "ogc_fid";
    fpSXF = fp;
    nLayerID = nID;
    stSXFMapDescription = sxfMapDesc;
    stSXFMapDescription.pSpatRef->Reference();
    m_nSXFFormatVer = nVer;
    oNextIt = mnRecordDesc.begin();
    m_hIOMutex = hIOMutex;
    m_dfCoeff = stSXFMapDescription.dfScale / stSXFMapDescription.nResolution;
    poFeatureDefn = new OGRFeatureDefn(pszLayerName);
    poFeatureDefn->Reference();
    
    poFeatureDefn->SetGeomType(wkbUnknown);
    if (poFeatureDefn->GetGeomFieldCount() != 0)
        poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(stSXFMapDescription.pSpatRef);

    //OGRGeomFieldDefn oGeomFieldDefn("Shape", wkbGeometryCollection);
    //oGeomFieldDefn.SetSpatialRef(stSXFMapDescription.pSpatRef);
    //poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);

    OGRFieldDefn oFIDField = OGRFieldDefn(sFIDColumn_, OFTInteger);
    poFeatureDefn->AddFieldDefn(&oFIDField);

    OGRFieldDefn oClCodeField = OGRFieldDefn( "CLCODE", OFTInteger );
    oClCodeField.SetWidth(10);
    poFeatureDefn->AddFieldDefn( &oClCodeField );

    OGRFieldDefn oClNameField = OGRFieldDefn( "CLNAME", OFTString );
    oClNameField.SetWidth(32);
    poFeatureDefn->AddFieldDefn( &oClNameField );

    OGRFieldDefn oNumField = OGRFieldDefn( "OBJECTNUMB", OFTInteger );
    oNumField.SetWidth(10);
    poFeatureDefn->AddFieldDefn( &oNumField );

    OGRFieldDefn oAngField = OGRFieldDefn("ANGLE", OFTReal);
    poFeatureDefn->AddFieldDefn(&oAngField);

    OGRFieldDefn  oTextField( "TEXT", OFTString );
    oTextField.SetWidth(255);
    poFeatureDefn->AddFieldDefn( &oTextField );
}

/************************************************************************/
/*                         ~OGRSXFLayer()                               */
/************************************************************************/

OGRSXFLayer::~OGRSXFLayer()
{
    stSXFMapDescription.pSpatRef->Release();
    poFeatureDefn->Release();
}

/************************************************************************/
/*                AddClassifyCode(unsigned nClassCode)                  */
/* Add layer supported classify codes. Only records with this code can  */
/* be in layer                                                          */
/************************************************************************/

void OGRSXFLayer::AddClassifyCode(unsigned nClassCode, const char *szName)
{
    if (szName != NULL)
    {
        mnClassificators[nClassCode] = CPLString(szName);
    }
    else
    {
        CPLString szIdName;
        szIdName.Printf("%d", nClassCode);
        mnClassificators[nClassCode] = szIdName;
    }
}

/************************************************************************/
/*                         AddRecord()                               */
/************************************************************************/

int OGRSXFLayer::AddRecord(long nFID, unsigned nClassCode, vsi_l_offset nOffset, bool bHasSemantic, int nSemanticsSize)
{
    if (mnClassificators.empty() || mnClassificators.find(nClassCode) != mnClassificators.end())
    {
        mnRecordDesc[nFID] = nOffset;
        //add addtionals semantics (attribute fields)
        if (bHasSemantic)
        {
            size_t offset = 0;

            while (offset < (size_t)nSemanticsSize)
            {
                SXFRecordAttributeInfo stAttrInfo;
                bool bAddField = false;
                size_t nCurrOff = 0;
                int nReadObj = VSIFReadL(&stAttrInfo, 4, 1, fpSXF);
                if (nReadObj == 1)
                {
                    CPLString oFieldName;
                    if (snAttributeCodes.find(stAttrInfo.nCode) == snAttributeCodes.end())
                    {
                        bAddField = true;
                        snAttributeCodes.insert(stAttrInfo.nCode);
                        oFieldName.Printf("SC_%d", stAttrInfo.nCode);
                    }

                    SXFRecordAttributeType eType = (SXFRecordAttributeType)stAttrInfo.nType;

                    offset += 4;


                    switch (eType) //TODO: set field type form RSC as here sometimes we have the codes and string values can be get from RSC by this code
                    {
                    case SXF_RAT_ASCIIZ_DOS:
                    {
                        if (bAddField)
                        {
                            OGRFieldDefn  oField(oFieldName, OFTString);
                            oField.SetWidth(255);
                            poFeatureDefn->AddFieldDefn(&oField);
                        }
                        offset += stAttrInfo.nScale + 1;
                        nCurrOff = stAttrInfo.nScale + 1;
                        break;
                    }
                    case SXF_RAT_ONEBYTE:
                    {
                        if (bAddField)
                        {
                            OGRFieldDefn  oField(oFieldName, OFTReal);
                            poFeatureDefn->AddFieldDefn(&oField);
                        }
                        offset += 1;
                        nCurrOff = 1;
                        break;
                    }
                    case SXF_RAT_TWOBYTE:
                    {
                        if (bAddField)
                        {
                            OGRFieldDefn  oField(oFieldName, OFTReal);
                            poFeatureDefn->AddFieldDefn(&oField);
                        }
                        offset += 2;
                        nCurrOff = 2;
                        break;
                    }
                    case SXF_RAT_FOURBYTE:
                    {
                        if (bAddField)
                        {
                            OGRFieldDefn  oField(oFieldName, OFTReal);
                            poFeatureDefn->AddFieldDefn(&oField);
                        }
                        offset += 4;
                        nCurrOff = 4;
                        break;
                    }
                    case SXF_RAT_EIGHTBYTE:
                    {
                        if (bAddField)
                        {
                            OGRFieldDefn  oField(oFieldName, OFTReal);
                            poFeatureDefn->AddFieldDefn(&oField);
                        }
                        offset += 8;
                        nCurrOff = 8;
                        break;
                    }
                    case SXF_RAT_ANSI_WIN:
                    {
                        if (bAddField)
                        {
                            OGRFieldDefn  oField(oFieldName, OFTString);
                            oField.SetWidth(255);
                            poFeatureDefn->AddFieldDefn(&oField);
                        }
                        unsigned nLen = unsigned(stAttrInfo.nScale) + 1;
                        offset += nLen;
                        nCurrOff = nLen;
                        break;
                    }
                    case SXF_RAT_UNICODE:
                    {
                        if (bAddField)
                        {
                            OGRFieldDefn  oField(oFieldName, OFTString);
                            oField.SetWidth(255);
                            poFeatureDefn->AddFieldDefn(&oField);
                        }
                        unsigned nLen = unsigned(stAttrInfo.nScale) + 1;
                        offset += nLen;
                        nCurrOff = nLen;
                        break;
                    }
                    case SXF_RAT_BIGTEXT:
                    {
                        if (bAddField)
                        {
                            OGRFieldDefn  oField(oFieldName, OFTString);
                            oField.SetWidth(1024);
                            poFeatureDefn->AddFieldDefn(&oField);
                        }
                        GUInt32 scale2;
                        VSIFReadL(&scale2, sizeof(GUInt32), 1, fpSXF);
                        CPL_LSBUINT32PTR(&scale2);

                        offset += scale2;
                        nCurrOff = scale2;
                        break;
                    }
                    default:
                        break;
                    }
                }
                if( nCurrOff == 0 )
                    break;
                VSIFSeekL(fpSXF, nCurrOff, SEEK_CUR);
            }
        }
        return TRUE;
    }

    return FALSE;
}

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

OGRErr OGRSXFLayer::SetNextByIndex(long nIndex)
{
  if (nIndex < 0 || (size_t)nIndex > mnRecordDesc.size())
        return OGRERR_FAILURE;

    oNextIt = mnRecordDesc.begin();
    std::advance(oNextIt, nIndex);

    return OGRERR_NONE;
}

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

OGRFeature *OGRSXFLayer::GetFeature(long nFID)
{
    std::map<long, vsi_l_offset>::const_iterator IT = mnRecordDesc.find(nFID);
    if (IT != mnRecordDesc.end())
    {
        VSIFSeekL(fpSXF, IT->second, SEEK_SET);
        OGRFeature *poFeature = GetNextRawFeature(IT->first);
        if (poFeature != NULL && poFeature->GetGeometryRef() != NULL && GetSpatialRef() != NULL)
        {
            poFeature->GetGeometryRef()->assignSpatialReference(GetSpatialRef());
        }
        return poFeature;
    }

    return NULL;
}

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

OGRSpatialReference *OGRSXFLayer::GetSpatialRef()
{
    return stSXFMapDescription.pSpatRef;
}

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

OGRErr OGRSXFLayer::GetExtent(OGREnvelope *psExtent, int bForce)
{
    if (bForce)
    {
        return OGRLayer::GetExtent(psExtent, bForce);
    }
    else
    {
        psExtent->MinX = stSXFMapDescription.Env.MinX;
        psExtent->MaxX = stSXFMapDescription.Env.MaxX;
        psExtent->MinY = stSXFMapDescription.Env.MinY;
        psExtent->MaxY = stSXFMapDescription.Env.MaxY;

        return OGRERR_NONE;
    }
}

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

int OGRSXFLayer::GetFeatureCount(int bForce)
{
    if (m_poFilterGeom == NULL && m_poAttrQuery == NULL)
        return static_cast<int>(mnRecordDesc.size());
    else
        return OGRLayer::GetFeatureCount(bForce);
}

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

void OGRSXFLayer::ResetReading()

{
    oNextIt = mnRecordDesc.begin();
}


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

OGRFeature *OGRSXFLayer::GetNextFeature()
{
    CPLMutexHolderD(m_hIOMutex);
    while (oNextIt != mnRecordDesc.end())
    {
        VSIFSeekL(fpSXF, oNextIt->second, SEEK_SET);
        OGRFeature  *poFeature = GetNextRawFeature(oNextIt->first);	

        ++oNextIt;

        if (poFeature == NULL)
            continue;

        if ((m_poFilterGeom == NULL
            || FilterGeometry(poFeature->GetGeometryRef()))
            && (m_poAttrQuery == NULL
            || m_poAttrQuery->Evaluate(poFeature)))
        {
            if (poFeature->GetGeometryRef() != NULL && GetSpatialRef() != NULL)
            {
                poFeature->GetGeometryRef()->assignSpatialReference(GetSpatialRef());
            }
	    
            return poFeature;
        }

        delete poFeature;
    }
    return NULL;
}

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

int OGRSXFLayer::TestCapability( const char * pszCap )

{
    if (EQUAL(pszCap, OLCStringsAsUTF8))
        return TRUE;
    else if (EQUAL(pszCap, OLCRandomRead))
        return TRUE;
    else if (EQUAL(pszCap, OLCFastFeatureCount))
        return TRUE;
    else if (EQUAL(pszCap, OLCFastGetExtent))
        return TRUE;
    else if (EQUAL(pszCap, OLCFastSetNextByIndex))
        return TRUE;
        
    return FALSE;
}

/************************************************************************/
/*                                TranslateXYH()                        */
/************************************************************************/
/****
 * TODO : Take into account informations given in the passport 
 * like unit of mesurement, type and dimensions (integer, float, double) of coordinate,
 * the vector format ....
 */

GUInt32 OGRSXFLayer::TranslateXYH(const SXFRecordDescription& certifInfo,
                                  const char *psBuff, GUInt32 nBufLen,
                          double *dfX, double *dfY, double *dfH)
{
    //Xp, Yp(м) = Xo, Yo(м) + (Xd, Yd / R * S), (1)

	int offset = 0;
    switch (certifInfo.eValType)
    {
    case SXF_VT_SHORT:
    {
        if( nBufLen < 4 )
            return 0;
        GInt16 x, y;
        memcpy(&x, psBuff, 2);
        CPL_LSBINT16PTR(&x);
        memcpy(&y, psBuff + 2, 2);
        CPL_LSBINT16PTR(&y);


        if (stSXFMapDescription.bIsRealCoordinates)
        {
            *dfX = (double)y;
            *dfY = (double)x;
        }
        else
        {
            if (m_nSXFFormatVer == 3)
            {
                *dfX = stSXFMapDescription.dfXOr + (double)y * m_dfCoeff;
                *dfY = stSXFMapDescription.dfYOr + (double)x * m_dfCoeff;
            }
            else if (m_nSXFFormatVer == 4)
            {
                //TODO: check on real data
                *dfX = stSXFMapDescription.dfXOr + (double)y * m_dfCoeff;
                *dfY = stSXFMapDescription.dfYOr + (double)x * m_dfCoeff;
            }
        }

        offset += 4;

        if (dfH != NULL)
        {
            if( nBufLen < 4 + 4 )
                return 0;
            float h;
            memcpy(&h, psBuff + 4, 4); // H always in float
            CPL_LSBPTR32(&h);
            *dfH = (double)h;

            offset += 4;
        }
    }
        break;
    case SXF_VT_FLOAT:
    {
        float x, y;
        if( nBufLen < 8 )
            return 0;
        memcpy(&y, psBuff, 4);
        CPL_LSBPTR32(&y);
        memcpy(&x, psBuff + 4, 4);
        CPL_LSBPTR32(&x);

        if (stSXFMapDescription.bIsRealCoordinates)
        {
            *dfX = (double)y;
            *dfY = (double)x;
        }
        else
        {
            *dfX = stSXFMapDescription.dfXOr + (double)x * m_dfCoeff;
            *dfY = stSXFMapDescription.dfYOr + (double)y * m_dfCoeff;
        }

        offset += 8;

        if (dfH != NULL)
        {
            if( nBufLen < 8 + 4 )
                return 0;
            float h;
            memcpy(&h, psBuff + 8, 4); // H always in float
            CPL_LSBPTR32(&h);
            *dfH = (double)h;

            offset += 4;
        }
    }
        break;
    case SXF_VT_INT:
    {
        if( nBufLen < 8 )
            return 0;
        GInt32 x, y;
        memcpy(&y, psBuff, 4);
        CPL_LSBPTR32(&y);
        memcpy(&x, psBuff + 4, 4);
        CPL_LSBPTR32(&x);

        if (stSXFMapDescription.bIsRealCoordinates)
        {
            *dfX = (double)y;
            *dfY = (double)x;
        }
        else
        {
            //TODO: check on real data
            if (m_nSXFFormatVer == 3)
            {
                *dfX = stSXFMapDescription.dfXOr + (double)y * m_dfCoeff;
                *dfY = stSXFMapDescription.dfYOr + (double)x * m_dfCoeff;
            }
            else if (m_nSXFFormatVer == 4)
            {
                *dfX = stSXFMapDescription.dfXOr + (double)y * m_dfCoeff;
                *dfY = stSXFMapDescription.dfYOr + (double)x * m_dfCoeff;
            }
        }
        offset += 8;

        if (dfH != NULL)
        {
            if( nBufLen < 8 + 4 )
                return 0;
            float h;
            memcpy(&h, psBuff + 8, 4); // H always in float
            CPL_LSBPTR32(&h);
            *dfH = (double)h;

            offset += 4;
        }
    }
        break;
    case SXF_VT_DOUBLE:
    {
        if( nBufLen < 16 )
            return 0;
        double x, y;
        memcpy(&y, psBuff, 8);
        CPL_LSBPTR64(&y);
        memcpy(&x, psBuff + 8, 8);
        CPL_LSBPTR64(&x);

        if (stSXFMapDescription.bIsRealCoordinates)
        {
            *dfY = y;
            *dfX = x;
        }
        else
        {
            *dfX = stSXFMapDescription.dfXOr + x * m_dfCoeff;
            *dfY = stSXFMapDescription.dfYOr + y * m_dfCoeff;
        }

        offset += 16;

        if (dfH != NULL)
        {
            if( nBufLen < 16 + 8 )
                return 0;
            double h;
            memcpy(&h, psBuff + 16, 8); // H in double
            CPL_LSBPTR64(&h);
            *dfH = (double)h;

            offset += 8;
        }
    }
        break;
    };

    return offset;
}

/************************************************************************/
/*                         GetNextRawFeature()                          */
/************************************************************************/

OGRFeature *OGRSXFLayer::GetNextRawFeature(long nFID)
{
    SXFRecordHeader stRecordHeader;
    int nObjectRead;

    nObjectRead = VSIFReadL(&stRecordHeader, sizeof(SXFRecordHeader), 1, fpSXF);

    if (nObjectRead != 1 || stRecordHeader.nID != IDSXFOBJ)
    {
        CPLError(CE_Failure, CPLE_FileIO, "SXF. Read record failed.");
        return NULL;
    }

    SXFGeometryType eGeomType;
    GByte code;
    if (m_nSXFFormatVer == 3)
    {
        if (CHECK_BIT(stRecordHeader.nRef[2], 3))
        {
            if (CHECK_BIT(stRecordHeader.nRef[2], 4))
            {
                code = 0x22;
                stRecordHeader.nSubObjectCount = 0;
            }
            else
            {
                code = 0x21; 
                stRecordHeader.nSubObjectCount = 0;
            }
        }
        else
        {
            code = stRecordHeader.nRef[0] & 3;//get first 2 bits
        }
    }
    else if (m_nSXFFormatVer == 4)
    {
        if (CHECK_BIT(stRecordHeader.nRef[2], 5))
        {
            stRecordHeader.nSubObjectCount = 0;
        }

        //check if vector
        code = stRecordHeader.nRef[0] & 15;//get first 4 bits
        if (code == 0x04) // xxxx0100
        {
            code = 0x21;
            stRecordHeader.nSubObjectCount = 0;
            //if (CHECK_BIT(stRecordHeader.nRef[2], 5))
            //{
            //    code = 0x22;
            //    stRecordHeader.nSubObjectCount = 0;
            //}
            //else
            //{
            //    code = 0x21;
            //    stRecordHeader.nSubObjectCount = 0;
            //}
            //if (CHECK_BIT(stRecordHeader.nRef[2], 4))
            //{
            //}
            //else
            //{
            //}
        }
    }

    if (code == 0x00) // xxxx0000
        eGeomType = SXF_GT_Line;
    else if (code == 0x01) // xxxx0001
        eGeomType = SXF_GT_Polygon;
    else if (code == 0x02) // xxxx0010
        eGeomType = SXF_GT_Point;
    else if (code == 0x03) // xxxx0011
        eGeomType = SXF_GT_Text;
    //beginning 4.0
    else if (code == 0x04) // xxxx0100
    {
        CPLError(CE_Warning, CPLE_NotSupported,
            "SXF. Not support type.");
        eGeomType = SXF_GT_Vector;
    }
    else if (code == 0x05) // xxxx0101
        eGeomType = SXF_GT_TextTemplate;
    else if (code == 0x21) 
        eGeomType = SXF_GT_VectorAngle;
    else if (code == 0x22) 
        eGeomType = SXF_GT_VectorScaled;
    bool bHasAttributes = CHECK_BIT(stRecordHeader.nRef[1], 1);
    bool bHasRefVector = CHECK_BIT(stRecordHeader.nRef[1], 3);
    if (bHasRefVector == true)
        CPLError(CE_Failure, CPLE_NotSupported,
        "SXF. Parsing the vector of the tying not support.");

    SXFRecordDescription stCertInfo;
    if (stRecordHeader.nPointCountSmall == 65535)
    {
        stCertInfo.nPointCount = stRecordHeader.nPointCount;
    }
    else
    {
        stCertInfo.nPointCount = stRecordHeader.nPointCountSmall;
    }
    stCertInfo.nSubObjectCount = stRecordHeader.nSubObjectCount;

    bool bFloatType = 0, bBigType = 0;
    bool b3D(true);
    if (m_nSXFFormatVer == 3)
    {
        b3D = CHECK_BIT(stRecordHeader.nRef[2], 1);
        bFloatType = CHECK_BIT(stRecordHeader.nRef[2], 2);
        bBigType = CHECK_BIT(stRecordHeader.nRef[1], 2);
        stCertInfo.bHasTextSign = CHECK_BIT(stRecordHeader.nRef[2], 5);
    }
    else if (m_nSXFFormatVer == 4)
    {
        b3D = CHECK_BIT(stRecordHeader.nRef[2], 1);
        bFloatType = CHECK_BIT(stRecordHeader.nRef[2], 2);
        bBigType = CHECK_BIT(stRecordHeader.nRef[1], 2);
        stCertInfo.bHasTextSign = CHECK_BIT(stRecordHeader.nRef[2], 3);
    }
    // Else trouble.

    if (b3D) //xххххх1х
        stCertInfo.bDim = 1;
    else
        stCertInfo.bDim = 0;

    if (bFloatType)
    {
        if (bBigType)
        {
            stCertInfo.eValType = SXF_VT_DOUBLE;
        }
        else
        {
            stCertInfo.eValType = SXF_VT_FLOAT;
        }
    }
    else
    {
        if (bBigType)
        {
            stCertInfo.eValType = SXF_VT_INT;
        }
        else
        {
            stCertInfo.eValType = SXF_VT_SHORT;
        }
    }


    stCertInfo.bFormat = CHECK_BIT(stRecordHeader.nRef[2], 0);
    stCertInfo.eGeomType = eGeomType;

    OGRFeature *poFeature = NULL;
    if( stRecordHeader.nGeometryLength > 100 * 1024 * 1024 )
        return NULL;
    char * recordCertifBuf = (char *)VSIMalloc(stRecordHeader.nGeometryLength);
    if( recordCertifBuf == NULL )
        return NULL;
    nObjectRead = VSIFReadL(recordCertifBuf, stRecordHeader.nGeometryLength, 1, fpSXF);
    if (nObjectRead != 1)
    {
        CPLError(CE_Failure, CPLE_FileIO,
            "SXF. Read geometry failed.");
        CPLFree(recordCertifBuf);
        return NULL;
    }

    if (eGeomType == SXF_GT_Point)
        poFeature = TranslatePoint(stCertInfo, recordCertifBuf,
                                   stRecordHeader.nGeometryLength);
    else if (eGeomType == SXF_GT_Line || eGeomType == SXF_GT_VectorScaled)
        poFeature = TranslateLine(stCertInfo, recordCertifBuf,
                                   stRecordHeader.nGeometryLength);
    else if (eGeomType == SXF_GT_Polygon)
        poFeature = TranslatePolygon(stCertInfo, recordCertifBuf,
                                   stRecordHeader.nGeometryLength);
    else if (eGeomType == SXF_GT_Text)
        poFeature = TranslateText(stCertInfo, recordCertifBuf,
                                   stRecordHeader.nGeometryLength);
    else if (eGeomType == SXF_GT_VectorAngle)
    {
        poFeature = TranslateVetorAngle(stCertInfo, recordCertifBuf,
            stRecordHeader.nGeometryLength);
    }
    else if (eGeomType == SXF_GT_Vector ) 
    {
      CPLError( CE_Warning, CPLE_NotSupported,
      "SXF. Geometry type Vector do not support." );
      return NULL;
    }
    else if (eGeomType == SXF_GT_TextTemplate ) // TODO realise this
    {
      CPLError( CE_Warning, CPLE_NotSupported,
      "SXF. Geometry type Text Template do not support." );
      return NULL;
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
            "SXF. Unsupported geometry type.");
        CPLFree(recordCertifBuf);
        return NULL;
    }
    
    if( poFeature == NULL )
    {
        CPLFree(recordCertifBuf);
        return NULL;
    }

    poFeature->SetField(sFIDColumn_, (int)nFID);

    poFeature->SetField("CLCODE", (int)stRecordHeader.nClassifyCode);

    CPLString szName = mnClassificators[stRecordHeader.nClassifyCode];

    if (szName.empty())
    {
        szName.Printf("%d", stRecordHeader.nClassifyCode);
    }
    poFeature->SetField("CLNAME", szName);

    poFeature->SetField("OBJECTNUMB", stRecordHeader.nSubObjectCount);

    if (bHasAttributes)
    {
        if( stRecordHeader.nFullLength < 32 ||
            stRecordHeader.nGeometryLength > stRecordHeader.nFullLength - 32 )
        {
            CPLFree(recordCertifBuf);
            delete poFeature;
            return NULL;
        }
        size_t  nSemanticsSize = stRecordHeader.nFullLength - 32 - stRecordHeader.nGeometryLength;
        if( nSemanticsSize > 1024 * 1024 )
        {
            CPLFree(recordCertifBuf);
            delete poFeature;
            return NULL;
        }
        char * psSemanticsdBuf = (char *)VSIMalloc(nSemanticsSize);
        if( psSemanticsdBuf == NULL )
        {
            CPLFree(recordCertifBuf);
            delete poFeature;
            return NULL;
        }
        char * psSemanticsdBufOrig = psSemanticsdBuf;
        nObjectRead = VSIFReadL(psSemanticsdBuf, nSemanticsSize, 1, fpSXF);
        if (nObjectRead == 1)
        {
            size_t offset = 0;
            double nVal = 0;

            while (offset + sizeof(SXFRecordAttributeInfo) < nSemanticsSize)
            {
                char *psSemanticsdBufBeg = psSemanticsdBuf + offset;
                SXFRecordAttributeInfo stAttInfo = *(SXFRecordAttributeInfo*)psSemanticsdBufBeg;
                offset += 4;

                CPLString oFieldName;
                oFieldName.Printf("SC_%d", stAttInfo.nCode);

                CPLString oFieldValue;

                SXFRecordAttributeType eType = (SXFRecordAttributeType)stAttInfo.nType;

                switch (eType)
                {
                case SXF_RAT_ASCIIZ_DOS:
                {
                    unsigned nLen = unsigned(stAttInfo.nScale) + 1;
                    if( nLen > nSemanticsSize || nSemanticsSize - nLen < offset )
                    {
                        nSemanticsSize = 0;
                        break;
                    }
                    char * value = (char*)CPLMalloc(nLen);
                    memcpy(value, psSemanticsdBuf + offset, nLen);
                    value[nLen-1] = 0;
                    char* pszRecoded = CPLRecode(value, "CP866", CPL_ENC_UTF8);
                    poFeature->SetField(oFieldName, pszRecoded);
                    CPLFree(pszRecoded);
                    CPLFree(value);

                    offset += stAttInfo.nScale + 1;
                    break;
                }
                case SXF_RAT_ONEBYTE:
                {
                    GByte nTmpVal;
                    if( offset + sizeof(GByte) > nSemanticsSize )
                    {
                        nSemanticsSize = 0;
                        break;
                    }
                    memcpy(&nTmpVal, psSemanticsdBuf + offset, sizeof(GByte));
                    nVal = double(nTmpVal) * pow(10.0, (double)stAttInfo.nScale);

                    poFeature->SetField(oFieldName, nVal);
                    offset += 1;
                    break;
                }
                case SXF_RAT_TWOBYTE:
                {
                    GInt16 nTmpVal;
                    if( offset + sizeof(GInt16) > nSemanticsSize )
                    {
                        nSemanticsSize = 0;
                        break;
                    }
                    memcpy(&nTmpVal, psSemanticsdBuf + offset, sizeof(GInt16));
                    nVal = double(CPL_LSBWORD16(nTmpVal)) * pow(10.0, (double)stAttInfo.nScale);
   
                    poFeature->SetField(oFieldName, nVal);
                    offset += 2;
                    break;
                }
                case SXF_RAT_FOURBYTE:
                {
                    GInt32 nTmpVal;
                    if( offset + sizeof(GInt32) > nSemanticsSize )
                    {
                        nSemanticsSize = 0;
                        break;
                    }
                    memcpy(&nTmpVal, psSemanticsdBuf + offset, sizeof(GInt32));
                    nVal = double(CPL_LSBWORD32(nTmpVal)) * pow(10.0, (double)stAttInfo.nScale);

                    poFeature->SetField(oFieldName, nVal);
                    offset += 4;
                    break;
                }
                case SXF_RAT_EIGHTBYTE:
                {
                    double dfTmpVal;
                    if( offset + sizeof(double) > nSemanticsSize )
                    {
                        nSemanticsSize = 0;
                        break;
                    }
                    memcpy(&dfTmpVal, psSemanticsdBuf + offset, sizeof(double));
                    CPL_LSBPTR64(&dfTmpVal);
                    double d = dfTmpVal * pow(10.0, (double)stAttInfo.nScale);

                    poFeature->SetField(oFieldName, d);

                    offset += 8;
                    break;
                }
                case SXF_RAT_ANSI_WIN:
                {
                    unsigned nLen = unsigned(stAttInfo.nScale) + 1;
                    if( nLen > nSemanticsSize || nSemanticsSize - nLen < offset )
                    {
                        nSemanticsSize = 0;
                        break;
                    }
                    char * value = (char*)CPLMalloc(nLen);
                    memcpy(value, psSemanticsdBuf + offset, nLen);
                    value[nLen-1] = 0;
                    char* pszRecoded = CPLRecode(value, "CP1251", CPL_ENC_UTF8);
                    poFeature->SetField(oFieldName, pszRecoded);
                    CPLFree(pszRecoded);
                    CPLFree(value);

                    offset += nLen;
                    break;
                }
                case SXF_RAT_UNICODE:
                {
                    unsigned nLen = unsigned(stAttInfo.nScale) + 1;
                    if( nLen > nSemanticsSize || nSemanticsSize - nLen < offset )
                    {
                        nSemanticsSize = 0;
                        break;
                    }
                    char * value = (char*)CPLMalloc(nLen);
                    memcpy(value, psSemanticsdBuf + offset, nLen);
                    value[nLen-1] = 0;
                    poFeature->SetField(oFieldName, value);
                    CPLFree(value);

                    offset += nLen;
                    break;
                }
                case SXF_RAT_BIGTEXT:
                {
                    GUInt32 scale2;
                    if( offset + sizeof(GUInt32) > nSemanticsSize )
                    {
                        nSemanticsSize = 0;
                        break;
                    }
                    memcpy(&scale2, psSemanticsdBuf + offset, sizeof(GUInt32));
                    CPL_LSBUINT32PTR(&scale2);
                    /* FIXME add ?: offset += sizeof(GUInt32); */
                    if( scale2 > nSemanticsSize - 1 || nSemanticsSize - (scale2 + 1) < offset )
                    {
                        nSemanticsSize = 0;
                        break;
                    }

                    char * value = (char*)CPLMalloc(scale2 + 1);
                    memcpy(value, psSemanticsdBuf + offset, scale2 + 1);
                    value[scale2] = 0;
                    char* pszRecoded = CPLRecode(value, CPL_ENC_UTF16, CPL_ENC_UTF8);
                    poFeature->SetField(oFieldName, pszRecoded);
                    CPLFree(pszRecoded);
                    CPLFree(value);

                    offset += scale2;
                    break;
                }
                default:
                    CPLFree(recordCertifBuf);
                    CPLFree(psSemanticsdBufOrig);
                    delete poFeature;
                    return NULL;
                }
            }
            CPLFree(psSemanticsdBufOrig);
        }
    }    
   
    poFeature->SetFID(nFID);

    CPLFree(recordCertifBuf);

    return poFeature;
}

/************************************************************************/
/*                         TranslatePoint   ()                          */
/************************************************************************/

OGRFeature *OGRSXFLayer::TranslatePoint(const SXFRecordDescription& certifInfo,
                                        const char * psRecordBuf, GUInt32 nBufLen)
{
    double dfX = 1.0;
    double dfY = 1.0;
    GUInt32 nOffset = 0;

    GUInt32 nDelta = TranslateXYH( certifInfo, psRecordBuf , nBufLen, &dfX, &dfY );
    if( nDelta == 0 )
        return NULL;
    nOffset += nDelta;

	//OGRFeatureDefn *fd = poFeatureDefn->Clone();
	//fd->SetGeomType( wkbMultiPoint );
 //   OGRFeature *poFeature = new OGRFeature(fd);
    OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
    OGRMultiPoint* poMPt = new OGRMultiPoint();

    if (certifInfo.bDim == 1) // TODO realise this
	{
		CPLError( CE_Failure, CPLE_NotSupported, 
				"SXF. 3D metrics do not support." );
	}


    poMPt->addGeometryDirectly( new OGRPoint( dfX, dfY ) );

/*---------------------- Reading SubObjects --------------------------------*/

    for(int count=0 ; count <  certifInfo.nSubObjectCount ; count++)
    {
        if( nOffset + 4 > nBufLen )
            break;

        GUInt16 nSubObj;
        memcpy(&nSubObj, psRecordBuf + nOffset, 2);
        CPL_LSBUINT16PTR(&nSubObj);

        GUInt16 nCoords;
        memcpy(&nCoords, psRecordBuf + nOffset + 2, 2);
        CPL_LSBUINT16PTR(&nCoords);

        nOffset +=4;

        for (int i=0; i < nCoords ; i++)
        {
            const char * psCoords = psRecordBuf + nOffset ;

            nDelta = TranslateXYH( certifInfo, psCoords , nBufLen - nOffset, &dfX, &dfY ) ;
            if( nDelta == 0 )
                break;
            nOffset +=  nDelta;

            poMPt->addGeometryDirectly( new OGRPoint( dfX, dfY ) );
        } 
    }

/*****
 * TODO : 
 *          - Translate graphics 
 *          - Translate 3D vector
 */

    poFeature->SetGeometryDirectly( poMPt );

    return poFeature;
}

/************************************************************************/
/*                         TranslateLine    ()                          */
/************************************************************************/

OGRFeature *OGRSXFLayer::TranslateLine(const SXFRecordDescription& certifInfo,
                                       const char * psRecordBuf, GUInt32 nBufLen)
{
    double dfX = 1.0;
    double dfY = 1.0;

    GUInt32 nOffset = 0;

	//OGRFeatureDefn *fd = poFeatureDefn->Clone();
	//fd->SetGeomType( wkbMultiLineString );
 //   OGRFeature *poFeature = new OGRFeature(fd);

    OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
    OGRMultiLineString *poMLS = new  OGRMultiLineString ();

/*---------------------- Reading Primary Line --------------------------------*/

    OGRLineString* poLS = new OGRLineString();

    if (certifInfo.bDim == 1) // TODO realise this
	{
		 CPLError( CE_Failure, CPLE_NotSupported, 
                  "SXF. 3D metrics do not support." );
	}

    for(int count=0 ; (GUInt32)count <  certifInfo.nPointCount ; count++)
    {
        const char * psCoords = psRecordBuf + nOffset ;
        GUInt32 nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY );
        if( nDelta == 0 )
            break;
        nOffset += nDelta;

        poLS->addPoint( dfX  , dfY );
    }

    poMLS->addGeometry( poLS );

/*---------------------- Reading Sub Lines --------------------------------*/

    for(int count=0 ; count <  certifInfo.nSubObjectCount ; count++)
    {
        poLS->empty();

        if( nOffset + 4 > nBufLen )
            break;

        GUInt16 nSubObj;
        memcpy(&nSubObj, psRecordBuf + nOffset, 2);
        CPL_LSBUINT16PTR(&nSubObj);

        GUInt16 nCoords;
        memcpy(&nCoords, psRecordBuf + nOffset + 2, 2);
        CPL_LSBUINT16PTR(&nCoords);

        nOffset +=4;

        for (int i=0; i < nCoords ; i++)
        {
            const char * psCoords = psRecordBuf + nOffset ;

            GUInt32 nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY );
            if( nDelta == 0 )
                break;
            nOffset += nDelta;

            poLS->addPoint( dfX  , dfY );
        }

        poMLS->addGeometry( poLS );
    }    // for

    delete poLS;
    poFeature->SetGeometryDirectly( poMLS );

/*****
 * TODO : 
 *          - Translate graphics 
 *          - Translate 3D vector
 */

    return poFeature;
}

/************************************************************************/
/*                       TranslateVetorAngle()                          */
/************************************************************************/

OGRFeature *OGRSXFLayer::TranslateVetorAngle(const SXFRecordDescription& certifInfo,
    const char * psRecordBuf, GUInt32 nBufLen)
{
    if (certifInfo.nPointCount != 2)
    {
        CPLError(CE_Failure, CPLE_NotSupported,
            "SXF. The vector object should have 2 points, but not.");
        return NULL;
    }

    double dfX = 1.0;
    double dfY = 1.0;

    GUInt32 nOffset = 0;

    OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
    OGRPoint *poPT = new OGRPoint();

    /*---------------------- Reading Primary Line --------------------------------*/

    OGRLineString* poLS = new OGRLineString();

    if (certifInfo.bDim == 1) // TODO realise this
    {
        CPLError(CE_Failure, CPLE_NotSupported,
            "SXF. 3D metrics do not support.");
    }

    for (int count = 0; (GUInt32)count < certifInfo.nPointCount; count++)
    {
        const char * psCoords = psRecordBuf + nOffset;
        GUInt32 nDelta = TranslateXYH(certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY);
        if (nDelta == 0)
            break;
        nOffset += nDelta;

        poLS->addPoint(dfX, dfY);
    }

    poLS->StartPoint(poPT);

    OGRPoint *poAngPT = new OGRPoint();
    poLS->EndPoint(poAngPT);

    double xDiff = poPT->getX() - poAngPT->getX();
    double yDiff = poPT->getY() - poAngPT->getY();
    double dfAngle = atan2(xDiff, yDiff) * TO_DEGREES - 90;
    if (dfAngle < 0)
        dfAngle += 360;

    poFeature->SetGeometryDirectly(poPT);
    poFeature->SetField("ANGLE", dfAngle);

    delete poAngPT;
    delete poLS;

    return poFeature;
}


/************************************************************************/
/*                         TranslatePolygon ()                          */
/************************************************************************/

OGRFeature *OGRSXFLayer::TranslatePolygon(const SXFRecordDescription& certifInfo,
                                          const char * psRecordBuf, GUInt32 nBufLen)
{
    double dfX = 1.0;
    double dfY = 1.0;
    GUInt32 nOffset = 0;

	//OGRFeatureDefn *fd = poFeatureDefn->Clone();
	//fd->SetGeomType( wkbMultiPolygon );
 //   OGRFeature *poFeature = new OGRFeature(fd);

    OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
    OGRPolygon *poPoly = new OGRPolygon();
    OGRLineString* poLS = new OGRLineString();

/*---------------------- Reading Primary Polygon --------------------------------*/

    if (certifInfo.bDim == 1) // TODO realise this
	{
			CPLError( CE_Failure, CPLE_NotSupported, 
					"SXF. 3D metrics do not support." );
	}

    for(int count=0 ; (GUInt32)count <  certifInfo.nPointCount ; count++)
    {
        const char * psBuf = psRecordBuf + nOffset ;

        GUInt32 nDelta = TranslateXYH( certifInfo, psBuf, nBufLen - nOffset, &dfX, &dfY );
        if( nDelta == 0 )
            break;
        nOffset += nDelta;
        poLS->addPoint( dfX  , dfY );

    }    // for

    OGRLinearRing *poLR = new OGRLinearRing();
    poLR->addSubLineString( poLS, 0 );

    poPoly->addRingDirectly( poLR );

/*---------------------- Reading Sub Lines --------------------------------*/

    for(int count=0 ; count <  certifInfo.nSubObjectCount ; count++)
    {
        poLS->empty();

        if( nOffset + 4 > nBufLen )
            break;

        GUInt16 nSubObj;
        memcpy(&nSubObj, psRecordBuf + nOffset, 2);
        CPL_LSBUINT16PTR(&nSubObj);

        GUInt16 nCoords;
        memcpy(&nCoords, psRecordBuf + nOffset + 2, 2);
        CPL_LSBUINT16PTR(&nCoords);

        nOffset +=4;

        int i;
        for (i=0; i < nCoords ; i++)
        {
            const char * psCoords = psRecordBuf + nOffset ;

            GUInt32 nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY );
            if( nDelta == 0 )
                break;
            nOffset += nDelta;

            poLS->addPoint( dfX  , dfY );
        }

        OGRLinearRing *poLR = new OGRLinearRing();
        poLR->addSubLineString( poLS, 0 );

        poPoly->addRingDirectly( poLR );
    }    // for

    poFeature->SetGeometryDirectly( poPoly );   //poLS);
    delete poLS;

/*****
 * TODO : 
 *          - Translate graphics 
 *          - Translate 3D vector
 */
    return poFeature;
}

/************************************************************************/
/*                         TranslateText    ()                          */
/************************************************************************/
OGRFeature *OGRSXFLayer::TranslateText(const SXFRecordDescription& certifInfo,
                                       const char * psRecordBuf, GUInt32 nBufLen)
{
    double dfX = 1.0;
    double dfY = 1.0;
    GUInt32 nOffset = 0;

	//OGRFeatureDefn *fd = poFeatureDefn->Clone();
	//fd->SetGeomType( wkbLineString );
 //   OGRFeature *poFeature = new OGRFeature(fd);

    OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
    OGRLineString* poLS = new OGRLineString();

    if (certifInfo.bDim == 1) // TODO realise this
	{
			CPLError( CE_Failure, CPLE_NotSupported, 
					"SXF. 3D metrics do not support." );
	}

    for(int count=0 ; (GUInt32)count <  certifInfo.nPointCount ; count++)
    {
        const char * psBuf = psRecordBuf + nOffset;
        GUInt32 nDelta = TranslateXYH( certifInfo, psBuf, nBufLen - nOffset, &dfX, &dfY );
        if( nDelta == 0 )
            break;
        nOffset += nDelta;
        poLS->addPoint( dfX  , dfY );
    }

    poFeature->SetGeometryDirectly( poLS );

/*------------------     READING TEXT VALUE   ---------------------------------------*/

    if ( certifInfo.nSubObjectCount == 0 && certifInfo.bHasTextSign == true)
    {
        if( nOffset + 1 > nBufLen )
            return poFeature;
        const char * pszTxt = psRecordBuf + nOffset;
        GByte nTextL = (GByte) *pszTxt;
        if( nOffset + 1 + nTextL > nBufLen )
            return poFeature;

        char * pszTextBuf = (char *)CPLMalloc( nTextL+1 );

        strncpy(pszTextBuf, (pszTxt+1),    nTextL);
        pszTextBuf[nTextL] = '\0';

        //TODO: Check encoding from sxf
        poFeature->SetField("TEXT", pszTextBuf);
 
        CPLFree( pszTextBuf );
    }


/*****
 * TODO : 
 *          - Translate graphics 
 *          - Translate 3D vector
 */

    return poFeature;
}

const char* OGRSXFLayer::GetFIDColumn()
{
    return sFIDColumn_.c_str();
}
