/**********************************************************************
 *
 * Name:     mitab_tooldef.cpp
 * Project:  MapInfo TAB Read/Write library
 * Language: C++
 * Purpose:  Implementation of the TABToolDefTable class used to handle
 *           a dataset's table of drawing tool blocks
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
 *
 **********************************************************************
 * Copyright (c) 1999, 2000, Daniel Morissette
 *
 * 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.
 **********************************************************************/

#include "cpl_port.h"
#include "mitab.h"

#include <cstddef>
#include <algorithm>

#include "cpl_conv.h"
#include "cpl_error.h"
#include "mitab_priv.h"
#include "mitab_utils.h"

/*=====================================================================
 *                      class TABToolDefTable
 *====================================================================*/

/**********************************************************************
 *                   TABToolDefTable::TABToolDefTable()
 *
 * Constructor.
 **********************************************************************/
TABToolDefTable::TABToolDefTable()
    : m_papsPen(nullptr), m_numPen(0), m_numAllocatedPen(0),
      m_papsBrush(nullptr), m_numBrushes(0), m_numAllocatedBrushes(0),
      m_papsFont(nullptr), m_numFonts(0), m_numAllocatedFonts(0),
      m_papsSymbol(nullptr), m_numSymbols(0), m_numAllocatedSymbols(0)
{
}

/**********************************************************************
 *                   TABToolDefTable::~TABToolDefTable()
 *
 * Destructor.
 **********************************************************************/
TABToolDefTable::~TABToolDefTable()
{
    for (int i = 0; m_papsPen && i < m_numPen; i++)
        CPLFree(m_papsPen[i]);
    CPLFree(m_papsPen);

    for (int i = 0; m_papsBrush && i < m_numBrushes; i++)
        CPLFree(m_papsBrush[i]);
    CPLFree(m_papsBrush);

    for (int i = 0; m_papsFont && i < m_numFonts; i++)
        CPLFree(m_papsFont[i]);
    CPLFree(m_papsFont);

    for (int i = 0; m_papsSymbol && i < m_numSymbols; i++)
        CPLFree(m_papsSymbol[i]);
    CPLFree(m_papsSymbol);
}

/**********************************************************************
 *                   TABToolDefTable::ReadAllToolDefs()
 *
 * Read all tool definition blocks until we reach the end of the chain.
 * This function will be called only once per dataset, after that
 * we keep all the tool definitions in memory.
 *
 * Returns 0 on success, -1 on error.
 **********************************************************************/
