/*
	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 .
*/
// SPDX-License-Identifier: GPL-3.0
/**
 * Yul inspector.
 */
#include 
#include 
#include 
#pragma once
namespace solidity::yul::test
{
/**
 * Inspector class to respond to queries by the user for:
 *
 * * Stepping through and over instructions.
 * * Printing a specific or all variables.
 * * Inspecting memory, storage and calldata memory.
 */
class Inspector
{
public:
	enum class NodeAction
	{
		RunNode,
		StepThroughNode,
	};
	Inspector(std::string const& _source, InterpreterState const& _state)
		:m_source(_source), m_state(_state) {}
	/* Asks the user what action to take.
	 * @returns NodeAction::RunNode if the current AST node (and all children nodes!) should be
	 *          processed without stopping, else NodeAction::StepThroughNode.
	 */
	NodeAction queryUser(DebugData const& _data, std::map const& _variables);
	void stepMode(NodeAction _action) { m_stepMode = _action; }
	std::string const& source() const { return m_source; }
	void interactiveVisit(DebugData const& _debugData, std::map const& _variables, std::function _visitNode)
	{
		Inspector::NodeAction action = queryUser(_debugData, _variables);
		if (action == NodeAction::RunNode)
		{
			// user requested to run the whole node without stopping
			stepMode(Inspector::NodeAction::RunNode);
			_visitNode();
			// Reset step mode back
			stepMode(Inspector::NodeAction::StepThroughNode);
		}
		else
			_visitNode();
	}
private:
	std::string currentSource(DebugData const& _data) const;
	/// Source of the file
	std::string const& m_source;
	/// State of the interpreter
	InterpreterState const& m_state;
	/// Last user query command
	std::string m_lastInput;
	/// Used to run AST nodes without user interaction
	NodeAction m_stepMode = NodeAction::StepThroughNode;
};
/**
 * Yul Interpreter with inspection. Allows the user to go through the code step
 * by step and inspect the state using the `Inspector` class
 */
class InspectedInterpreter: public Interpreter
{
public:
	static void run(
		std::shared_ptr _inspector,
		InterpreterState& _state,
		Dialect const& _dialect,
		Block const& _ast,
		bool _disableExternalCalls,
		bool _disableMemoryTracing
	);
	InspectedInterpreter(
		std::shared_ptr _inspector,
		InterpreterState& _state,
		Dialect const& _dialect,
		Scope& _scope,
		bool _disableExternalCalls,
		bool _disableMemoryTracing,
		std::map _variables = {}
	):
		Interpreter(_state, _dialect, _scope, _disableExternalCalls, _disableMemoryTracing, _variables),
		m_inspector(_inspector)
	{
	}
	void operator()(ExpressionStatement const& _node) override { helper(_node); }
	void operator()(Assignment const& _node) override { helper(_node); }
	void operator()(VariableDeclaration const& _node) override { helper(_node); }
	void operator()(If const& _node) override { helper(_node); }
	void operator()(Switch const& _node) override { helper(_node); }
	void operator()(ForLoop const& _node) override { helper(_node); }
	void operator()(Break const& _node) override { helper(_node); }
	void operator()(Continue const& _node) override { helper(_node); }
	void operator()(Leave const& _node) override { helper(_node); }
	void operator()(Block const& _node) override { helper(_node); }
protected:
	/// Asserts that the expression evaluates to exactly one value and returns it.
	u256 evaluate(Expression const& _expression) override;
	/// Evaluates the expression and returns its value.
	std::vector evaluateMulti(Expression const& _expression) override;
private:
	std::shared_ptr m_inspector;
	template 
	void helper(ConcreteNode const& _node)
	{
		m_inspector->interactiveVisit(*_node.debugData, m_variables, [&]() {
			Interpreter::operator()(_node);
		});
	}
};
class InspectedExpressionEvaluator: public ExpressionEvaluator
{
public:
	InspectedExpressionEvaluator(
		std::shared_ptr _inspector,
		InterpreterState& _state,
		Dialect const& _dialect,
		Scope& _scope,
		std::map const& _variables,
		bool _disableExternalCalls,
		bool _disableMemoryTrace
	):
		ExpressionEvaluator(_state, _dialect, _scope, _variables, _disableExternalCalls, _disableMemoryTrace),
		m_inspector(_inspector)
	{}
	template 
	void helper(ConcreteNode const& _node)
	{
		m_inspector->interactiveVisit(*_node.debugData, m_variables, [&]() {
			ExpressionEvaluator::operator()(_node);
		});
	}
	void operator()(Literal const& _node) override { helper(_node); }
	void operator()(Identifier const& _node) override { helper(_node); }
	void operator()(FunctionCall const& _node) override { helper(_node); }
protected:
	std::unique_ptr makeInterpreterCopy(std::map _variables = {}) const override
	{
		return std::make_unique(
			m_inspector,
			m_state,
			m_dialect,
			m_scope,
			m_disableExternalCalls,
			m_disableMemoryTrace,
			std::move(_variables)
		);
	}
	std::unique_ptr makeInterpreterNew(InterpreterState& _state, Scope& _scope) const override
	{
		return std::make_unique(
			std::make_unique(
				m_inspector->source(),
				_state
			),
			_state,
			m_dialect,
			_scope,
			m_disableExternalCalls,
			m_disableMemoryTrace
		);
	}
private:
	std::shared_ptr m_inspector;
};
}