#ifndef _RSGVARIABLETYPE_HPP
#define _RSGVARIABLETYPE_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Random Shader Generator
 * ----------------------------------------------------
 *
 * 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 Variable Type class.
 *//*--------------------------------------------------------------------*/

#include "rsgDefs.hpp"

#include <vector>
#include <string>

namespace rsg
{

class TokenStream;

class VariableType
{
public:
    enum Type
    {
        TYPE_VOID = 0,
        TYPE_FLOAT,
        TYPE_INT,
        TYPE_BOOL,
        TYPE_STRUCT,
        TYPE_ARRAY,
        TYPE_SAMPLER_2D,
        TYPE_SAMPLER_CUBE,

        TYPE_LAST
    };

    enum Precision
    {
        PRECISION_NONE = 0,
        PRECISION_LOW,
        PRECISION_MEDIUM,
        PRECISION_HIGH,

        PRECISION_LAST
    };

    class Member
    {
    public:
        Member(void) : m_type(DE_NULL), m_name()
        {
        }

        Member(const VariableType &type, const char *name) : m_type(new VariableType(type)), m_name(name)
        {
        }

        ~Member(void)
        {
            delete m_type;
        }

        Member(const Member &other) : m_type(DE_NULL), m_name(other.m_name)
        {
            if (other.m_type)
                m_type = new VariableType(*other.m_type);
        }

        Member &operator=(const Member &other)
        {
            if (this == &other)
                return *this;

            delete m_type;

            m_type = DE_NULL;
            m_name = other.m_name;

            if (other.m_type)
                m_type = new VariableType(*other.m_type);

            return *this;
        }

        bool operator!=(const Member &other) const
        {
            if (m_name != other.m_name)
                return true;
            if (!!m_type != !!other.m_type)
                return true;
            if (m_type && *m_type != *other.m_type)
                return true;
            return false;
        }

        bool operator==(const Member &other) const
        {
            return !(*this != other);
        }

        const VariableType &getType(void) const
        {
            return *m_type;
        }
        const char *getName(void) const
        {
            return m_name.c_str();
        }

    private:
        VariableType *m_type;
        std::string m_name;
    };

    VariableType(void);
    VariableType(Type baseType, int numElements = 0);
    VariableType(Type baseType, const VariableType &elementType, int numElements);
    VariableType(Type baseType, const char *typeName);
    ~VariableType(void);

    Type getBaseType(void) const
    {
        return m_baseType;
    }
    Precision getPrecision(void) const
    {
        return m_precision;
    }
    const char *getTypeName(void) const
    {
        return m_typeName.c_str();
    }
    int getNumElements(void) const
    {
        return m_numElements;
    }
    const VariableType &getElementType(void) const;

    const std::vector<Member> &getMembers(void) const
    {
        return m_members;
    }
    std::vector<Member> &getMembers(void)
    {
        return m_members;
    }

    int getScalarSize(void) const;
    int getElementScalarOffset(int elementNdx) const;
    int getMemberScalarOffset(int memberNdx) const;

    bool operator!=(const VariableType &other) const;
    bool operator==(const VariableType &other) const;

    VariableType &operator=(const VariableType &other);
    VariableType(const VariableType &other);

    void tokenizeShortType(TokenStream &str) const;

    bool isStruct(void) const
    {
        return m_baseType == TYPE_STRUCT;
    }
    bool isArray(void) const
    {
        return m_baseType == TYPE_ARRAY;
    }
    bool isFloatOrVec(void) const
    {
        return m_baseType == TYPE_FLOAT;
    }
    bool isIntOrVec(void) const
    {
        return m_baseType == TYPE_INT;
    }
    bool isBoolOrVec(void) const
    {
        return m_baseType == TYPE_BOOL;
    }
    bool isSampler(void) const
    {
        return m_baseType == TYPE_SAMPLER_2D || m_baseType == TYPE_SAMPLER_CUBE;
    }
    bool isVoid(void) const
    {
        return m_baseType == TYPE_VOID;
    }

    static const VariableType &getScalarType(Type baseType);

private:
    Type m_baseType;
    Precision m_precision;
    std::string m_typeName;
    int m_numElements;
    VariableType *m_elementType;
    std::vector<Member> m_members;
};

inline VariableType::VariableType(void)
    : m_baseType(TYPE_VOID)
    , m_precision(PRECISION_NONE)
    , m_typeName()
    , m_numElements(0)
    , m_elementType(DE_NULL)
{
}

inline VariableType::VariableType(Type baseType, int numElements)
    : m_baseType(baseType)
    , m_precision(PRECISION_NONE)
    , m_typeName()
    , m_numElements(numElements)
    , m_elementType(DE_NULL)
{
    DE_ASSERT(baseType != TYPE_ARRAY && baseType != TYPE_STRUCT);
}

inline VariableType::VariableType(Type baseType, const VariableType &elementType, int numElements)
    : m_baseType(baseType)
    , m_precision(PRECISION_NONE)
    , m_typeName()
    , m_numElements(numElements)
    , m_elementType(new VariableType(elementType))
{
    DE_ASSERT(baseType == TYPE_ARRAY);
}

inline VariableType::VariableType(Type baseType, const char *typeName)
    : m_baseType(baseType)
    , m_precision(PRECISION_NONE)
    , m_typeName(typeName)
    , m_numElements(0)
    , m_elementType(DE_NULL)
{
    DE_ASSERT(baseType == TYPE_STRUCT);
}

inline VariableType::~VariableType(void)
{
    delete m_elementType;
}

} // namespace rsg

#endif // _RSGVARIABLETYPE_HPP
