-- | Various utilities for printing file and
-- variable data.
module Data.TI85.IO where

import Prelude hiding (putStrLn)
import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Text (Text, intercalate, pack)
import Data.Text.Encoding (decodeLatin1)
import Data.Text.IO (putStrLn)
import Data.Word
import Numeric (showHex)

import Data.TI85.Encoding
import Data.TI85.File
import Data.TI85.Parsers
import Data.TI85.Var

-- | A backup file contains a variable table that lists
-- all user data, and indexes into the user memory area.
printVariableTable :: Word16 -> VarTable -> IO ()
printVariableTable :: Word16 -> VarTable -> IO ()
printVariableTable Word16
baseAddr VarTable
vars = do
    VarTable -> (VarTableEntry -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ VarTable
vars VarTableEntry -> IO ()
printEntry
  where
    printEntry :: VarTableEntry -> IO ()
printEntry (VarTableEntry Word8
idNum Word16
addr Word8
_ ByteString
name) = do
        let idName :: Text
idName = (VarType -> Text
showType(VarType -> Text) -> (Word8 -> VarType) -> Word8 -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Word8 -> VarType
idToType) Word8
idNum
        let offset :: Word16
offset = Word16
addr Word16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
- Word16
baseAddr 
        Text -> IO ()
putStrLn (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ Text
"Name: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
tiDecode ByteString
name Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
            Text
"Type: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
idName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
            Text
"Addr: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
pack (Word16 -> ShowS
forall a. Integral a => a -> ShowS
showHex Word16
addr String
"") Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (offset " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (String -> Text
pack(String -> Text) -> (Word16 -> String) -> Word16 -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Word16 -> String
forall a. Show a => a -> String
show) Word16
offset Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
")\n"

-- | User the variable table to lookup variable data
-- in user memory and display it.
extractVariableTable :: Word16 -> TIBackupData -> IO ()
extractVariableTable :: Word16 -> TIBackupData -> IO ()
extractVariableTable Word16
baseAddr TIBackupData
tiBackup = do
    let table :: VarTable
table = TIBackupData -> VarTable
varTable TIBackupData
tiBackup
    let userData :: ByteString
userData = TIBackupData -> ByteString
data2 TIBackupData
tiBackup
    let vars :: [Variable]
vars = Word16 -> VarTable -> ByteString -> [Variable]
readUserMem Word16
baseAddr VarTable
table ByteString
userData
    VarTable -> (VarTableEntry -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ VarTable
table ((VarTableEntry -> IO ()) -> IO ())
-> (VarTableEntry -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \entry :: VarTableEntry
entry@(VarTableEntry Word8
idNum Word16
addr Word8
_nameLen ByteString
name) -> do
        let var :: Variable
var = Word16 -> VarTableEntry -> ByteString -> Variable
readVarMem Word16
baseAddr VarTableEntry
entry ByteString
userData
        Text -> IO ()
putStrLn (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ Text
"\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (VarType -> Text
showType (VarType -> Text) -> (Word8 -> VarType) -> Word8 -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> VarType
idToType ) Word8
idNum Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" \"" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
tiDecode ByteString
name Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\""
            Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" at 0x" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Word16 -> Text
hexify Word16
addr Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (0x" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Word16 -> Text
hexify (Word16
addrWord16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
-Word16
baseAddr) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"):\n"
        Variable -> IO ()
printVariable Variable
var
  where
    hexify :: Word16 -> Text
    hexify :: Word16 -> Text
hexify Word16
w = String -> Text
pack (Word16 -> ShowS
forall a. Integral a => a -> ShowS
showHex Word16
w String
"")

-- | Print file metadata.
printFileSummary :: TIFile -> IO ()
printFileSummary :: TIFile -> IO ()
printFileSummary TIFile
tiFile =
    let hdr :: TIHeader
hdr = TIFile -> TIHeader
tiHeader TIFile
tiFile
        check :: Word16
check = TIFile -> Word16
tiChecksum TIFile
tiFile
        sig :: Text
sig = ByteString -> Text
decodeLatin1 (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ TIHeader -> ByteString
hdrSig TIHeader
hdr
        comment :: Text
comment = ByteString -> Text
decodeLatin1 (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ TIHeader -> ByteString
hdrComment TIHeader
hdr
        fileType :: Text
fileType = case TIFile -> TIFileData
tiData TIFile
tiFile of
            BackupData TIBackupData
_ -> Text
"Backup"
            VariableData TIVarData
_ -> Text
"Variable"
    in do
        Text -> IO ()
putStrLn (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$
            Text
"\nTI " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
fileType Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" File <" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
sig Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
">\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
            Text
"\"" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
comment Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\"\n\n"
        case TIFile -> TIFileData
tiData TIFile
tiFile of
            BackupData TIBackupData
backupData -> TIBackupData -> IO ()
printBackupSummary TIBackupData
backupData
            VariableData TIVarData
variableData -> TIVarData -> IO ()
printVariableSummary TIVarData
variableData

-- | Print backup-specific top-level iniformation.
printBackupSummary :: TIBackupData -> IO ()
printBackupSummary :: TIBackupData -> IO ()
printBackupSummary TIBackupData
tiBackup = do
    let backupHdr :: TIBackupHeader
backupHdr = TIBackupData -> TIBackupHeader
backupHeader TIBackupData
tiBackup
    let data2Addr :: Word16
data2Addr = TIBackupHeader -> Word16
hdrData2Addr TIBackupHeader
backupHdr
    Text -> IO ()
putStrLn (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Data Section 1 (" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Word16 -> String
forall a. Show a => a -> String
show (TIBackupData -> Word16
data1Len TIBackupData
tiBackup) String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"):"
    Text -> IO ()
putStrLn (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Data Section 2 (" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Word16 -> String
forall a. Show a => a -> String
show (TIBackupData -> Word16
data2Len TIBackupData
tiBackup) String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"):"
    Text -> IO ()
putStrLn (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Data 2 Address: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Word16 -> ShowS
forall a. Integral a => a -> ShowS
showHex Word16
data2Addr String
"\n"

-- | Print variable-specific metadata.
printVariableSummary :: TIVarData -> IO ()
printVariableSummary :: TIVarData -> IO ()
printVariableSummary (TIVarData [TIVar]
vars) =
    Text -> IO ()
putStrLn (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$
        Text
"Variables:\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
        Text -> [Text] -> Text
intercalate Text
"\n" ((TIVar -> Text) -> [TIVar] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map TIVar -> Text
varSummary [TIVar]
vars)
  where
    varSummary :: TIVar -> Text
    varSummary :: TIVar -> Text
varSummary TIVar
var =
        let varIdStr :: Text
varIdStr = (String -> Text
pack(String -> Text) -> (Word8 -> String) -> Word8 -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Int -> String
forall a. Show a => a -> String
show(Int -> String) -> (Word8 -> Int) -> Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Word8 -> Int
forall a. Enum a => a -> Int
fromEnum) (TIVar -> Word8
varId TIVar
var)
            varIdType :: Text
varIdType = (VarType -> Text
showType(VarType -> Text) -> (Word8 -> VarType) -> Word8 -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Word8 -> VarType
idToType) (TIVar -> Word8
varId TIVar
var)
        in  Text
"\tName: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
tiDecode (TIVar -> ByteString
varName TIVar
var) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
            Text
"\tType: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
varIdType Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
varIdStr Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
")\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
            Text
"\tLength : " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (String -> Text
pack(String -> Text) -> (Word16 -> String) -> Word16 -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Int -> String
forall a. Show a => a -> String
show(Int -> String) -> (Word16 -> Int) -> Word16 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Word16 -> Int
forall a. Enum a => a -> Int
fromEnum) (TIVar -> Word16
varDataLen TIVar
var) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\n"