#ifndef _TCUFORMATUTIL_HPP
#define _TCUFORMATUTIL_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Tester Core
 * ----------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief String format utilities.
 *//*--------------------------------------------------------------------*/

#include "tcuDefs.hpp"
#include "deString.h"

#include <ostream>
#include <string>
#include <sstream>
#include <iomanip>

namespace tcu
{
namespace Format
{

// Hexadecimal value formatter.
template <size_t NumDigits>
class Hex
{
public:
    Hex(uint64_t value_) : value(value_)
    {
    }

    std::ostream &toStream(std::ostream &stream) const
    {
        DE_STATIC_ASSERT(0 < NumDigits && NumDigits <= 16);

        return stream << "0x" << std::right << std::setfill('0') << std::setw(NumDigits) << std::hex << value;
    }

    std::string toString(void) const
    {
        std::stringstream stream;

        toStream(stream);
        return stream.str();
    }

private:
    uint64_t value;
};

template <size_t NumDigits>
std::ostream &operator<<(std::ostream &stream, tcu::Format::Hex<NumDigits> hex)
{
    return hex.toStream(stream);
}

// Bitfield formatter.

class BitDesc
{
public:
    uint64_t bit;
    const char *name;

    BitDesc(uint64_t bit_, const char *name_) : bit(bit_), name(name_)
    {
    }
};

#define TCU_BIT_DESC(BIT) tcu::Format::BitDesc(BIT, #BIT)

template <size_t BitfieldSize>
class Bitfield
{
public:
    Bitfield(uint64_t value, const BitDesc *begin, const BitDesc *end) : m_value(value), m_begin(begin), m_end(end)
    {
    }

    std::ostream &toStream(std::ostream &stream)
    {
        uint64_t bitsLeft = m_value;
        for (const BitDesc *curDesc = m_begin; curDesc != m_end; curDesc++)
        {
            if (curDesc->bit & bitsLeft)
            {
                if (bitsLeft != m_value)
                    stream << "|";
                stream << curDesc->name;
                bitsLeft ^= curDesc->bit;
            }
        }

        if (bitsLeft != 0)
        {
            if (bitsLeft != m_value)
                stream << "|";
            stream << Hex<BitfieldSize / 4>(bitsLeft);
        }

        return stream;
    }

private:
    uint64_t m_value;
    const BitDesc *m_begin;
    const BitDesc *m_end;
};

template <size_t BitfieldSize>
inline std::ostream &operator<<(std::ostream &stream, Bitfield<BitfieldSize> decoder)
{
    return decoder.toStream(stream);
}

// Enum formatter.
// \todo [2012-10-30 pyry] Use template for GetName.
template <typename T, size_t NumBytes = sizeof(T)>
class Enum
{
public:
    typedef const char *(*GetNameFunc)(T value);

    Enum(GetNameFunc getName, T value) : m_getName(getName), m_value(value)
    {
    }

    std::ostream &toStream(std::ostream &stream) const
    {
        const char *name = m_getName(m_value);
        if (name)
            return stream << name;
        else
            return stream << Hex<NumBytes * 2>((uint64_t)m_value);
    }

    std::string toString(void) const
    {
        const char *name = m_getName(m_value);
        if (name)
            return std::string(name);
        else
            return Hex<NumBytes * 2>((uint64_t)m_value).toString();
    }

private:
    const GetNameFunc m_getName;
    const T m_value;
};

template <typename T, size_t NumBytes>
inline std::ostream &operator<<(std::ostream &stream, const Enum<T, NumBytes> &fmt)
{
    return fmt.toStream(stream);
}

// Array formatters.

template <typename Iterator>
class Array
{
public:
    Iterator begin;
    Iterator end;

    Array(const Iterator &begin_, const Iterator &end_) : begin(begin_), end(end_)
    {
    }
};

template <typename T>
class ArrayPointer
{
public:
    const T *arr;
    int size;

    ArrayPointer(const T *arr_, int size_) : arr(arr_), size(size_)
    {
    }
};

template <typename Iterator>
std::ostream &operator<<(std::ostream &str, const Array<Iterator> &fmt)
{
    str << "{ ";
    for (Iterator cur = fmt.begin; cur != fmt.end; ++cur)
    {
        if (cur != fmt.begin)
            str << ", ";
        str << *cur;
    }
    str << " }";
    return str;
}

template <typename T>
std::ostream &operator<<(std::ostream &str, const ArrayPointer<T> &fmt)
{
    if (fmt.arr != DE_NULL)
        return str << Array<const T *>(fmt.arr, fmt.arr + fmt.size);
    else
        return str << "(null)";
}

// Hex format iterator (useful for combining with ArrayFormatter).
// \todo [2012-10-30 pyry] Implement more generic format iterator.

template <typename T, typename Iterator = const T *>
class HexIterator
{
public:
    HexIterator(Iterator iter) : m_iter(iter)
    {
    }

    HexIterator<T, Iterator> &operator++(void)
    {
        ++m_iter;
        return *this;
    }
    HexIterator<T, Iterator> operator++(int)
    {
        return HexIterator(m_iter++);
    }

    bool operator==(const HexIterator<T, Iterator> &other) const
    {
        return m_iter == other.m_iter;
    }
    bool operator!=(const HexIterator<T, Iterator> &other) const
    {
        return m_iter != other.m_iter;
    }

#if !defined(__INTELLISENSE__)
    // Intellisense in VS2013 crashes when parsing this.
    Hex<sizeof(T) * 2> operator*(void) const
    {
        return Hex<sizeof(T) * 2>(*m_iter);
    }
#endif

private:
    Iterator m_iter;
};

} // namespace Format

template <int Bits>
inline uint64_t makeMask64(void)
{
    return (1ull << Bits) - 1;
}
template <>
inline uint64_t makeMask64<64>(void)
{
    return ~0ull;
}
template <typename T>
inline uint64_t toUint64(T value)
{
    return (uint64_t)value & makeMask64<sizeof(T) * 8>();
}

/** Format value as hexadecimal number. */
template <size_t NumDigits, typename T>
inline Format::Hex<NumDigits> toHex(T value)
{
    return Format::Hex<NumDigits>(toUint64(value));
}

/** Format value as hexadecimal number. */
template <typename T>
inline Format::Hex<sizeof(T) * 2> toHex(T value)
{
    return Format::Hex<sizeof(T) * 2>(toUint64(value));
}

/** Decode and format bitfield. */
template <typename T, size_t Size>
inline Format::Bitfield<sizeof(T) * 8> formatBitfield(T value, const Format::BitDesc (&desc)[Size])
{
    return Format::Bitfield<sizeof(T) * 8>((uint64_t)value, &desc[0], &desc[Size]);
}

/** Format array contents. */
template <typename Iterator>
inline Format::Array<Iterator> formatArray(const Iterator &begin, const Iterator &end)
{
    return Format::Array<Iterator>(begin, end);
}

/** Format array contents. */
template <typename T>
inline Format::ArrayPointer<T> formatArray(const T *arr, int size)
{
    return Format::ArrayPointer<T>(arr, size);
}

/** Format array contents. */
template <typename T, int Size>
inline Format::ArrayPointer<T> formatArray(const T (&arr)[Size])
{
    return Format::ArrayPointer<T>(arr, Size);
}

} // namespace tcu

#endif // _TCUFORMATUTIL_HPP