int TABToolDefTable::ReadAllToolDefs(TABMAPToolBlock *poBlock)
{
    int nStatus = 0;

    /*-----------------------------------------------------------------
     * Loop until we reach the end of the chain of blocks... we assume
     * that the first block of data is already pre-loaded.
     *----------------------------------------------------------------*/
    while (!poBlock->EndOfChain())
    {
        const int nDefType = poBlock->ReadByte();
        switch (nDefType)
        {
            case TABMAP_TOOL_PEN:  // PEN
                if (m_numPen >= m_numAllocatedPen)
                {
                    // Realloc array by blocks of 20 items
                    m_numAllocatedPen += 20;
                    m_papsPen = static_cast<TABPenDef **>(CPLRealloc(
                        m_papsPen, m_numAllocatedPen * sizeof(TABPenDef *)));
                }
                m_papsPen[m_numPen] =
                    static_cast<TABPenDef *>(CPLCalloc(1, sizeof(TABPenDef)));

                m_papsPen[m_numPen]->nRefCount = poBlock->ReadInt32();
                m_papsPen[m_numPen]->nPixelWidth = poBlock->ReadByte();
                m_papsPen[m_numPen]->nLinePattern = poBlock->ReadByte();
                m_papsPen[m_numPen]->nPointWidth = poBlock->ReadByte();
                m_papsPen[m_numPen]->rgbColor =
                    poBlock->ReadByte() * 256 * 256 +
                    poBlock->ReadByte() * 256 + poBlock->ReadByte();

                // Adjust width value...
                // High bits for point width values > 255 are stored in the
                // pixel width byte
                if (m_papsPen[m_numPen]->nPixelWidth > 7)
                {
                    m_papsPen[m_numPen]->nPointWidth +=
                        (m_papsPen[m_numPen]->nPixelWidth - 8) * 0x100;
                    m_papsPen[m_numPen]->nPixelWidth = 1;
                }

                m_numPen++;

                break;
            case TABMAP_TOOL_BRUSH:  // BRUSH
                if (m_numBrushes >= m_numAllocatedBrushes)
                {
                    // Realloc array by blocks of 20 items
                    m_numAllocatedBrushes += 20;
                    m_papsBrush = static_cast<TABBrushDef **>(
                        CPLRealloc(m_papsBrush, m_numAllocatedBrushes *
                                                    sizeof(TABBrushDef *)));
                }
                m_papsBrush[m_numBrushes] = static_cast<TABBrushDef *>(
                    CPLCalloc(1, sizeof(TABBrushDef)));

                m_papsBrush[m_numBrushes]->nRefCount = poBlock->ReadInt32();
                m_papsBrush[m_numBrushes]->nFillPattern = poBlock->ReadByte();
                m_papsBrush[m_numBrushes]->bTransparentFill =
                    poBlock->ReadByte();
                m_papsBrush[m_numBrushes]->rgbFGColor =
                    poBlock->ReadByte() * 256 * 256 +
                    poBlock->ReadByte() * 256 + poBlock->ReadByte();
                m_papsBrush[m_numBrushes]->rgbBGColor =
                    poBlock->ReadByte() * 256 * 256 +
                    poBlock->ReadByte() * 256 + poBlock->ReadByte();

                m_numBrushes++;

                break;
            case TABMAP_TOOL_FONT:  // FONT NAME
                if (m_numFonts >= m_numAllocatedFonts)
                {
                    // Realloc array by blocks of 20 items
                    m_numAllocatedFonts += 20;
                    m_papsFont = static_cast<TABFontDef **>(
                        CPLRealloc(m_papsFont,
                                   m_numAllocatedFonts * sizeof(TABFontDef *)));
                }
                m_papsFont[m_numFonts] =
                    static_cast<TABFontDef *>(CPLCalloc(1, sizeof(TABFontDef)));

                m_papsFont[m_numFonts]->nRefCount = poBlock->ReadInt32();
                poBlock->ReadBytes(32, reinterpret_cast<GByte *>(
                                           m_papsFont[m_numFonts]->szFontName));
                m_papsFont[m_numFonts]->szFontName[32] = '\0';

                m_numFonts++;

                break;
            case TABMAP_TOOL_SYMBOL:  // SYMBOL
                if (m_numSymbols >= m_numAllocatedSymbols)
                {
                    // Realloc array by blocks of 20 items
                    m_numAllocatedSymbols += 20;
                    m_papsSymbol = static_cast<TABSymbolDef **>(
                        CPLRealloc(m_papsSymbol, m_numAllocatedSymbols *
                                                     sizeof(TABSymbolDef *)));
                }
                m_papsSymbol[m_numSymbols] = static_cast<TABSymbolDef *>(
                    CPLCalloc(1, sizeof(TABSymbolDef)));

                m_papsSymbol[m_numSymbols]->nRefCount = poBlock->ReadInt32();
                m_papsSymbol[m_numSymbols]->nSymbolNo = poBlock->ReadInt16();
                m_papsSymbol[m_numSymbols]->nPointSize = poBlock->ReadInt16();
                m_papsSymbol[m_numSymbols]->_nUnknownValue_ =
                    poBlock->ReadByte();
                m_papsSymbol[m_numSymbols]->rgbColor =
                    poBlock->ReadByte() * 256 * 256 +
                    poBlock->ReadByte() * 256 + poBlock->ReadByte();

                m_numSymbols++;

                break;
            default:
                /* Unsupported Tool type!!! */
                CPLError(CE_Failure, CPLE_NotSupported,
                         "Unsupported drawing tool type: `%d'", nDefType);
                nStatus = -1;
        }

        if (CPLGetLastErrorType() == CE_Failure)
        {
            // An error happened reading this tool definition... stop now.
            nStatus = -1;
            break;
        }
    }

    return nStatus;
}

