#pragma once

#include "Common.h"

namespace llvm
{
	class APInt;
}

namespace dev
{
namespace evmjit
{

/// Virtual machine bytecode instruction.
enum class Instruction: uint8_t
{
	STOP = 0x00,		///< halts execution
	ADD,				///< addition operation
	MUL,				///< mulitplication operation
	SUB,				///< subtraction operation
	DIV,				///< integer division operation
	SDIV,				///< signed integer division operation
	MOD,				///< modulo remainder operation
	SMOD,				///< signed modulo remainder operation
	ADDMOD,				///< unsigned modular addition
	MULMOD,				///< unsigned modular multiplication
	EXP,				///< exponential operation
	SIGNEXTEND,			///< extend length of signed integer

	LT = 0x10,			///< less-than comparision
	GT,					///< greater-than comparision
	SLT,				///< signed less-than comparision
	SGT,				///< signed greater-than comparision
	EQ,					///< equality comparision
	ISZERO,				///< simple not operator
	AND,				///< bitwise AND operation
	OR,					///< bitwise OR operation
	XOR,				///< bitwise XOR operation
	NOT,				///< bitwise NOT opertation
	BYTE,				///< retrieve single byte from word

	SHA3 = 0x20,		///< compute SHA3-256 hash

	ADDRESS = 0x30,		///< get address of currently executing account
	BALANCE,			///< get balance of the given account
	ORIGIN,				///< get execution origination address
	CALLER,				///< get caller address
	CALLVALUE,			///< get deposited value by the instruction/transaction responsible for this execution
	CALLDATALOAD,		///< get input data of current environment
	CALLDATASIZE,		///< get size of input data in current environment
	CALLDATACOPY,		///< copy input data in current environment to memory
	CODESIZE,			///< get size of code running in current environment
	CODECOPY,			///< copy code running in current environment to memory
	GASPRICE,			///< get price of gas in current environment
	EXTCODESIZE,		///< get external code size (from another contract)
	EXTCODECOPY,		///< copy external code (from another contract)

	BLOCKHASH = 0x40,	///< get hash of most recent complete block
	COINBASE,			///< get the block's coinbase address
	TIMESTAMP,			///< get the block's timestamp
	NUMBER,				///< get the block's number
	DIFFICULTY,			///< get the block's difficulty
	GASLIMIT,			///< get the block's gas limit

	POP = 0x50,			///< remove item from stack
	MLOAD,				///< load word from memory
	MSTORE,				///< save word to memory
	MSTORE8,			///< save byte to memory
	SLOAD,				///< load word from storage
	SSTORE,				///< save word to storage
	JUMP,				///< alter the program counter
	JUMPI,				///< conditionally alter the program counter
	PC,					///< get the program counter
	MSIZE,				///< get the size of active memory
	GAS,				///< get the amount of available gas
	JUMPDEST,			///< set a potential jump destination

	PUSH1 = 0x60,		///< place 1 byte item on stack
	PUSH2,				///< place 2 byte item on stack
	PUSH3,				///< place 3 byte item on stack
	PUSH4,				///< place 4 byte item on stack
	PUSH5,				///< place 5 byte item on stack
	PUSH6,				///< place 6 byte item on stack
	PUSH7,				///< place 7 byte item on stack
	PUSH8,				///< place 8 byte item on stack
	PUSH9,				///< place 9 byte item on stack
	PUSH10,				///< place 10 byte item on stack
	PUSH11,				///< place 11 byte item on stack
	PUSH12,				///< place 12 byte item on stack
	PUSH13,				///< place 13 byte item on stack
	PUSH14,				///< place 14 byte item on stack
	PUSH15,				///< place 15 byte item on stack
	PUSH16,				///< place 16 byte item on stack
	PUSH17,				///< place 17 byte item on stack
	PUSH18,				///< place 18 byte item on stack
	PUSH19,				///< place 19 byte item on stack
	PUSH20,				///< place 20 byte item on stack
	PUSH21,				///< place 21 byte item on stack
	PUSH22,				///< place 22 byte item on stack
	PUSH23,				///< place 23 byte item on stack
	PUSH24,				///< place 24 byte item on stack
	PUSH25,				///< place 25 byte item on stack
	PUSH26,				///< place 26 byte item on stack
	PUSH27,				///< place 27 byte item on stack
	PUSH28,				///< place 28 byte item on stack
	PUSH29,				///< place 29 byte item on stack
	PUSH30,				///< place 30 byte item on stack
	PUSH31,				///< place 31 byte item on stack
	PUSH32,				///< place 32 byte item on stack

