//
// Copyright 2018 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.
//
// SeparateArrayConstructorStatements splits statements that are array constructors and drops all of
// their constant arguments. For example, a statement like:
//   int[2](0, i++);
// Will be changed to:
//   i++;

#include "compiler/translator/tree_ops/hlsl/SeparateArrayConstructorStatements.h"

#include "compiler/translator/tree_util/IntermTraverse.h"

namespace sh
{

namespace
{

void SplitConstructorArgs(const TIntermSequence &originalArgs, TIntermSequence *argsOut)
{
    for (TIntermNode *arg : originalArgs)
    {
        TIntermTyped *argTyped = arg->getAsTyped();
        if (argTyped->hasSideEffects())
        {
            TIntermAggregate *argAggregate = argTyped->getAsAggregate();
            if (argTyped->isArray() && argAggregate && argAggregate->isConstructor())
            {
                SplitConstructorArgs(*argAggregate->getSequence(), argsOut);
            }
            else
            {
                argsOut->push_back(argTyped);
            }
        }
    }
}

class SeparateArrayConstructorStatementsTraverser : public TIntermTraverser
{
  public:
    SeparateArrayConstructorStatementsTraverser();

    bool visitAggregate(Visit visit, TIntermAggregate *node) override;
};

SeparateArrayConstructorStatementsTraverser::SeparateArrayConstructorStatementsTraverser()
    : TIntermTraverser(true, false, false)
{}

bool SeparateArrayConstructorStatementsTraverser::visitAggregate(Visit visit,
                                                                 TIntermAggregate *node)
{
    TIntermBlock *parentAsBlock = getParentNode()->getAsBlock();
    if (!parentAsBlock)
    {
        return false;
    }
    if (!node->isArray() || !node->isConstructor())
    {
        return false;
    }

    TIntermSequence constructorArgs;
    SplitConstructorArgs(*node->getSequence(), &constructorArgs);
    mMultiReplacements.emplace_back(parentAsBlock, node, std::move(constructorArgs));

    return false;
}

}  // namespace

bool SeparateArrayConstructorStatements(TCompiler *compiler, TIntermBlock *root)
{
    SeparateArrayConstructorStatementsTraverser traverser;
    root->traverse(&traverser);
    return traverser.updateTree(compiler, root);
}

}  // namespace sh
