//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

#include "compiler/translator/tree_ops/msl/IntroduceVertexIndexID.h"
#include "compiler/translator/StaticType.h"
#include "compiler/translator/msl/AstHelpers.h"
#include "compiler/translator/msl/IntermRebuild.h"
#include "compiler/translator/tree_util/BuiltIn.h"
using namespace sh;

////////////////////////////////////////////////////////////////////////////////

namespace
{

constexpr const TVariable kgl_VertexIDMetal(BuiltInId::gl_VertexID,
                                            ImmutableString("gl_VertexID"),
                                            SymbolType::BuiltIn,
                                            TExtension::UNDEFINED,
                                            StaticType::Get<EbtUInt, EbpHigh, EvqVertexID, 1, 1>());

constexpr const TVariable kgl_instanceIdMetal(
    BuiltInId::gl_InstanceID,
    ImmutableString("instanceIdMod"),
    SymbolType::AngleInternal,
    TExtension::UNDEFINED,
    StaticType::Get<EbtUInt, EbpHigh, EvqInstanceID, 1, 1>());

constexpr const TVariable kgl_baseInstanceMetal(
    BuiltInId::gl_BaseInstance,
    ImmutableString("baseInstance"),
    SymbolType::AngleInternal,
    TExtension::UNDEFINED,
    StaticType::Get<EbtUInt, EbpHigh, EvqInstanceID, 1, 1>());

class Rewriter : public TIntermRebuild
{
  public:
    Rewriter(TCompiler &compiler) : TIntermRebuild(compiler, true, true) {}

  private:
    PreResult visitFunctionDefinitionPre(TIntermFunctionDefinition &node) override
    {
        if (node.getFunction()->isMain())
        {
            const TFunction *mainFunction = node.getFunction();
            bool needsVertexId            = true;
            bool needsInstanceId          = true;
            std::vector<const TVariable *> mVariablesToIntroduce;
            for (size_t i = 0; i < mainFunction->getParamCount(); ++i)
            {
                const TVariable *param = mainFunction->getParam(i);
                Name instanceIDName =
                    Pipeline{Pipeline::Type::InstanceId, nullptr}.getStructInstanceName(
                        Pipeline::Variant::Modified);
                if (Name(*param) == instanceIDName)
                {
                    needsInstanceId = false;
                }
                else if (param->getType().getQualifier() == TQualifier::EvqVertexID)
                {
                    needsVertexId = false;
                }
            }
            if (needsInstanceId)
            {
                // Ensure these variables are present because they are required for XFB emulation.
                mVariablesToIntroduce.push_back(&kgl_instanceIdMetal);
                mVariablesToIntroduce.push_back(&kgl_baseInstanceMetal);
            }
            if (needsVertexId)
            {
                mVariablesToIntroduce.push_back(&kgl_VertexIDMetal);
            }
            const TFunction &newFunction = CloneFunctionAndAppendParams(
                mSymbolTable, nullptr, *node.getFunction(), mVariablesToIntroduce);
            TIntermFunctionPrototype *newProto = new TIntermFunctionPrototype(&newFunction);
            return new TIntermFunctionDefinition(newProto, node.getBody());
        }
        return node;
    }
};

}  // anonymous namespace

////////////////////////////////////////////////////////////////////////////////

bool sh::IntroduceVertexAndInstanceIndex(TCompiler &compiler, TIntermBlock &root)
{
    if (!Rewriter(compiler).rebuildRoot(root))
    {
        return false;
    }
    return true;
}
