module Ethereum.Analyzer.EVM.Disasm
  ( EvmBytecode(..)
  , EvmHexString(..)
  , HasEvmBytecode(..)
  , disasm
  ) where

import Protolude hiding (show)

import Blockchain.Data.Code
import Blockchain.ExtWord
import Blockchain.Util
import Blockchain.VM.Code
import Blockchain.VM.Opcodes
import Data.HexString

class HasEvmBytecode a where
  evmBytecodeOf :: a -> EvmBytecode

newtype EvmBytecode = EvmBytecode
  { unEvmBytecode :: ByteString
  } deriving (Show, Eq)

newtype EvmHexString = EvmHexString
  { unEvmHexString :: Text
  } deriving (Show, Eq)

instance HasEvmBytecode EvmBytecode where
  evmBytecodeOf = identity

instance HasEvmBytecode EvmHexString where
  evmBytecodeOf =
    EvmBytecode . toBytes . hexString . encodeUtf8 . unEvmHexString

instance HasEvmBytecode Code where
  evmBytecodeOf (Code bs) = EvmBytecode bs
  evmBytecodeOf _ = EvmBytecode ""

disasm
  :: HasEvmBytecode a
  => a -> [(Word256, Operation)]
disasm a =
  let bs = (unEvmBytecode . evmBytecodeOf) a
      hardlimit = 10000
  in disasmBSAt bs 0 hardlimit

disasmBSAt :: ByteString -> Word256 -> Int -> [(Word256, Operation)]
disasmBSAt "" _ _ = []
disasmBSAt _ _ 0 = []
disasmBSAt bs base limit =
  (base, op) : disasmBSAt (safeDrop next bs) (base + next) (limit - 1)
  where
    (op, next) = getOperationAt' bs 0