	DUP1 = 0x80,		///< copies the highest item in the stack to the top of the stack
	DUP2,				///< copies the second highest item in the stack to the top of the stack
	DUP3,				///< copies the third highest item in the stack to the top of the stack
	DUP4,				///< copies the 4th highest item in the stack to the top of the stack
	DUP5,				///< copies the 5th highest item in the stack to the top of the stack
	DUP6,				///< copies the 6th highest item in the stack to the top of the stack
	DUP7,				///< copies the 7th highest item in the stack to the top of the stack
	DUP8,				///< copies the 8th highest item in the stack to the top of the stack
	DUP9,				///< copies the 9th highest item in the stack to the top of the stack
	DUP10,				///< copies the 10th highest item in the stack to the top of the stack
	DUP11,				///< copies the 11th highest item in the stack to the top of the stack
	DUP12,				///< copies the 12th highest item in the stack to the top of the stack
	DUP13,				///< copies the 13th highest item in the stack to the top of the stack
	DUP14,				///< copies the 14th highest item in the stack to the top of the stack
	DUP15,				///< copies the 15th highest item in the stack to the top of the stack
	DUP16,				///< copies the 16th highest item in the stack to the top of the stack

	SWAP1 = 0x90,		///< swaps the highest and second highest value on the stack
	SWAP2,				///< swaps the highest and third highest value on the stack
	SWAP3,				///< swaps the highest and 4th highest value on the stack
	SWAP4,				///< swaps the highest and 5th highest value on the stack
	SWAP5,				///< swaps the highest and 6th highest value on the stack
	SWAP6,				///< swaps the highest and 7th highest value on the stack
	SWAP7,				///< swaps the highest and 8th highest value on the stack
	SWAP8,				///< swaps the highest and 9th highest value on the stack
	SWAP9,				///< swaps the highest and 10th highest value on the stack
	SWAP10,				///< swaps the highest and 11th highest value on the stack
	SWAP11,				///< swaps the highest and 12th highest value on the stack
	SWAP12,				///< swaps the highest and 13th highest value on the stack
	SWAP13,				///< swaps the highest and 14th highest value on the stack
	SWAP14,				///< swaps the highest and 15th highest value on the stack
	SWAP15,				///< swaps the highest and 16th highest value on the stack
	SWAP16,				///< swaps the highest and 17th highest value on the stack

	LOG0 = 0xa0,		///< Makes a log entry; no topics.
	LOG1,				///< Makes a log entry; 1 topic.
	LOG2,				///< Makes a log entry; 2 topics.
	LOG3,				///< Makes a log entry; 3 topics.
	LOG4,				///< Makes a log entry; 4 topics.

	CREATE = 0xf0,		///< create a new account with associated code
	CALL,				///< message-call into an account
	CALLCODE,			///< message-call with another account's code only
	RETURN,				///< halt execution returning output data
	SUICIDE = 0xff		///< halt execution and register account for later deletion
};

/// Reads PUSH data from pointed fragment of bytecode and constructs number out of it
/// Reading out of bytecode means reading 0
/// @param _curr is updated and points the last real byte read
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end);

/// Skips PUSH data in pointed fragment of bytecode.
/// @param _curr is updated and points the last real byte skipped
void skipPushData(code_iterator& _curr, code_iterator _end);

#define ANY_PUSH	  PUSH1:  \
	case Instruction::PUSH2:  \
	case Instruction::PUSH3:  \
	case Instruction::PUSH4:  \
	case Instruction::PUSH5:  \
	case Instruction::PUSH6:  \
	case Instruction::PUSH7:  \
	case Instruction::PUSH8:  \
	case Instruction::PUSH9:  \
	case Instruction::PUSH10: \
	case Instruction::PUSH11: \
	case Instruction::PUSH12: \
	case Instruction::PUSH13: \
	case Instruction::PUSH14: \
	case Instruction::PUSH15: \
	case Instruction::PUSH16: \
	case Instruction::PUSH17: \
	case Instruction::PUSH18: \
	case Instruction::PUSH19: \
	case Instruction::PUSH20: \
	case Instruction::PUSH21: \
	case Instruction::PUSH22: \
	case Instruction::PUSH23: \
	case Instruction::PUSH24: \
	case Instruction::PUSH25: \
	case Instruction::PUSH26: \
	case Instruction::PUSH27: \
	case Instruction::PUSH28: \
	case Instruction::PUSH29: \
	case Instruction::PUSH30: \
	case Instruction::PUSH31: \
	case Instruction::PUSH32

#define ANY_DUP		  DUP1:	 \
	case Instruction::DUP2:	 \
	case Instruction::DUP3:	 \
	case Instruction::DUP4:	 \
	case Instruction::DUP5:	 \
	case Instruction::DUP6:	 \
	case Instruction::DUP7:	 \
	case Instruction::DUP8:	 \
	case Instruction::DUP9:	 \
	case Instruction::DUP10: \
	case Instruction::DUP11: \
	case Instruction::DUP12: \
	case Instruction::DUP13: \
	case Instruction::DUP14: \
	case Instruction::DUP15: \
	case Instruction::DUP16

#define ANY_SWAP	  SWAP1:  \
	case Instruction::SWAP2:  \
	case Instruction::SWAP3:  \
	case Instruction::SWAP4:  \
	case Instruction::SWAP5:  \
	case Instruction::SWAP6:  \
	case Instruction::SWAP7:  \
	case Instruction::SWAP8:  \
	case Instruction::SWAP9:  \
	case Instruction::SWAP10: \
	case Instruction::SWAP11: \
	case Instruction::SWAP12: \
	case Instruction::SWAP13: \
	case Instruction::SWAP14: \
	case Instruction::SWAP15: \
	case Instruction::SWAP16

}
}