//----------------------------------------------------------------------------
//
// TSDuck - The MPEG Transport Stream Toolkit
// Copyright (c) 2005-2020, Thierry Lelegard
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
//----------------------------------------------------------------------------
//!
//!  @file
//!  Representation of a Tag, Length, Value (TVL) syntax.
//!
//----------------------------------------------------------------------------

#pragma once
#include "tsPlatform.h"
#include "tsCerrReport.h"
#include "tsSafePtr.h"
#include "tsMutex.h"
#include "tsArgs.h"

namespace ts {
    //!
    //! Representation of a Tag, Length, Value (TVL) syntax.
    //! @ingroup cmd
    //!
    //! An instance of this class describe how a part of a data area, typically
    //! in the payload of an MPEG section, is structured as a suite of TLV records.
    //! The complete suite of all contiguous TLV records is named the "TLV area"
    //! inside the larger data area.
    //!
    //! A TLV area is described by the following values:
    //!
    //! - The @e start index in the data area. This is the starting index of
    //!   the first @e tag field. A negative value for @e start means "auto".
    //!   The best match of a "TLV area" will be found automatically.
    //! - The @e size in bytes of the TLV area. A negative value means "auto",
    //!   up to the last consistent TLV record before the end of the data area.
    //! - The @e tag @e size in bytes. The valid sizes are 1 (default), 2 and 4.
    //! - The @e length @e size in bytes. The valid sizes are 1 (default), 2 and 4.
    //! - The byte order of the @e tag and @e length fields. The default is MSB.
    //!
    class TSDUCKDLL TLVSyntax
    {
    public:
        //!
        //! Default constructor.
        //! @param [in] start Starting index of the "TLV area" in the "data area". A negative value means "auto".
        //! @param [in] size Size in bytes of the "TLV area". A negative value means "auto".
        //! @param [in] tagSize Size in bytes of the @e tag field. The valid sizes are 1 (default), 2 and 4.
        //! @param [in] lengthSize Size in bytes of the @e length field. The valid sizes are 1 (default), 2 and 4.
        //! @param [in] msb If true, the @e tag and @e length fields are represented in MSB-first.
        //! @param [in,out] report Where to report errors.
        //!
        TLVSyntax(int start = -1, int size = -1, size_t tagSize = 1, size_t lengthSize = 1, bool msb = true, Report& report = CERR);

        //!
        //! Set the values of a TLVSyntax object.
        //! @param [in] start Starting index of the "TLV area" in the "data area". A negative value means "auto".
        //! @param [in] size Size in bytes of the "TLV area". A negative value means "auto".
        //! @param [in] tagSize Size in bytes of the @e tag field. The valid sizes are 1 (default), 2 and 4.
        //! @param [in] lengthSize Size in bytes of the @e length field. The valid sizes are 1 (default), 2 and 4.
        //! @param [in] msb If true, the @e tag and @e length fields are represented in MSB-first.
        //! @param [in,out] report Where to report errors.
        //! @return True on success, false on error (invalid sizes).
        //!
        bool set(int start = -1, int size = -1, size_t tagSize = 1, size_t lengthSize = 1, bool msb = true, Report& report = CERR);

        //!
        //! Update the TLV syntax to automatically locate the TLV area.
        //!
        void setAutoLocation()
        {
            _start = _size = -1;
        }

        //!
        //! Get the size in bytes of the Tag field.
        //! @return The size in bytes of the Tag field.
        //!
        size_t getTagSize() const
        {
            return _tagSize;
        }

        //!
        //! Get the size in bytes of the Length field.
        //! @return The size in bytes of the Length field.
        //!
        size_t getLengthSize() const
        {
            return _lengthSize;
        }

        //!
        //! Set the values of a TLVSyntax object from a string representation.
        //! Typically used to ingest command line parameters.
        //! @param [in] s The string representation in the form "start,size,tagSize,lengthSize,order".
        //! The @e start and @e size fields can be set to "auto".
        //! The @e order field shall be either "msb" or lsb".
        //! All fields are optional. Missing fields receive the default value as defined in the constructor.
        //! @param [in,out] report Where to report errors.
        //! @return True on success, false on error.
        //!
        bool fromString(const UString& s, Report& report = CERR);

        //!
        //! Extract a tag and length value from a data area.
        //! @param [in] data Address of the TLV record.
        //! @param [in] size Total size in bytes of the data area after @a data address.
        //! @param [out] tag Returned tag value.
        //! @param [out] length Returned length value.
        //! @return The size of the tag and length field, ie. the offset after @a data
        //! where the value field starts. Return 0 on error (if the TLV record does not fit).
        //!
        size_t getTagAndLength(const uint8_t* data, size_t size, uint32_t& tag, size_t& length) const;

        //!
        //! Locate the "TLV area" inside a data area.
        //! If the @e start or @e size are set to "auto", return the best match.
        //! @param [in] data Address of the TLV data area.
        //! @param [in] dataSize Total size in bytes of the data area.
        //! @param [out] tlvStart Starting offset of the "TLV area" after @a data.
        //! @param [out] tlvSize Size in bytes of the "TLV area". Zero if no suitable TLV area was found.
        //! @return True if TLV area found, false otherwise.
        //!
        bool locateTLV(const uint8_t* data, size_t dataSize, size_t& tlvStart, size_t& tlvSize) const;

        //!
        //! Comparison operator, typically used to sort containers of TLVSyntax.
        //! @param [in] other Other instance to compare.
        //! @return True if this object logically preceeds @a other.
        //!
        bool operator<(const TLVSyntax& other) const
        {
            return _start < other._start;
        }

    private:
        int    _start;
        int    _size;
        size_t _tagSize;
        size_t _lengthSize;
        bool   _msb;

        // Compute the size of the longest TLV area starting at tlvStart.
        size_t longestTLV(const uint8_t* data, size_t dataSize, size_t tlvStart) const;

        // Get an integer in the right byte order.
        uint32_t getInt(const uint8_t* data, size_t size) const;
    };

    //!
    //! A vector of TLVSyntax.
    //!
    typedef std::vector<TLVSyntax> TLVSyntaxVector;
}