/**********************************************************************
 *                   TABToolDefTable::WriteAllToolDefs()
 *
 * Write all tool definition structures to the TABMAPToolBlock.
 *
 * Note that at the end of this call, poBlock->CommitToFile() will have
 * been called.
 *
 * Returns 0 on success, -1 on error.
 **********************************************************************/
int TABToolDefTable::WriteAllToolDefs(TABMAPToolBlock *poBlock)
{
    int nStatus = 0;

    /*-----------------------------------------------------------------
     * Write Pen Defs
     *----------------------------------------------------------------*/
    for (int i = 0; nStatus == 0 && i < m_numPen; i++)
    {
        // The pen width is encoded over 2 bytes
        GByte byPixelWidth = 1;
        GByte byPointWidth = 0;
        if (m_papsPen[i]->nPointWidth > 0)
        {
            byPointWidth = static_cast<GByte>(m_papsPen[i]->nPointWidth & 0xff);
            if (m_papsPen[i]->nPointWidth > 255)
                byPixelWidth =
                    8 + static_cast<GByte>(m_papsPen[i]->nPointWidth / 0x100);
        }
        else
        {
            const GByte nMinWidth = 1;
            const GByte nMaxWidth = 7;
            byPixelWidth = std::min(
                std::max(m_papsPen[i]->nPixelWidth, nMinWidth), nMaxWidth);
        }

        poBlock->CheckAvailableSpace(TABMAP_TOOL_PEN);
        poBlock->WriteByte(TABMAP_TOOL_PEN);  // Def Type = Pen
        poBlock->WriteInt32(m_papsPen[i]->nRefCount);

        poBlock->WriteByte(byPixelWidth);
        poBlock->WriteByte(m_papsPen[i]->nLinePattern);
        poBlock->WriteByte(byPointWidth);
        poBlock->WriteByte(static_cast<GByte>(COLOR_R(m_papsPen[i]->rgbColor)));
        poBlock->WriteByte(static_cast<GByte>(COLOR_G(m_papsPen[i]->rgbColor)));
        poBlock->WriteByte(static_cast<GByte>(COLOR_B(m_papsPen[i]->rgbColor)));

        if (CPLGetLastErrorType() == CE_Failure)
        {
            // An error happened reading this tool definition... stop now.
            nStatus = -1;
        }
    }

    /*-----------------------------------------------------------------
     * Write Brush Defs
     *----------------------------------------------------------------*/
    for (int i = 0; nStatus == 0 && i < m_numBrushes; i++)
    {
        poBlock->CheckAvailableSpace(TABMAP_TOOL_BRUSH);

        poBlock->WriteByte(TABMAP_TOOL_BRUSH);  // Def Type = Brush
        poBlock->WriteInt32(m_papsBrush[i]->nRefCount);

        poBlock->WriteByte(m_papsBrush[i]->nFillPattern);
        poBlock->WriteByte(m_papsBrush[i]->bTransparentFill);
        poBlock->WriteByte(
            static_cast<GByte>(COLOR_R(m_papsBrush[i]->rgbFGColor)));
        poBlock->WriteByte(
            static_cast<GByte>(COLOR_G(m_papsBrush[i]->rgbFGColor)));
        poBlock->WriteByte(
            static_cast<GByte>(COLOR_B(m_papsBrush[i]->rgbFGColor)));
        poBlock->WriteByte(
            static_cast<GByte>(COLOR_R(m_papsBrush[i]->rgbBGColor)));
        poBlock->WriteByte(
            static_cast<GByte>(COLOR_G(m_papsBrush[i]->rgbBGColor)));
        poBlock->WriteByte(
            static_cast<GByte>(COLOR_B(m_papsBrush[i]->rgbBGColor)));

        if (CPLGetLastErrorType() == CE_Failure)
        {
            // An error happened reading this tool definition... stop now.
            nStatus = -1;
        }
    }

    /*-----------------------------------------------------------------
     * Write Font Defs
     *----------------------------------------------------------------*/
    for (int i = 0; nStatus == 0 && i < m_numFonts; i++)
    {
        poBlock->CheckAvailableSpace(TABMAP_TOOL_FONT);

        poBlock->WriteByte(TABMAP_TOOL_FONT);  // Def Type = Font name
        poBlock->WriteInt32(m_papsFont[i]->nRefCount);

        poBlock->WriteBytes(
            32, reinterpret_cast<GByte *>(m_papsFont[i]->szFontName));

        if (CPLGetLastErrorType() == CE_Failure)
        {
            // An error happened reading this tool definition... stop now.
            nStatus = -1;
        }
    }

    /*-----------------------------------------------------------------
     * Write Symbol Defs
     *----------------------------------------------------------------*/
    for (int i = 0; nStatus == 0 && i < m_numSymbols; i++)
    {
        poBlock->CheckAvailableSpace(TABMAP_TOOL_SYMBOL);

        poBlock->WriteByte(TABMAP_TOOL_SYMBOL);  // Def Type = Symbol
        poBlock->WriteInt32(m_papsSymbol[i]->nRefCount);

        poBlock->WriteInt16(m_papsSymbol[i]->nSymbolNo);
        poBlock->WriteInt16(m_papsSymbol[i]->nPointSize);
        poBlock->WriteByte(m_papsSymbol[i]->_nUnknownValue_);
        poBlock->WriteByte(
            static_cast<GByte>(COLOR_R(m_papsSymbol[i]->rgbColor)));
        poBlock->WriteByte(
            static_cast<GByte>(COLOR_G(m_papsSymbol[i]->rgbColor)));
        poBlock->WriteByte(
            static_cast<GByte>(COLOR_B(m_papsSymbol[i]->rgbColor)));

        if (CPLGetLastErrorType() == CE_Failure)
        {
            // An error happened reading this tool definition... stop now.
            nStatus = -1;
        }
    }

    if (nStatus == 0)
        nStatus = poBlock->CommitToFile();

    return nStatus;
}

