# Bitcoin script consists of opcodes ("operation codes") and raw binary data.
# Opcodes define how the data is added, removed and modified on the stack.
module BTC
  module Opcode
    extend self

    # Returns a human-readable name for a given opcode byte value.
    def name_for_opcode(opcode)
      OPCODE_VALUE_TO_NAME[opcode] || "OP_UNKNOWN"
    end

    # Returns an opcode byte value for a given name ("OP_...").
    def opcode_for_name(name)
      OPCODE_NAME_TO_VALUE[name] || OP_INVALIDOPCODE
    end

    # Returns OP_1NEGATE, OP_0 .. OP_16 for ints from -1 to 16.
    # Returns OP_INVALIDOPCODE for other ints.
    def opcode_for_small_integer(small_int)
      raise ArgumentError, "small_int must not be nil" if !small_int
      return OP_0       if small_int == 0
      return OP_1NEGATE if small_int == -1
      if small_int >= 1 && small_int <= 16
        return OP_1 + (small_int - 1)
      end
      return OP_INVALIDOPCODE
    end

    # Converts opcode OP_<N> or OP_1NEGATE to an integer value.
    # If incorrect opcode is given, nil is returned.
    def small_integer_from_opcode(opcode)
      return 0  if opcode == "".b # OP_O
      return 0  if opcode == OP_0
      return -1 if opcode == OP_1NEGATE
      if opcode >= OP_1 && opcode <= OP_16
        return opcode - (OP_1 - 1)
      end
      return nil
    end
  end
  
  # All opcodes are defined in Opcodes module so they can be included in
  # a different namespace without the baggage of other BTC identifiers.
  module Opcodes

    # 1. Operators pushing data on stack.

    # Push 1 byte 0x00 on the stack
    OP_FALSE = 0x00
    OP_0     = 0x00

    # Any opcode with value < PUSHDATA1 is a length of the string to be pushed on the stack.
    # So opcode 0x01 is followed by 1 byte of data, 0x09 by 9 bytes and so on up to 0x4b (75 bytes)

    # PUSHDATA<N> opcode is followed by N-byte length of the string that follows.
    OP_PUSHDATA1 = 0x4c # followed by a 1-byte length of the string to push (allows pushing 0..255 bytes).
    OP_PUSHDATA2 = 0x4d # followed by a 2-byte length of the string to push (allows pushing 0..65535 bytes).
    OP_PUSHDATA4 = 0x4e # followed by a 4-byte length of the string to push (allows pushing 0..4294967295 bytes).
    OP_1NEGATE   = 0x4f # pushes -1 number on the stack
    OP_RESERVED  = 0x50 # Not assigned. If executed, transaction is invalid.

    # OP_<N> pushes number <N> on the stack
    OP_TRUE = 0x51
    OP_1    = 0x51
    OP_2    = 0x52
    OP_3    = 0x53
    OP_4    = 0x54
    OP_5    = 0x55
    OP_6    = 0x56
    OP_7    = 0x57
    OP_8    = 0x58
    OP_9    = 0x59
    OP_10   = 0x5a
    OP_11   = 0x5b
    OP_12   = 0x5c
    OP_13   = 0x5d
    OP_14   = 0x5e
    OP_15   = 0x5f
    OP_16   = 0x60

    # 2. Control flow operators

    OP_NOP      = 0x61 # Does nothing
    OP_VER      = 0x62 # Not assigned. If executed, transaction is invalid.

    # BitcoinQT executes all operators from OP_IF to OP_ENDIF even inside "non-executed" branch (to keep track of nesting).
    # Since OP_VERIF and OP_VERNOTIF are not assigned, even inside a non-executed branch they will fall in "default:" switch case
    # and cause the script to fail. Some other ops like OP_VER can be present inside non-executed branch because they'll be skipped.
    OP_IF       = 0x63 # If the top stack value is not 0, the statements are executed. The top stack value is removed.
    OP_NOTIF    = 0x64 # If the top stack value is 0, the statements are executed. The top stack value is removed.
    OP_VERIF    = 0x65 # Not assigned. Script is invalid with that opcode (even if inside non-executed branch).
    OP_VERNOTIF = 0x66 # Not assigned. Script is invalid with that opcode (even if inside non-executed branch).
    OP_ELSE     = 0x67 # Executes code if the previous OP_IF or OP_NOTIF was not executed.
    OP_ENDIF    = 0x68 # Finishes if/else block

    OP_VERIFY   = 0x69 # Removes item from the stack if it's not 0x00 or 0x80 (negative zero). Otherwise, marks script as invalid.
    OP_RETURN   = 0x6a # Marks transaction as invalid.

    # Stack ops
    OP_TOALTSTACK   = 0x6b # Moves item from the stack to altstack
    OP_FROMALTSTACK = 0x6c # Moves item from the altstack to stack
    OP_2DROP = 0x6d
    OP_2DUP  = 0x6e
    OP_3DUP  = 0x6f
    OP_2OVER = 0x70
    OP_2ROT  = 0x71
    OP_2SWAP = 0x72
    OP_IFDUP = 0x73
    OP_DEPTH = 0x74
    OP_DROP  = 0x75
    OP_DUP   = 0x76
    OP_NIP   = 0x77
    OP_OVER  = 0x78
    OP_PICK  = 0x79
    OP_ROLL  = 0x7a
    OP_ROT   = 0x7b
    OP_SWAP  = 0x7c
    OP_TUCK  = 0x7d

    # Splice ops
    OP_CAT    = 0x7e # Disabled opcode. If executed, transaction is invalid.
    OP_SUBSTR = 0x7f # Disabled opcode. If executed, transaction is invalid.
    OP_LEFT   = 0x80 # Disabled opcode. If executed, transaction is invalid.
    OP_RIGHT  = 0x81 # Disabled opcode. If executed, transaction is invalid.
    OP_SIZE   = 0x82

    # Bit logic
    OP_INVERT = 0x83 # Disabled opcode. If executed, transaction is invalid.
    OP_AND    = 0x84 # Disabled opcode. If executed, transaction is invalid.
    OP_OR     = 0x85 # Disabled opcode. If executed, transaction is invalid.
    OP_XOR    = 0x86 # Disabled opcode. If executed, transaction is invalid.

    OP_EQUAL = 0x87        # Last two items are removed from the stack and compared. Result (true or false) is pushed to the stack.
    OP_EQUALVERIFY = 0x88  # Same as OP_EQUAL, but removes the result from the stack if it's true or marks script as invalid.

    OP_RESERVED1 = 0x89 # Disabled opcode. If executed, transaction is invalid.
    OP_RESERVED2 = 0x8a # Disabled opcode. If executed, transaction is invalid.

    # Numeric
    OP_1ADD      = 0x8b  # adds 1 to last item, pops it from stack and pushes result.
    OP_1SUB      = 0x8c  # substracts 1 to last item, pops it from stack and pushes result.
    OP_2MUL      = 0x8d  # Disabled opcode. If executed, transaction is invalid.
    OP_2DIV      = 0x8e  # Disabled opcode. If executed, transaction is invalid.
    OP_NEGATE    = 0x8f  # negates the number, pops it from stack and pushes result.
    OP_ABS       = 0x90  # replaces number with its absolute value
    OP_NOT       = 0x91  # replaces number with True if it's zero, False otherwise.
    OP_0NOTEQUAL = 0x92  # replaces number with True if it's not zero, False otherwise.

    OP_ADD    = 0x93  # (x y -- x+y)
    OP_SUB    = 0x94  # (x y -- x-y)
    OP_MUL    = 0x95  # Disabled opcode. If executed, transaction is invalid.
    OP_DIV    = 0x96  # Disabled opcode. If executed, transaction is invalid.
    OP_MOD    = 0x97  # Disabled opcode. If executed, transaction is invalid.
    OP_LSHIFT = 0x98  # Disabled opcode. If executed, transaction is invalid.
    OP_RSHIFT = 0x99  # Disabled opcode. If executed, transaction is invalid.

    OP_BOOLAND            = 0x9a
    OP_BOOLOR             = 0x9b
    OP_NUMEQUAL           = 0x9c
    OP_NUMEQUALVERIFY     = 0x9d
    OP_NUMNOTEQUAL        = 0x9e
    OP_LESSTHAN           = 0x9f
    OP_GREATERTHAN        = 0xa0
    OP_LESSTHANOREQUAL    = 0xa1
    OP_GREATERTHANOREQUAL = 0xa2
    OP_MIN                = 0xa3
    OP_MAX                = 0xa4

    OP_WITHIN = 0xa5

    # Crypto
    OP_RIPEMD160      = 0xa6
    OP_SHA1           = 0xa7
    OP_SHA256         = 0xa8
    OP_HASH160        = 0xa9
    OP_HASH256        = 0xaa
    OP_CODESEPARATOR  = 0xab # This opcode is rarely used because it's useless, but we need to support it anyway.
    OP_CHECKSIG       = 0xac
    OP_CHECKSIGVERIFY = 0xad
    OP_CHECKMULTISIG  = 0xae
    OP_CHECKMULTISIGVERIFY = 0xaf

    # Expansion
    OP_NOP1  = 0xb0
    OP_NOP2  = 0xb1
    OP_CHECKLOCKTIMEVERIFY = OP_NOP2 # See BIP65
    OP_NOP3  = 0xb2
    OP_NOP4  = 0xb3
    OP_NOP5  = 0xb4
    OP_NOP6  = 0xb5
    OP_NOP7  = 0xb6
    OP_NOP8  = 0xb7
    OP_NOP9  = 0xb8
    OP_NOP10 = 0xb9

    OP_INVALIDOPCODE = 0xff

    OPCODE_NAME_TO_VALUE = Hash[*constants.grep(/^OP_/).map{|c| [c.to_s, const_get(c)] }.flatten]
    OPCODE_VALUE_TO_NAME = Hash[*constants.grep(/^OP_/).map{|c| [const_get(c), c.to_s] }.flatten]
  end
  
  # Make all opcodes available as BTC::OP_...
  include Opcodes
  module Opcode
    include Opcodes
  end
end

