/**********************************************************************
 *
 * Project:  GML Reader
 * Purpose:  Implementation of GMLPropertyDefn
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 **********************************************************************
 * Copyright (c) 2002, Frank Warmerdam
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
 *
 * SPDX-License-Identifier: MIT
 ****************************************************************************/

#include "cpl_port.h"
#include "gmlfeature.h"

#include <cstring>

#include "cpl_conv.h"
#include "cpl_string.h"

/************************************************************************/
/*                           GMLPropertyDefn                            */
/************************************************************************/

GMLPropertyDefn::GMLPropertyDefn(const char *pszName, const char *pszSrcElement)
    : m_pszName(CPLStrdup(pszName)),
      m_pszSrcElement(pszSrcElement ? CPLStrdup(pszSrcElement) : nullptr),
      m_nSrcElementLen(pszSrcElement ? strlen(pszSrcElement) : 0)
{
}

/************************************************************************/
/*                          ~GMLPropertyDefn()                          */
/************************************************************************/

GMLPropertyDefn::~GMLPropertyDefn()

{
    CPLFree(m_pszName);
    CPLFree(m_pszSrcElement);
    CPLFree(m_pszCondition);
}

/************************************************************************/
/*                           SetSrcElement()                            */
/************************************************************************/

void GMLPropertyDefn::SetSrcElement(const char *pszSrcElement)

{
    CPLFree(m_pszSrcElement);
    if (pszSrcElement != nullptr)
    {
        m_nSrcElementLen = strlen(pszSrcElement);
        m_pszSrcElement = CPLStrdup(pszSrcElement);
    }
    else
    {
        m_nSrcElementLen = 0;
        m_pszSrcElement = nullptr;
    }
}

/************************************************************************/
/*                            SetCondition()                            */
/************************************************************************/

void GMLPropertyDefn::SetCondition(const char *pszCondition)
{
    CPLFree(m_pszCondition);
    m_pszCondition =
        pszCondition != nullptr ? CPLStrdup(pszCondition) : nullptr;
}

/************************************************************************/
/*                        AnalysePropertyValue()                        */
/*                                                                      */
/*      Examine the passed property value, and see if we need to        */
/*      make the field type more specific, or more general.             */
/************************************************************************/

void GMLPropertyDefn::AnalysePropertyValue(const GMLProperty *psGMLProperty,
                                           bool bSetWidth)