/**********************************************************************
 *                   TABToolDefTable::GetNumPen()
 *
 * Return the number of valid pen indexes for this .MAP file
 **********************************************************************/
int TABToolDefTable::GetNumPen()
{
    return m_numPen;
}

/**********************************************************************
 *                   TABToolDefTable::GetPenDefRef()
 *
 * Return a reference to the specified Pen tool definition, or NULL if
 * specified index is invalid.
 *
 * Note that nIndex is a 1-based index.  A value of 0 indicates "none"
 * in MapInfo.
 **********************************************************************/
TABPenDef *TABToolDefTable::GetPenDefRef(int nIndex)
{
    if (nIndex > 0 && nIndex <= m_numPen)
        return m_papsPen[nIndex - 1];

    return nullptr;
}

/**********************************************************************
 *                   TABToolDefTable::AddPenDefRef()
 *
 * Either create a new PenDefRef or add a reference to an existing one.
 *
 * Return the pen index that has been attributed to this Pen tool
 * definition, or -1 if something went wrong
 *
 * Note that nIndex is a 1-based index.  A value of 0 indicates "none"
 * in MapInfo.
 **********************************************************************/
int TABToolDefTable::AddPenDefRef(TABPenDef *poNewPenDef)
{
    if (poNewPenDef == nullptr)
        return -1;

    /*-----------------------------------------------------------------
     * Check for "none" case: pattern = 0 (pattern 0 does not exist!)
     *----------------------------------------------------------------*/
    if (poNewPenDef->nLinePattern < 1)
        return 0;

    /*-----------------------------------------------------------------
     * Start by searching the list of existing pens
     *----------------------------------------------------------------*/
    int nNewPenIndex = 0;
    for (int i = 0; nNewPenIndex == 0 && i < m_numPen; i++)
    {
        TABPenDef *poDef = m_papsPen[i];
        if (poDef->nPixelWidth == poNewPenDef->nPixelWidth &&
            poDef->nLinePattern == poNewPenDef->nLinePattern &&
            poDef->nPointWidth == poNewPenDef->nPointWidth &&
            poDef->rgbColor == poNewPenDef->rgbColor)
        {
            nNewPenIndex = i + 1;  // Fount it!
            poDef->nRefCount++;
        }
    }

    /*-----------------------------------------------------------------
     * OK, we did not find a match, then create a new entry
     *----------------------------------------------------------------*/
    if (nNewPenIndex == 0)
    {
        if (m_numPen >= m_numAllocatedPen)
        {
            // Realloc array by blocks of 20 items
            m_numAllocatedPen += 20;
            m_papsPen = static_cast<TABPenDef **>(
                CPLRealloc(m_papsPen, m_numAllocatedPen * sizeof(TABPenDef *)));
        }
        m_papsPen[m_numPen] =
            static_cast<TABPenDef *>(CPLCalloc(1, sizeof(TABPenDef)));

        *m_papsPen[m_numPen] = *poNewPenDef;
        m_papsPen[m_numPen]->nRefCount = 1;
        nNewPenIndex = ++m_numPen;
    }

    return nNewPenIndex;
}

