module Network.Haskoin.Wallet.Model
( -- Database types
  Account(..)
, AccountId
, WalletAddr(..)
, WalletAddrId
, WalletState(..)
, WalletStateId
, WalletCoin(..)
, WalletCoinId
, SpentCoin(..)
, SpentCoinId
, WalletTx(..)
, WalletTxId
, EntityField(..)
, Unique(..)
, migrateWallet

-- JSON conversion
, toJsonAccount
, toJsonAddr
, toJsonCoin
, toJsonTx

) where

import Data.Word (Word32, Word64)
import Data.Time (UTCTime)
import Data.Text (Text)
import Data.String.Conversions (cs)

import Database.Persist (EntityField, Unique)
import Database.Persist.Quasi (lowerCaseSettings)
import Database.Persist.TH
    ( share
    , mkPersist
    , sqlSettings
    , mkMigrate
    , persistFileWith
    )

import Network.Haskoin.Wallet.Types
import Network.Haskoin.Block
import Network.Haskoin.Transaction
import Network.Haskoin.Script
import Network.Haskoin.Crypto
import Network.Haskoin.Node
import Network.Haskoin.Node.HeaderTree

share [ mkPersist sqlSettings
      , mkMigrate "migrateWallet"
      ]
    $(persistFileWith lowerCaseSettings "config/models")

{- JSON Types -}

toJsonAccount :: Maybe Mnemonic -> Account -> JsonAccount
toJsonAccount msM acc = JsonAccount
    { jsonAccountName         = accountName acc
    , jsonAccountType         = accountType acc
    , jsonAccountMnemonic     = fmap cs msM
    , jsonAccountMaster       = accountMaster acc
    , jsonAccountDerivation   = accountDerivation acc
    , jsonAccountKeys         = accountKeys acc
    , jsonAccountGap          = accountGap acc
    , jsonAccountCreated      = accountCreated acc
    }

toJsonAddr :: WalletAddr
           -> Maybe BalanceInfo
           -> JsonAddr
toJsonAddr addr balM = JsonAddr
    { jsonAddrAddress        = walletAddrAddress addr
    , jsonAddrIndex          = walletAddrIndex addr
    , jsonAddrType           = walletAddrType addr
    , jsonAddrLabel          = walletAddrLabel addr
    , jsonAddrRedeem         = walletAddrRedeem addr
    , jsonAddrKey            = walletAddrKey addr
    , jsonAddrCreated        = walletAddrCreated addr
    , jsonAddrBalance        = balM
    }

toJsonTx :: AccountName
         -> Maybe (BlockHash, BlockHeight) -- ^ Current best block
         -> WalletTx
         -> JsonTx
toJsonTx acc bbM tx = JsonTx
    { jsonTxHash            = walletTxHash tx
    , jsonTxNosigHash       = walletTxNosigHash tx
    , jsonTxType            = walletTxType tx
    , jsonTxInValue         = walletTxInValue tx
    , jsonTxOutValue        = walletTxOutValue tx
    , jsonTxValue           = fromIntegral (walletTxInValue tx) -
                              fromIntegral (walletTxOutValue tx)
    , jsonTxInputs          = walletTxInputs tx
    , jsonTxOutputs         = walletTxOutputs tx
    , jsonTxChange          = walletTxChange tx
    , jsonTxTx              = walletTxTx tx
    , jsonTxIsCoinbase      = walletTxIsCoinbase tx
    , jsonTxConfidence      = walletTxConfidence tx
    , jsonTxConfirmedBy     = walletTxConfirmedBy tx
    , jsonTxConfirmedHeight = walletTxConfirmedHeight tx
    , jsonTxConfirmedDate   = walletTxConfirmedDate tx
    , jsonTxCreated         = walletTxCreated tx
    , jsonTxAccount         = acc
    , jsonTxConfirmations   = f =<< walletTxConfirmedHeight tx
    , jsonTxBestBlock       = fst <$> bbM
    , jsonTxBestBlockHeight = snd <$> bbM
    }
  where
    f confirmedHeight = case bbM of
        Just (_, h) -> return $ fromInteger $
            max 0 $ toInteger h - toInteger confirmedHeight + 1
        _ -> Nothing

toJsonCoin :: WalletCoin
           -> Maybe JsonTx   -- ^ Coin’s transaction
           -> Maybe JsonAddr -- ^ Coin’s address
           -> Maybe JsonTx   -- ^ Coin’s spending transaction
           -> JsonCoin
toJsonCoin coin txM addrM spendM = JsonCoin
    { jsonCoinHash       = walletCoinHash coin
    , jsonCoinPos        = walletCoinPos coin
    , jsonCoinValue      = walletCoinValue coin
    , jsonCoinScript     = walletCoinScript coin
    , jsonCoinCreated    = walletCoinCreated coin
    -- Optional tx
    , jsonCoinTx         = txM
    -- Optional address
    , jsonCoinAddress    = addrM
    -- Optional spending tx
    , jsonCoinSpendingTx = spendM
    }