{
    /* -------------------------------------------------------------------- */
    /*      Does the string consist entirely of numeric values?             */
    /* -------------------------------------------------------------------- */
    bool bIsReal = false;

    for (int j = 0; j < psGMLProperty->nSubProperties; j++)
    {
        if (j > 0)
        {
            if (m_eType == GMLPT_Integer)
            {
                m_eType = GMLPT_IntegerList;
            }
            else if (m_eType == GMLPT_Integer64)
            {
                m_eType = GMLPT_Integer64List;
            }
            else if (m_eType == GMLPT_Real)
            {
                m_eType = GMLPT_RealList;
            }
            else if (m_eType == GMLPT_String)
            {
                m_eType = GMLPT_StringList;
                m_nWidth = 0;
            }
            else if (m_eType == GMLPT_Boolean)
                m_eType = GMLPT_BooleanList;
        }
        const char *pszValue = psGMLProperty->papszSubProperties[j];
        /* --------------------------------------------------------------------
         */
        /*      If it is a zero length string, just return.  We can't deduce */
        /*      much from this. */
        /* --------------------------------------------------------------------
         */
        if (*pszValue == '\0')
            continue;

        const CPLValueType valueType = CPLGetValueType(pszValue);

        if (valueType == CPL_VALUE_STRING && m_eType != GMLPT_String &&
            m_eType != GMLPT_StringList)
        {
            if ((m_eType == GMLPT_Untyped || m_eType == GMLPT_Boolean) &&
                (strcmp(pszValue, "true") == 0 ||
                 strcmp(pszValue, "false") == 0))
            {
                m_eType = GMLPT_Boolean;
            }
            else if (m_eType == GMLPT_BooleanList)
            {
                if (!(strcmp(pszValue, "true") == 0 ||
                      strcmp(pszValue, "false") == 0))
                    m_eType = GMLPT_StringList;
            }
            else if (m_eType == GMLPT_IntegerList ||
                     m_eType == GMLPT_Integer64List ||
                     m_eType == GMLPT_RealList)
            {
                m_eType = GMLPT_StringList;
            }
            else
            {
                const auto IsDigitLowerOrEqual = [](char c, int max)
                { return c >= '0' && c <= '0' + max; };

                if ((m_eType == GMLPT_Untyped || m_eType == GMLPT_DateTime ||
                     m_eType == GMLPT_Date) &&
                    IsDigitLowerOrEqual(pszValue[0], 9) &&
                    IsDigitLowerOrEqual(pszValue[1], 9) &&
                    IsDigitLowerOrEqual(pszValue[2], 9) &&
                    IsDigitLowerOrEqual(pszValue[3], 9) && pszValue[4] == '-' &&
                    IsDigitLowerOrEqual(pszValue[5], 1) &&
                    IsDigitLowerOrEqual(pszValue[6], 9) && pszValue[7] == '-' &&
                    IsDigitLowerOrEqual(pszValue[8], 3) &&
                    IsDigitLowerOrEqual(pszValue[9], 9))
                {
                    if (pszValue[10] == 'T' &&
                        IsDigitLowerOrEqual(pszValue[11], 2) &&
                        IsDigitLowerOrEqual(pszValue[12], 9) &&
                        pszValue[13] == ':' &&
                        IsDigitLowerOrEqual(pszValue[14], 5) &&
                        IsDigitLowerOrEqual(pszValue[15], 9) &&
                        pszValue[16] == ':' &&
                        IsDigitLowerOrEqual(pszValue[17], 6) &&
                        IsDigitLowerOrEqual(pszValue[18], 9) &&
                        (pszValue[19] == '\0' || pszValue[19] == '.' ||
                         pszValue[19] == 'Z' || pszValue[19] == '+' ||
                         pszValue[19] == '-'))
                    {
                        m_eType = GMLPT_DateTime;
                    }
                    else if (pszValue[10] == '\0')
                    {
                        if (m_eType != GMLPT_DateTime)
                            m_eType = GMLPT_Date;
                    }
                    else
                    {
                        m_eType = GMLPT_String;
                    }
                }
                else if ((m_eType == GMLPT_Untyped || m_eType == GMLPT_Time) &&
                         IsDigitLowerOrEqual(pszValue[0], 2) &&
                         IsDigitLowerOrEqual(pszValue[1], 9) &&
                         pszValue[2] == ':' &&
                         IsDigitLowerOrEqual(pszValue[3], 5) &&
                         IsDigitLowerOrEqual(pszValue[4], 9) &&
                         pszValue[5] == ':' &&
                         IsDigitLowerOrEqual(pszValue[6], 6) &&
                         IsDigitLowerOrEqual(pszValue[7], 9) &&
                         (pszValue[8] == '\0' || pszValue[8] == '.'))
                {
                    m_eType = GMLPT_Time;
                }
                else
                {
                    m_eType = GMLPT_String;
                }
            }
        }
        else
        {
            bIsReal = valueType == CPL_VALUE_REAL;
        }

        if (m_eType == GMLPT_String)
        {
            if (bSetWidth)
            {
                // Grow the Width to the length of the string passed in.
                const int nWidth = static_cast<int>(strlen(pszValue));
                if (m_nWidth < nWidth)
                    SetWidth(nWidth);
            }
        }
        else if (m_eType == GMLPT_Untyped || m_eType == GMLPT_Integer ||
                 m_eType == GMLPT_Integer64)
        {
            if (bIsReal)
                m_eType = GMLPT_Real;
            else if (m_eType != GMLPT_Integer64)
            {
                const GIntBig nVal = CPLAtoGIntBig(pszValue);
                if (!CPL_INT64_FITS_ON_INT32(nVal))
                    m_eType = GMLPT_Integer64;
                else
                    m_eType = GMLPT_Integer;
            }
        }
        else if ((m_eType == GMLPT_IntegerList ||
                  m_eType == GMLPT_Integer64List) &&
                 bIsReal)
        {
            m_eType = GMLPT_RealList;
        }
        else if (m_eType == GMLPT_IntegerList && valueType == CPL_VALUE_INTEGER)
        {
            GIntBig nVal = CPLAtoGIntBig(pszValue);
            if (!CPL_INT64_FITS_ON_INT32(nVal))
                m_eType = GMLPT_Integer64List;
        }
    }
}

/************************************************************************/
/*                       GMLGeometryPropertyDefn                        */
/************************************************************************/

GMLGeometryPropertyDefn::GMLGeometryPropertyDefn(
    const char *pszName, const char *pszSrcElement, OGRwkbGeometryType nType,
    int nAttributeIndex, bool bNullable,
    const OGRGeomCoordinatePrecision &oCoordPrec)
    : m_pszName((pszName == nullptr || pszName[0] == '\0')
                    ? CPLStrdup(pszSrcElement)
                    : CPLStrdup(pszName)),
      m_pszSrcElement(CPLStrdup(pszSrcElement)), m_nGeometryType(nType),
      m_nAttributeIndex(nAttributeIndex), m_bNullable(bNullable),
      m_oCoordPrecision(oCoordPrec)
{
}

/************************************************************************/
/*                       ~GMLGeometryPropertyDefn                       */
/************************************************************************/

GMLGeometryPropertyDefn::~GMLGeometryPropertyDefn()
{
    CPLFree(m_pszName);
    CPLFree(m_pszSrcElement);
}

/************************************************************************/
/*                            MergeSRSName()                            */
/************************************************************************/

void GMLGeometryPropertyDefn::MergeSRSName(const std::string &osSRSName)

{
    if (!m_bSRSNameConsistent)
        return;

    if (m_osSRSName.empty())
    {
        m_osSRSName = osSRSName;
    }
    else
    {
        m_bSRSNameConsistent = osSRSName == m_osSRSName;
        if (!m_bSRSNameConsistent)
        {
            m_osSRSName.clear();
        }
    }
}
