{-# LANGUAGE DeriveGeneric #-}
module Data.Dwarf.CFA where

import           Control.Applicative (Applicative(..))
import           Data.Binary.Get (getWord8, Get)
import           Data.Bits (shiftR, (.&.))
import qualified Data.ByteString as B
import           Data.Dwarf.Reader
import           Data.Dwarf.Utils
import           Data.Int (Int64)
import           Data.Word (Word8, Word16, Word32, Word64)
import           GHC.Generics (Generic)
import           TextShow (TextShow(..))
import           TextShow.Generic (genericShowbPrec)

-- Section 7.22 - Call Frame
data DW_CFA
    = DW_CFA_advance_loc Word8
    | DW_CFA_offset Word8 Word64
    | DW_CFA_restore Word8
    | DW_CFA_nop
    | DW_CFA_set_loc Word64
    | DW_CFA_advance_loc1 Word8
    | DW_CFA_advance_loc2 Word16
    | DW_CFA_advance_loc4 Word32
    | DW_CFA_offset_extended Word64 Word64
    | DW_CFA_restore_extended Word64
    | DW_CFA_undefined Word64
    | DW_CFA_same_value Word64
    | DW_CFA_register Word64 Word64
    | DW_CFA_remember_state
    | DW_CFA_restore_state
    | DW_CFA_def_cfa Word64 Word64
    | DW_CFA_def_cfa_register Word64
    | DW_CFA_def_cfa_offset Word64
    | DW_CFA_def_cfa_expression B.ByteString
    | DW_CFA_expression Word64 B.ByteString
    | DW_CFA_offset_extended_sf Word64 Int64
    | DW_CFA_def_cfa_sf Word64 Int64
    | DW_CFA_def_cfa_offset_sf Int64
    | DW_CFA_val_offset Word64 Word64
    | DW_CFA_val_offset_sf Word64 Int64
    | DW_CFA_val_expression Word64 B.ByteString
    deriving (Eq, Ord, Read, Show, Generic)

instance TextShow DW_CFA where showbPrec = genericShowbPrec

getDW_CFA :: Reader -> Get DW_CFA
getDW_CFA dr = do
    tag <- getWord8
    case tag `shiftR` 6 of
        0x1 -> pure $ DW_CFA_advance_loc $ tag .&. 0x3f
        0x2 -> pure (DW_CFA_offset (tag .&. 0x3f)) <*> getULEB128
        0x3 -> pure $ DW_CFA_restore $ tag .&. 0x3f
        0x0 -> case tag .&. 0x3f of
            0x00 -> pure DW_CFA_nop
            0x01 -> pure DW_CFA_set_loc <*> drGetTargetAddress dr
            0x02 -> pure DW_CFA_advance_loc1 <*> getWord8
            0x03 -> pure DW_CFA_advance_loc2 <*> drGetW16 dr
            0x04 -> pure DW_CFA_advance_loc4 <*> drGetW32 dr
            0x05 -> pure DW_CFA_offset_extended <*> getULEB128 <*> getULEB128
            0x06 -> pure DW_CFA_restore_extended <*> getULEB128
            0x07 -> pure DW_CFA_undefined <*> getULEB128
            0x08 -> pure DW_CFA_same_value <*> getULEB128
            0x09 -> pure DW_CFA_register <*> getULEB128 <*> getULEB128
            0x0a -> pure DW_CFA_remember_state
            0x0b -> pure DW_CFA_restore_state
            0x0c -> pure DW_CFA_def_cfa <*> getULEB128 <*> getULEB128
            0x0d -> pure DW_CFA_def_cfa_register <*> getULEB128
            0x0e -> pure DW_CFA_def_cfa_offset <*> getULEB128
            0x0f -> pure DW_CFA_def_cfa_expression <*> getByteStringLen getULEB128
            0x10 -> pure DW_CFA_expression <*> getULEB128 <*> getByteStringLen getULEB128
            0x11 -> pure DW_CFA_offset_extended_sf <*> getULEB128 <*> getSLEB128
            0x12 -> pure DW_CFA_def_cfa_sf <*> getULEB128 <*> getSLEB128
            0x13 -> pure DW_CFA_def_cfa_offset_sf <*> getSLEB128
            0x14 -> pure DW_CFA_val_offset <*> getULEB128 <*> getULEB128
            0x15 -> pure DW_CFA_val_offset_sf <*> getULEB128 <*> getSLEB128
            0x16 -> pure DW_CFA_val_expression <*> getULEB128 <*> getByteStringLen getULEB128
            _ -> fail $ "Invalid tag: " ++ show tag
        _ -> fail $ "Invalid tag: " ++ show tag