/*
	This file is part of solidity.
	solidity is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	solidity is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with solidity.  If not, see .
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
using namespace solidity;
using namespace solidity::yul;
using OptionalStatements = std::optional>;
namespace {
OptionalStatements replaceConstArgSwitch(Switch& _switchStmt, u256 const& _constExprVal)
{
	Block* matchingCaseBlock = nullptr;
	Case* defaultCase = nullptr;
	for (auto& _case: _switchStmt.cases)
	{
		if (_case.value && valueOfLiteral(*_case.value) == _constExprVal)
		{
			matchingCaseBlock = &_case.body;
			break;
		}
		else if (!_case.value)
			defaultCase = &_case;
	}
	if (!matchingCaseBlock && defaultCase)
		matchingCaseBlock = &defaultCase->body;
	if (matchingCaseBlock)
		return util::make_vector(std::move(*matchingCaseBlock));
	else
		return optional>{vector{}};
}
}
void StructuralSimplifier::run(OptimiserStepContext&, Block& _ast)
{
	StructuralSimplifier{}(_ast);
}
void StructuralSimplifier::operator()(Block& _block)
{
	simplify(_block.statements);
}
void StructuralSimplifier::simplify(std::vector& _statements)
{
	util::GenericVisitor visitor{
		util::VisitorFallback{},
		[&](If& _ifStmt) -> OptionalStatements {
			if (expressionAlwaysTrue(*_ifStmt.condition))
				return {std::move(_ifStmt.body.statements)};
			else if (expressionAlwaysFalse(*_ifStmt.condition))
				return {vector{}};
			return {};
		},
		[&](Switch& _switchStmt) -> OptionalStatements {
			if (std::optional const constExprVal = hasLiteralValue(*_switchStmt.expression))
				return replaceConstArgSwitch(_switchStmt, constExprVal.value());
			return {};
		},
		[&](ForLoop& _forLoop) -> OptionalStatements {
			if (expressionAlwaysFalse(*_forLoop.condition))
				return {std::move(_forLoop.pre.statements)};
			return {};
		}
	};
	util::iterateReplacing(
		_statements,
		[&](Statement& _stmt) -> OptionalStatements
		{
			OptionalStatements result = std::visit(visitor, _stmt);
			if (result)
				simplify(*result);
			else
				visit(_stmt);
			return result;
		}
	);
}
bool StructuralSimplifier::expressionAlwaysTrue(Expression const& _expression)
{
	if (std::optional value = hasLiteralValue(_expression))
		return *value != 0;
	else
		return false;
}
bool StructuralSimplifier::expressionAlwaysFalse(Expression const& _expression)
{
	if (std::optional value = hasLiteralValue(_expression))
		return *value == 0;
	else
		return false;
}
std::optional StructuralSimplifier::hasLiteralValue(Expression const& _expression) const
{
	if (holds_alternative(_expression))
		return valueOfLiteral(std::get(_expression));
	else
		return std::optional();
}