/**********************************************************************
 *                   TABToolDefTable::GetNumBrushes()
 *
 * Return the number of valid Brush indexes for this .MAP file
 **********************************************************************/
int TABToolDefTable::GetNumBrushes()
{
    return m_numBrushes;
}

/**********************************************************************
 *                   TABToolDefTable::GetBrushDefRef()
 *
 * Return a reference to the specified Brush tool definition, or NULL if
 * specified index is invalid.
 *
 * Note that nIndex is a 1-based index.  A value of 0 indicates "none"
 * in MapInfo.
 **********************************************************************/
TABBrushDef *TABToolDefTable::GetBrushDefRef(int nIndex)
{
    if (nIndex > 0 && nIndex <= m_numBrushes)
        return m_papsBrush[nIndex - 1];

    return nullptr;
}

/**********************************************************************
 *                   TABToolDefTable::AddBrushDefRef()
 *
 * Either create a new BrushDefRef or add a reference to an existing one.
 *
 * Return the Brush index that has been attributed to this Brush tool
 * definition, or -1 if something went wrong
 *
 * Note that nIndex is a 1-based index.  A value of 0 indicates "none"
 * in MapInfo.
 **********************************************************************/
int TABToolDefTable::AddBrushDefRef(TABBrushDef *poNewBrushDef)
{
    if (poNewBrushDef == nullptr)
        return -1;

    /*-----------------------------------------------------------------
     * Check for "none" case: pattern = 0 (pattern 0 does not exist!)
     *----------------------------------------------------------------*/
    if (poNewBrushDef->nFillPattern < 1)
        return 0;

    /*-----------------------------------------------------------------
     * Start by searching the list of existing Brushs
     *----------------------------------------------------------------*/
    int nNewBrushIndex = 0;
    for (int i = 0; nNewBrushIndex == 0 && i < m_numBrushes; i++)
    {
        TABBrushDef *poDef = m_papsBrush[i];
        if (poDef->nFillPattern == poNewBrushDef->nFillPattern &&
            poDef->bTransparentFill == poNewBrushDef->bTransparentFill &&
            poDef->rgbFGColor == poNewBrushDef->rgbFGColor &&
            poDef->rgbBGColor == poNewBrushDef->rgbBGColor)
        {
            nNewBrushIndex = i + 1;  // Fount it!
            poDef->nRefCount++;
        }
    }

    /*-----------------------------------------------------------------
     * OK, we did not find a match, then create a new entry
     *----------------------------------------------------------------*/
    if (nNewBrushIndex == 0)
    {
        if (m_numBrushes >= m_numAllocatedBrushes)
        {
            // Realloc array by blocks of 20 items
            m_numAllocatedBrushes += 20;
            m_papsBrush = static_cast<TABBrushDef **>(CPLRealloc(
                m_papsBrush, m_numAllocatedBrushes * sizeof(TABBrushDef *)));
        }
        m_papsBrush[m_numBrushes] =
            static_cast<TABBrushDef *>(CPLCalloc(1, sizeof(TABBrushDef)));

        *m_papsBrush[m_numBrushes] = *poNewBrushDef;
        m_papsBrush[m_numBrushes]->nRefCount = 1;
        nNewBrushIndex = ++m_numBrushes;
    }

    return nNewBrushIndex;
}

