#ifndef _ES31FPROGRAMINTERFACEDEFINITIONUTIL_HPP
#define _ES31FPROGRAMINTERFACEDEFINITIONUTIL_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.1 Module
 * -------------------------------------------------
 *
 * 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 Program interface utilities
 *//*--------------------------------------------------------------------*/

#include "tcuDefs.hpp"
#include "tes31TestCase.hpp"
#include "gluShaderProgram.hpp"
#include "es31fProgramInterfaceDefinition.hpp"

namespace deqp
{
namespace gles31
{
namespace Functional
{
namespace ProgramInterfaceDefinition
{

class Program;

class VariablePathComponent
{
public:
    VariablePathComponent(void) : m_type(TYPE_LAST)
    {
    }
    VariablePathComponent(const glu::VarType *type) : m_type(TYPE_TYPE)
    {
        m_data.type = type;
    }
    VariablePathComponent(const glu::InterfaceBlock *block) : m_type(TYPE_INTERFACEBLOCK)
    {
        m_data.block = block;
    }
    VariablePathComponent(const glu::VariableDeclaration *decl) : m_type(TYPE_DECLARATION)
    {
        m_data.declaration = decl;
    }

    VariablePathComponent(const VariablePathComponent &other) : m_data(other.m_data), m_type(other.m_type)
    {
    }
    VariablePathComponent &operator=(const VariablePathComponent &other)
    {
        m_type = other.m_type;
        m_data = other.m_data;
        return *this;
    }

    bool isVariableType(void) const
    {
        return m_type == TYPE_TYPE;
    }
    bool isInterfaceBlock(void) const
    {
        return m_type == TYPE_INTERFACEBLOCK;
    }
    bool isDeclaration(void) const
    {
        return m_type == TYPE_DECLARATION;
    }

    const glu::VarType *getVariableType(void) const
    {
        DE_ASSERT(isVariableType());
        return m_data.type;
    }
    const glu::InterfaceBlock *getInterfaceBlock(void) const
    {
        DE_ASSERT(isInterfaceBlock());
        return m_data.block;
    }
    const glu::VariableDeclaration *getDeclaration(void) const
    {
        DE_ASSERT(isDeclaration());
        return m_data.declaration;
    }

private:
    enum Type
    {
        TYPE_TYPE,
        TYPE_INTERFACEBLOCK,
        TYPE_DECLARATION,

        TYPE_LAST
    };

    union Data
    {
        const glu::VarType *type;
        const glu::InterfaceBlock *block;
        const glu::VariableDeclaration *declaration;

        Data(void) : type(DE_NULL)
        {
        }
    } m_data;

    Type m_type;
};

struct VariableSearchFilter
{
private:
    VariableSearchFilter(void);

public:
    static VariableSearchFilter createShaderTypeFilter(glu::ShaderType);
    static VariableSearchFilter createStorageFilter(glu::Storage);
    static VariableSearchFilter createShaderTypeStorageFilter(glu::ShaderType, glu::Storage);

    static VariableSearchFilter logicalOr(const VariableSearchFilter &a, const VariableSearchFilter &b);
    static VariableSearchFilter logicalAnd(const VariableSearchFilter &a, const VariableSearchFilter &b);

    bool matchesFilter(const ProgramInterfaceDefinition::Shader *shader) const;
    bool matchesFilter(const glu::VariableDeclaration &variable) const;
    bool matchesFilter(const glu::InterfaceBlock &block) const;

    uint32_t getShaderTypeBits(void) const
    {
        return m_shaderTypeBits;
    }
    uint32_t getStorageBits(void) const
    {
        return m_storageBits;
    }

private:
    uint32_t m_shaderTypeBits;
    uint32_t m_storageBits;
};

struct ShaderResourceUsage
{
    int numInputs;
    int numInputVectors;
    int numInputComponents;
    int numOutputs;
    int numOutputVectors;
    int numOutputComponents;
    int numPatchInputComponents;
    int numPatchOutputComponents;

    int numDefaultBlockUniformComponents;
    int numCombinedUniformComponents;
    int numUniformVectors;

    int numSamplers;
    int numImages;

    int numAtomicCounterBuffers;
    int numAtomicCounters;

    int numUniformBlocks;
    int numShaderStorageBlocks;
};

struct ProgramResourceUsage
{
    int uniformBufferMaxBinding;
    int uniformBufferMaxSize;
    int numUniformBlocks;
    int numCombinedVertexUniformComponents;
    int numCombinedFragmentUniformComponents;
    int numCombinedGeometryUniformComponents;
    int numCombinedTessControlUniformComponents;
    int numCombinedTessEvalUniformComponents;
    int shaderStorageBufferMaxBinding;
    int shaderStorageBufferMaxSize;
    int numShaderStorageBlocks;
    int numVaryingComponents;
    int numVaryingVectors;
    int numCombinedSamplers;
    int atomicCounterBufferMaxBinding;
    int atomicCounterBufferMaxSize;
    int numAtomicCounterBuffers;
    int numAtomicCounters;
    int maxImageBinding;
    int numCombinedImages;
    int numCombinedOutputResources;
    int numXFBInterleavedComponents;
    int numXFBSeparateAttribs;
    int numXFBSeparateComponents;
    int fragmentOutputMaxBinding;
};

} // namespace ProgramInterfaceDefinition

enum ResourceNameGenerationFlag
{
    RESOURCE_NAME_GENERATION_FLAG_DEFAULT                     = 0x0,
    RESOURCE_NAME_GENERATION_FLAG_TOP_LEVEL_BUFFER_VARIABLE   = 0x1,
    RESOURCE_NAME_GENERATION_FLAG_TRANSFORM_FEEDBACK_VARIABLE = 0x2,

    RESOURCE_NAME_GENERATION_FLAG_MASK = 0x3
};

bool programContainsIOBlocks(const ProgramInterfaceDefinition::Program *program);
bool shaderContainsIOBlocks(const ProgramInterfaceDefinition::Shader *shader);
glu::ShaderType getProgramTransformFeedbackStage(const ProgramInterfaceDefinition::Program *program);
std::vector<std::string> getProgramInterfaceResourceList(const ProgramInterfaceDefinition::Program *program,
                                                         ProgramInterface interface);
std::vector<std::string> getProgramInterfaceBlockMemberResourceList(const glu::InterfaceBlock &interfaceBlock);
const char *getUnusedZeroUniformName();
glu::ProgramSources generateProgramInterfaceProgramSources(const ProgramInterfaceDefinition::Program *program);
bool findProgramVariablePathByPathName(std::vector<ProgramInterfaceDefinition::VariablePathComponent> &typePath,
                                       const ProgramInterfaceDefinition::Program *program, const std::string &pathName,
                                       const ProgramInterfaceDefinition::VariableSearchFilter &filter);
void generateVariableTypeResourceNames(std::vector<std::string> &resources, const std::string &name,
                                       const glu::VarType &type, uint32_t resourceNameGenerationFlags);
ProgramInterfaceDefinition::ShaderResourceUsage getShaderResourceUsage(
    const ProgramInterfaceDefinition::Program *program, const ProgramInterfaceDefinition::Shader *shader);
ProgramInterfaceDefinition::ProgramResourceUsage getCombinedProgramResourceUsage(
    const ProgramInterfaceDefinition::Program *program);

} // namespace Functional
} // namespace gles31
} // namespace deqp

#endif // _ES31FPROGRAMINTERFACEDEFINITIONUTIL_HPP