/**********************************************************************
 *                   TABToolDefTable::GetNumFonts()
 *
 * Return the number of valid Font indexes for this .MAP file
 **********************************************************************/
int TABToolDefTable::GetNumFonts()
{
    return m_numFonts;
}

/**********************************************************************
 *                   TABToolDefTable::GetFontDefRef()
 *
 * Return a reference to the specified Font tool definition, or NULL if
 * specified index is invalid.
 *
 * Note that nIndex is a 1-based index.  A value of 0 indicates "none"
 * in MapInfo.
 **********************************************************************/
TABFontDef *TABToolDefTable::GetFontDefRef(int nIndex)
{
    if (nIndex > 0 && nIndex <= m_numFonts)
        return m_papsFont[nIndex - 1];

    return nullptr;
}

/**********************************************************************
 *                   TABToolDefTable::AddFontDefRef()
 *
 * Either create a new FontDefRef or add a reference to an existing one.
 *
 * Return the Font index that has been attributed to this Font tool
 * definition, or -1 if something went wrong
 *
 * Note that nIndex is a 1-based index.  A value of 0 indicates "none"
 * in MapInfo.
 **********************************************************************/
int TABToolDefTable::AddFontDefRef(TABFontDef *poNewFontDef)
{
    if (poNewFontDef == nullptr)
        return -1;

    /*-----------------------------------------------------------------
     * Start by searching the list of existing Fonts
     *----------------------------------------------------------------*/
    int nNewFontIndex = 0;
    for (int i = 0; nNewFontIndex == 0 && i < m_numFonts; i++)
    {
        TABFontDef *poDef = m_papsFont[i];
        if (EQUAL(poDef->szFontName, poNewFontDef->szFontName))
        {
            nNewFontIndex = i + 1;  // Fount it!
            poDef->nRefCount++;
        }
    }

    /*-----------------------------------------------------------------
     * OK, we did not find a match, then create a new entry
     *----------------------------------------------------------------*/
    if (nNewFontIndex == 0)
    {
        if (m_numFonts >= m_numAllocatedFonts)
        {
            // Realloc array by blocks of 20 items
            m_numAllocatedFonts += 20;
            m_papsFont = static_cast<TABFontDef **>(CPLRealloc(
                m_papsFont, m_numAllocatedFonts * sizeof(TABFontDef *)));
        }
        m_papsFont[m_numFonts] =
            static_cast<TABFontDef *>(CPLCalloc(1, sizeof(TABFontDef)));

        *m_papsFont[m_numFonts] = *poNewFontDef;
        m_papsFont[m_numFonts]->nRefCount = 1;
        nNewFontIndex = ++m_numFonts;
    }

    return nNewFontIndex;
}

/**********************************************************************
 *                   TABToolDefTable::GetNumSymbols()
 *
 * Return the number of valid Symbol indexes for this .MAP file
 **********************************************************************/
int TABToolDefTable::GetNumSymbols()
{
    return m_numSymbols;
}

/**********************************************************************
 *                   TABToolDefTable::GetSymbolDefRef()
 *
 * Return a reference to the specified Symbol tool definition, or NULL if
 * specified index is invalid.
 *
 * Note that nIndex is a 1-based index.  A value of 0 indicates "none"
 * in MapInfo.
 **********************************************************************/
TABSymbolDef *TABToolDefTable::GetSymbolDefRef(int nIndex)
{
    if (nIndex > 0 && nIndex <= m_numSymbols)
        return m_papsSymbol[nIndex - 1];

    return nullptr;
}

/**********************************************************************
 *                   TABToolDefTable::AddSymbolDefRef()
 *
 * Either create a new SymbolDefRef or add a reference to an existing one.
 *
 * Return the Symbol index that has been attributed to this Symbol tool
 * definition, or -1 if something went wrong
 *
 * Note that nIndex is a 1-based index.  A value of 0 indicates "none"
 * in MapInfo.
 **********************************************************************/
int TABToolDefTable::AddSymbolDefRef(TABSymbolDef *poNewSymbolDef)
{
    if (poNewSymbolDef == nullptr)
        return -1;

    /*-----------------------------------------------------------------
     * Start by searching the list of existing Symbols
     *----------------------------------------------------------------*/
    int nNewSymbolIndex = 0;
    for (int i = 0; nNewSymbolIndex == 0 && i < m_numSymbols; i++)
    {
        TABSymbolDef *poDef = m_papsSymbol[i];
        if (poDef->nSymbolNo == poNewSymbolDef->nSymbolNo &&
            poDef->nPointSize == poNewSymbolDef->nPointSize &&
            poDef->_nUnknownValue_ == poNewSymbolDef->_nUnknownValue_ &&
            poDef->rgbColor == poNewSymbolDef->rgbColor)
        {
            nNewSymbolIndex = i + 1;  // Fount it!
            poDef->nRefCount++;
        }
    }

    /*-----------------------------------------------------------------
     * OK, we did not find a match, then create a new entry
     *----------------------------------------------------------------*/
    if (nNewSymbolIndex == 0)
    {
        if (m_numSymbols >= m_numAllocatedSymbols)
        {
            // Realloc array by blocks of 20 items
            m_numAllocatedSymbols += 20;
            m_papsSymbol = static_cast<TABSymbolDef **>(CPLRealloc(
                m_papsSymbol, m_numAllocatedSymbols * sizeof(TABSymbolDef *)));
        }
        m_papsSymbol[m_numSymbols] =
            static_cast<TABSymbolDef *>(CPLCalloc(1, sizeof(TABSymbolDef)));

        *m_papsSymbol[m_numSymbols] = *poNewSymbolDef;
        m_papsSymbol[m_numSymbols]->nRefCount = 1;
        nNewSymbolIndex = ++m_numSymbols;
    }

    return nNewSymbolIndex;
}

/**********************************************************************
 *                   TABToolDefTable::GetMinVersionNumber()
 *
 * Returns the minimum file version number that can accept all the
 * tool objects currently defined.
 *
 * Default is 300, and currently 450 can be returned if file contains
 * pen widths defined in points.
 **********************************************************************/
int TABToolDefTable::GetMinVersionNumber()
{
    int i, nVersion = 300;

    /*-----------------------------------------------------------------
     * Scan Pen Defs
     *----------------------------------------------------------------*/
    for (i = 0; i < m_numPen; i++)
    {
        if (m_papsPen[i]->nPointWidth > 0)
        {
            nVersion = std::max(nVersion, 450);  // Raise version to 450
        }
    }

    return nVersion;
}
