{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE LambdaCase #-}

module Zenith.GUI where

import Codec.Picture
import Codec.Picture.Types (pixelFold, promoteImage)
import Codec.QRCode
import Codec.QRCode.JuicyPixels
import Control.Concurrent (threadDelay)
import Control.Exception (throwIO, try)
import Control.Monad (forM_, unless, when)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger
  ( LoggingT
  , NoLoggingT
  , logDebugN
  , runNoLoggingT
  , runStderrLoggingT
  )
import Data.Aeson
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import Data.HexString (toText)
import Data.Maybe (fromJust, fromMaybe, isJust, isNothing)
import Data.Scientific (Scientific, fromFloatDigits)
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TLE
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
import qualified Data.UUID as U
import Database.Esqueleto.Experimental (ConnectionPool, fromSqlKey, val)
import Database.Persist
import Lens.Micro ((&), (+~), (.~), (?~), (^.), set)
import Lens.Micro.TH
import Monomer
import qualified Monomer.Lens as L
import System.Directory (getHomeDirectory)
import System.FilePath ((</>))
import Text.Printf (printf)
import Text.Wrap (FillScope(..), FillStrategy(..), WrapSettings(..), wrapText)
import TextShow hiding (toText)
import ZcashHaskell.Keys
  ( deriveUfvk
  , deriveUivk
  , encodeVK
  , generateWalletSeedPhrase
  )
import ZcashHaskell.Orchard
  ( getSaplingFromUA
  , isValidUnifiedAddress
  , parseAddress
  )
import ZcashHaskell.Transparent
  ( decodeTransparentAddress
  , encodeTransparentReceiver
  )
import ZcashHaskell.Types
  ( BlockResponse(..)
  , Phrase(..)
  , Scope(..)
  , ToBytes(..)
  , UnifiedAddress(..)
  , ValidAddress(..)
  , ValidVk(..)
  , ValidVk(..)
  , ZcashNet(..)
  , ZebraGetBlockChainInfo(..)
  , ZebraGetInfo(..)
  )
import ZcashHaskell.Utils (getBlockTime, makeZebraCall)
import Zenith.Core
import Zenith.DB
import Zenith.GUI.Theme
import Zenith.Scanner (checkIntegrity, processTx, rescanZebra, updateConfs)
import Zenith.Types hiding (ZcashAddress(..))
import Zenith.Utils
  ( createZip321
  , displayAmount
  , getChainTip
  , getZcashPrice
  , isNotEmptyAfterTrim
  , isRecipientValidGUI
  , isValidString
  , isZecAddressValid
  , jsonNumber
  , padWithZero
  , parseZcashPayment
  , showAddress
  , toPhrase
  , validBarValue
  )

data VkTypeDef
  = VkNone
  | VkFull
  | VkIncoming
  deriving (Eq, Show)

data AppEvent
  = AppInit
  | ShowMsg !T.Text
  | ShowError !T.Text
  | ShowModal !T.Text
  | CloseMsg
  | WalletClicked
  | AccountClicked
  | MenuClicked
  | NewClicked
  | NewAddress !(Maybe (Entity ZcashAccount))
  | NewInternalAddress !(Maybe (Entity ZcashAccount))
  | NewAccount !(Maybe (Entity ZcashWallet))
  | NewWallet
  | SetPool !ZcashPool
  | SwitchQr !(Maybe QrCode)
  | SwitchAddr !Int
  | SwitchAcc !Int
  | SwitchWal !Int
  | UpdateBalance !(Integer, Integer, Integer, Integer)
  | CopyAddr !(Maybe (Entity WalletAddress))
  | LoadTxs ![Entity UserTx]
  | LoadAddrs ![Entity WalletAddress]
  | LoadAccs ![Entity ZcashAccount]
  | LoadWallets ![Entity ZcashWallet]
  | CheckChange
  | ConfirmCancel
  | SaveAddress !(Maybe (Entity ZcashAccount))
  | SaveAccount !(Maybe (Entity ZcashWallet))
  | SaveWallet
  | CloseSeed
  | CloseTxId
  | ShowSeed
  | CopySeed !T.Text
  | CopyTx !T.Text
  | CloseTx
  | ShowTx !Int
  | TickUp
  | SyncVal !Float
  | SendTx
  | ShowSend
  | CancelSend
  | CheckRecipient !T.Text
  | CheckAmount !Float
  | ShowTxId !T.Text
  | LoadAbList ![Entity AddressBook]
  | ShowAdrBook
  | CloseAdrBook
  | NewAdrBkEntry
  | CloseNewAdrBook
  | NotImplemented
  | CloseMsgAB
  | CheckValidAddress !T.Text
  | CheckValidDescrip !T.Text
  | SaveNewABEntry
  | UpdateABEntry !T.Text !T.Text
  | CloseUpdABEntry
  | ShowMessage !T.Text
  | ShowABAddress !T.Text !T.Text
  | CloseShowABAddress
  | CopyABAdress !T.Text
  | DeleteABEntry !T.Text
  | UpdateABDescrip !T.Text !T.Text
  | ResetRecipientValid
  | ShowShield
  | CloseShield
  | ShowDeShield
  | CloseDeShield
  | SendDeShield
  | SendShield
  | StartSync
  | TreeSync
  | ShowFIATBalance
  | DisplayFIATBalance !Double !Double
  | CloseFIATBalance
  | ViewingKeysClicked
  | PrepareViewingKey !VkTypeDef !(Maybe (Entity ZcashAccount))
  | ShowViewingKey !VkTypeDef !T.Text
  | CopyViewingKey !T.Text !T.Text
  | CloseShowVK
  | DisplayPaymentURIForm !T.Text
  | ClosePaymentURIForm
  | PrepareURIString
  | CloseShowURIOverlay
  | ShowURIOverlay !(Maybe URIQrCode) !T.Text
  | QRImageLoaded
  | CopyURIString !T.Text
  | DisplayPayUsingURI
  | ClosePayUsingURI
  | ProcIfValidURI
  | PreparePaymentURIForm
  | SetTimerCount
  | ShowNViewingKey
  | CloseNVkShow
  | SaveViewKey
  | ReportVKeySaved
  | ShowNSeedPhrase
  | CloseNSpShow
  | SaveSeedPhrase
  | ReportSeedSaved
  deriving (Eq, Show)

data AppModel = AppModel
  { _configuration :: !Config
  , _network :: !ZcashNet
  , _wallets :: ![Entity ZcashWallet]
  , _selWallet :: !Int
  , _accounts :: ![Entity ZcashAccount]
  , _selAcc :: !Int
  , _addresses :: ![Entity WalletAddress]
  , _selAddr :: !Int
  , _transactions :: ![Entity UserTx]
  , _setTx :: !Int
  , _msg :: !(Maybe T.Text)
  , _zebraOn :: !Bool
  , _balance :: !Integer
  , _unconfBalance :: !(Maybe Integer)
  , _selPool :: !ZcashPool
  , _qrCodeWidget :: !(Maybe QrCode)
  , _accPopup :: !Bool
  , _walPopup :: !Bool
  , _menuPopup :: !Bool
  , _newPopup :: !Bool
  , _mainInput :: !T.Text
  , _confirmTitle :: !(Maybe T.Text)
  , _confirmAccept :: !T.Text
  , _confirmCancel :: !T.Text
  , _confirmEvent :: !AppEvent
  , _inError :: !Bool
  , _showSeed :: !Bool
  , _modalMsg :: !(Maybe T.Text)
  , _showTx :: !(Maybe Int)
  , _timer :: !Int
  , _barValue :: !Float
  , _openSend :: !Bool
  , _sendRecipient :: !T.Text
  , _sendAmount :: !Float
  , _sendMemo :: !T.Text
  , _recipientValid :: !Bool
  , _amountValid :: !Bool
  , _showId :: !(Maybe T.Text)
  , _home :: !FilePath
  , _showAdrBook :: !Bool
  , _newAdrBkEntry :: !Bool
  , _abdescrip :: !T.Text
  , _abaddress :: !T.Text
  , _abAddressValid :: !Bool
  , _abDescripValid :: !Bool
  , _abaddressList :: ![Entity AddressBook]
  , _msgAB :: !(Maybe T.Text)
  , _showABAddress :: !Bool
  , _updateABAddress :: !Bool
  , _privacyChoice :: !PrivacyPolicy
  , _shieldZec :: !Bool
  , _deShieldZec :: !Bool
  , _tBalance :: !Integer
  , _tBalanceValid :: !Bool
  , _sBalance :: !Integer
  , _sBalanceValid :: !Bool
  , _displayFIATBalance :: !Bool
  , _zPrice :: !Double
  , _aBal :: !Double
  , _viewingKeyPopup :: !Bool
  , _viewingKeyDisplay :: !Bool
  , _vkTypeName :: !T.Text
  , _vkData :: !T.Text
  , _paymentURIDisplay :: !Bool
  , _showURIDisplay :: !Bool
  , _usepmtURIOverlay :: !Bool
  , _uriString :: !T.Text
  , _uriAddr :: !T.Text
  , _uriQRImage :: !(Maybe URIQrCode)
  , _uriQRInProgress :: !Bool
  , _nVkName :: !T.Text
  , _nVkString :: !T.Text
  , _nVkBirthday :: !Int
  , _nVkShow :: !Bool
  , _nSpName :: !T.Text
  , _nSpString :: !T.Text
  , _nSpBirthday :: !Int
  , _nSpShow :: !Bool
  } deriving (Eq, Show)

makeLenses ''AppModel

remixArrowRightWideLine :: T.Text
remixArrowRightWideLine = toGlyph 0xF496

remixHourglassFill :: T.Text
remixHourglassFill = toGlyph 0xF338

remixIcon :: T.Text -> WidgetNode s e
remixIcon i = label i `styleBasic` [textFont "Remix", textMiddle]

getURIQRWidth :: Maybe URIQrCode -> Int
getURIQRWidth qr =
  case qr of
    Nothing -> 0
    Just qr -> round (uriWidth qr)

getURIQRHeight :: Maybe URIQrCode -> Int
getURIQRHeight qr =
  case qr of
    Nothing -> 0
    Just qr -> round (uriHeight qr)

buildUI ::
     WidgetEnv AppModel AppEvent -> AppModel -> WidgetNode AppModel AppEvent
buildUI wenv model = widgetTree
  where
    btnColor = rgbHex "#ff5722" --rgbHex "#1818B2"
    btnHiLite = rgbHex "#207DE8"
    currentWallet =
      if null (model ^. wallets)
        then Nothing
        else Just ((model ^. wallets) !! (model ^. selWallet))
    currentAccount =
      if null (model ^. accounts)
        then Nothing
        else Just ((model ^. accounts) !! (model ^. selAcc))
    currentAddress =
      if null (model ^. addresses)
        then Nothing
        else Just ((model ^. addresses) !! (model ^. selAddr))
    widgetTree =
      zstack
        [ mainWindow
        , confirmOverlay `nodeVisible` isJust (model ^. confirmTitle)
        , seedOverlay `nodeVisible` model ^. showSeed
        , txOverlay `nodeVisible` isJust (model ^. showTx)
        , sendTxOverlay `nodeVisible` model ^. openSend
        , txIdOverlay `nodeVisible` isJust (model ^. showId)
        , adrbookOverlay `nodeVisible` model ^. showAdrBook
        , newAdrBkOverlay `nodeVisible` model ^. newAdrBkEntry
        , showABAddressOverlay (model ^. abdescrip) (model ^. abaddress) `nodeVisible`
          model ^.
          showABAddress
        , updateABAddressOverlay (model ^. abdescrip) (model ^. abaddress) `nodeVisible`
          model ^.
          updateABAddress
        , shieldOverlay `nodeVisible` model ^. shieldZec
        , deShieldOverlay `nodeVisible` model ^. deShieldZec
        , dfBalOverlay `nodeVisible` model ^. displayFIATBalance
        , showVKOverlay `nodeVisible` model ^. viewingKeyDisplay
        , nViewingkeyOverlay `nodeVisible` model ^. nVkShow
        , nSeedPhraseOverlay `nodeVisible` model ^. nSpShow
        , paymentURIOverlay `nodeVisible` model ^. paymentURIDisplay
        , showURIInProgress `nodeVisible` model ^. uriQRInProgress
        , showURIOverlay `nodeVisible` model ^. showURIDisplay
        , pmtUsingURIOverlay `nodeVisible` model ^. usepmtURIOverlay
        , msgAdrBookOverlay `nodeVisible` isJust (model ^. msgAB)
        , msgOverlay `nodeVisible` isJust (model ^. msg)
        , modalOverlay `nodeVisible` isJust (model ^. modalMsg)
        ]
    mainWindow =
      vstack
        [ windowHeader
        , spacer
        , balanceBox `nodeVisible`
          ((zcashAccountType . entityVal <$> currentAccount) /=
           Just IncomingViewKey)
        , filler
        , mainPane
        , filler
        , windowFooter
        ]
    windowHeader =
      hstack
        [ vstack
            [ box_
                [onClick MenuClicked, alignMiddle]
                (remixIcon remixMenuFill `styleBasic`
                 [textSize 16, textColor white]) `styleBasic`
              [cursorHand, height 25, padding 3] `styleHover`
              [bgColor btnHiLite]
            , popup menuPopup menuBox
            ]
        , vstack
            [ box_ [onClick WalletClicked, alignMiddle] walletButton `styleBasic`
              [cursorHand, height 25, padding 3] `styleHover`
              [bgColor btnHiLite]
            , popup walPopup walListPopup
            ]
        , vstack
            [ box_ [onClick AccountClicked, alignMiddle] accountButton `styleBasic`
              [cursorHand, height 25, padding 3] `styleHover`
              [bgColor btnHiLite]
            , popup accPopup accListPopup
            ]
        , filler
        , label "Read-Only" `styleBasic` [textColor white] `nodeVisible`
          ((zcashAccountType . entityVal <$> currentAccount) /= Just Local)
        , remixIcon remixErrorWarningFill `styleBasic` [textColor white] `nodeVisible`
          (model ^. network == TestNet)
        , label "Testnet" `styleBasic` [textColor white] `nodeVisible`
          (model ^. network == TestNet)
        ] `styleBasic`
      [bgColor btnColor]
    menuBox =
      box_
        [alignMiddle]
        (vstack
           [ box_
               [alignLeft]
               (vstack
                  [ box_
                      [alignLeft, onClick NewClicked]
                      (hstack
                         [ label "New"
                         , filler
                         , widgetIf (not $ model ^. newPopup) $
                           remixIcon remixMenuUnfoldFill
                         , widgetIf (model ^. newPopup) $
                           remixIcon remixMenuFoldFill
                         ])
                  , widgetIf (model ^. newPopup) $ animSlideIn newBox
                  ]) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_ [alignLeft, onClick ShowSeed] (label "Backup Wallet") `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_ [alignLeft, onClick ShowAdrBook] (label "Address Book") `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_ [alignLeft, onClick ShowShield] (label "Shield ZEC") `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_ [alignLeft, onClick ShowDeShield] (label "De-Shield ZEC") `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_
               [alignLeft]
               (vstack
                  [ box_
                      [alignLeft, onClick ViewingKeysClicked]
                      (hstack
                         [ label "Viewing Keys"
                         , filler
                         , widgetIf (not $ model ^. viewingKeyPopup) $
                           remixIcon remixMenuUnfoldFill
                         , widgetIf (model ^. viewingKeyPopup) $
                           remixIcon remixMenuFoldFill
                         ])
                  , widgetIf (model ^. viewingKeyPopup) $
                    animSlideIn viewingKeysBox
                  ]) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_
               [alignLeft, onClick ShowFIATBalance]
               (label
                  ("Balance in " <>
                   T.toUpper (c_currencyCode (model ^. configuration)))) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_
               [alignLeft, onClick PreparePaymentURIForm]
               (label "Create URI") `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_
               [alignLeft, onClick DisplayPayUsingURI]
               (label "Pay using URI") `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           ]) `styleBasic`
      [bgColor btnColor, padding 3]
    newBox =
      box_
        [alignMiddle]
        (vstack
           [ box_
               [alignLeft, onClick $ NewAddress currentAccount]
               (hstack [label "Address", filler]) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_
               [alignLeft, onClick $ NewAccount currentWallet]
               (hstack [label "Account", filler]) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_
               [alignLeft, onClick NewWallet]
               (hstack [label "Wallet", filler]) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_
               [alignLeft, onClick ShowNViewingKey]
               (hstack [label "Import Viewing Key", filler]) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_
               [alignLeft, onClick ShowNSeedPhrase]
               (hstack [label "Import Seed Phrase", filler]) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           ])
    viewingKeysBox =
      box_
        [alignMiddle]
        (vstack
           [ box_
               [alignLeft, onClick (PrepareViewingKey VkFull currentAccount)]
               (hstack [label "Full VK", filler]) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           , box_
               [ alignLeft
               , onClick (PrepareViewingKey VkIncoming currentAccount)
               ]
               (hstack [label "Incoming VK", filler]) `styleBasic`
             [bgColor white, borderB 1 gray, padding 3]
           ])
    walletButton =
      hstack
        [ label "Wallet: " `styleBasic` [textFont "Bold", textColor white]
        , label (maybe "None" (zcashWalletName . entityVal) currentWallet) `styleBasic`
          [textFont "Regular", textColor white]
        , remixIcon remixArrowRightWideLine `styleBasic` [textColor white]
        ]
    walListPopup =
      box_ [alignMiddle] dispWalList `styleBasic` [bgColor btnColor, padding 3]
    dispWalList = vstack (zipWith walRow [0 ..] (model ^. wallets))
    walRow :: Int -> Entity ZcashWallet -> WidgetNode AppModel AppEvent
    walRow idx wal =
      box_
        [onClick $ SwitchWal idx, alignCenter]
        (label (zcashWalletName (entityVal wal))) `styleBasic`
      [ padding 1
      , borderB 1 gray
      , bgColor white
      , width 80
      , styleIf (model ^. selWallet == idx) (borderL 2 btnHiLite)
      , styleIf (model ^. selWallet == idx) (borderR 2 btnHiLite)
      ]
    accountButton =
      hstack
        [ label "Account: " `styleBasic` [textFont "Bold", textColor white]
        , label (maybe "None" (zcashAccountName . entityVal) currentAccount) `styleBasic`
          [textFont "Regular", textColor white]
        , remixIcon remixArrowRightWideLine `styleBasic` [textColor white]
        ]
    accListPopup =
      box_ [alignMiddle] dispAccList `styleBasic` [bgColor btnColor, padding 3]
    dispAccList = vstack (zipWith accRow [0 ..] (model ^. accounts))
    accRow :: Int -> Entity ZcashAccount -> WidgetNode AppModel AppEvent
    accRow idx wAcc =
      box_
        [onClick $ SwitchAcc idx, alignLeft]
        (label (zcashAccountName (entityVal wAcc))) `styleBasic`
      [ padding 1
      , borderB 1 gray
      , bgColor white
      , width 80
      , styleIf (model ^. selAcc == idx) (borderL 2 btnHiLite)
      , styleIf (model ^. selAcc == idx) (borderR 2 btnHiLite)
      ]
    mainPane =
      box_ [alignMiddle] $
      hstack
        [ addressBox
        , vstack
            [ mainButton "Send" ShowSend `nodeEnabled`
              ((zcashAccountType . entityVal <$> currentAccount) == Just Local) `styleBasic`
              [textFont "Bold"]
            , txBox `nodeVisible` not (null $ model ^. transactions)
            ]
        ]
    balanceBox =
      hstack
        [ filler
        , boxShadow $
          box_
            [alignMiddle]
            (vstack
               [ hstack
                   [ filler
                   , animFadeIn
                       (label
                          (displayAmount (model ^. network) $ model ^. balance) `styleBasic`
                        [textSize 20])
                   , filler
                   ]
               , hstack
                   [ filler
                   , remixIcon remixHourglassFill `styleBasic` [textSize 8]
                   , label
                       (maybe "0" (displayAmount (model ^. network)) $
                        model ^. unconfBalance) `styleBasic`
                     [textSize 8]
                   , filler
                   ] `nodeVisible`
                 isJust (model ^. unconfBalance)
               ]) `styleBasic`
          [bgColor white, radius 5, border 1 btnColor]
        , filler
        ]
    addressBox =
      vstack
        [ boxShadow $
          box_
            [alignMiddle]
            (vstack
               [ label "Addresses" `styleBasic`
                 [textFont "Bold", textColor white, bgColor btnColor]
               , vscroll (vstack (zipWith addrRow [0 ..] (model ^. addresses))) `nodeKey`
                 "addrScroll"
               ]) `styleBasic`
          [padding 3, radius 2, bgColor white]
        , addrQRCode
        ]
    addrQRCode :: WidgetNode AppModel AppEvent
    addrQRCode =
      box_
        [alignMiddle]
        (hstack
           [ filler
           , boxShadow $
             hstack
               [ vstack
                   [ tooltip "Unified" $
                     box_
                       [onClick (SetPool OrchardPool)]
                       (remixIcon remixShieldCheckFill `styleBasic`
                        [ textSize 14
                        , padding 4
                        , styleIf
                            (model ^. selPool == OrchardPool)
                            (bgColor btnColor)
                        , styleIf
                            (model ^. selPool == OrchardPool)
                            (textColor white)
                        ])
                   , filler
                   , tooltip "Legacy Shielded" $
                     box_
                       [onClick (SetPool SaplingPool)]
                       (remixIcon remixShieldLine `styleBasic`
                        [ textSize 14
                        , padding 4
                        , styleIf
                            (model ^. selPool == SaplingPool)
                            (bgColor btnColor)
                        , styleIf
                            (model ^. selPool == SaplingPool)
                            (textColor white)
                        ])
                   , filler
                   , tooltip "Transparent" $
                     box_
                       [onClick (SetPool TransparentPool)]
                       (remixIcon remixEyeLine `styleBasic`
                        [ textSize 14
                        , padding 4
                        , styleIf
                            (model ^. selPool == TransparentPool)
                            (bgColor btnColor)
                        , styleIf
                            (model ^. selPool == TransparentPool)
                            (textColor white)
                        ])
                   ] `styleBasic`
                 [bgColor white]
               , vstack
                   [ filler
                   , tooltip "Copy" $
                     box_
                       [onClick $ CopyAddr currentAddress]
                       (hstack
                          [ label
                              (case model ^. selPool of
                                 OrchardPool -> "Unified"
                                 SaplingPool -> "Legacy Shielded"
                                 TransparentPool -> "Transparent"
                                 SproutPool -> "Unknown") `styleBasic`
                            [textColor white]
                          , remixIcon remixFileCopyFill `styleBasic`
                            [textSize 14, padding 4, textColor white]
                          ]) `styleBasic`
                     [cursorHand]
                   , box_
                       [alignMiddle]
                       (case model ^. qrCodeWidget of
                          Just qr ->
                            imageMem_
                              (qrCodeName qr)
                              (qrCodeBytes qr)
                              (Size
                                 (fromIntegral $ qrCodeHeight qr)
                                 (fromIntegral $ qrCodeWidth qr))
                              [fitWidth]
                          Nothing ->
                            image_
                              (T.pack $
                               (model ^. home) </>
                               "Zenith/assets/1F928_color.png")
                              [fitEither]) `styleBasic`
                     [bgColor white, height 100, width 100]
                   , filler
                   ] `styleBasic`
                 [bgColor btnColor, border 2 btnColor]
               ] `styleBasic`
             [radius 3, border 1 btnColor]
           , filler
           ])
    addrRow :: Int -> Entity WalletAddress -> WidgetNode AppModel AppEvent
    addrRow idx wAddr =
      box_
        [onClick $ SwitchAddr idx, alignLeft]
        (label
           (walletAddressName (entityVal wAddr) <>
            ": " <> showAddress (walletAddressUAddress $ entityVal wAddr))) `styleBasic`
      [ padding 1
      , borderB 1 gray
      , styleIf (model ^. selAddr == idx) (borderL 2 btnHiLite)
      , styleIf (model ^. selAddr == idx) (borderR 2 btnHiLite)
      ]
    txBox =
      boxShadow $
      box_
        [alignMiddle]
        (vstack
           [ label "Transactions" `styleBasic`
             [textFont "Bold", bgColor btnColor, textColor white]
           , vscroll (vstack (zipWith txRow [0 ..] (model ^. transactions))) `nodeKey`
             "txScroll"
           ]) `styleBasic`
      [radius 2, padding 3, bgColor white]
    txRow :: Int -> Entity UserTx -> WidgetNode AppModel AppEvent
    txRow idx tx =
      box_
        [onClick $ ShowTx idx]
        (hstack
           [ label
               (T.pack $
                show
                  (posixSecondsToUTCTime
                     (fromIntegral (userTxTime $ entityVal tx))))
           , filler
           , widgetIf
               (T.length (userTxMemo $ entityVal tx) > 1)
               (remixIcon remixDiscussFill)
           , if 0 >= userTxAmount (entityVal tx)
               then remixIcon remixArrowRightUpFill `styleBasic` [textColor red]
               else remixIcon remixArrowRightDownFill `styleBasic`
                    [textColor green]
           , label $
             displayAmount (model ^. network) $
             fromIntegral $ userTxAmount (entityVal tx)
           ]) `styleBasic`
      [padding 2, borderB 1 gray]
    windowFooter =
      hstack
        [ label
            ("Last block sync: " <>
             maybe "N/A" (showt . zcashWalletLastSync . entityVal) currentWallet) `styleBasic`
          [padding 3, textSize 8]
        , spacer
        , label (showt $ model ^. timer) `styleBasic` [padding 3, textSize 8]
        , filler
        , image_
            (T.pack $ (model ^. home) </> "Zenith/assets/1F993.png")
            [fitHeight] `styleBasic`
          [height 24, width 24] `nodeVisible`
          (model ^. zebraOn)
        , label
            ("Connected on " <>
             c_zebraHost (model ^. configuration) <>
             ":" <> showt (c_zebraPort $ model ^. configuration)) `styleBasic`
          [padding 3, textSize 8] `nodeVisible`
          (model ^. zebraOn)
        , label "Disconnected" `styleBasic` [padding 3, textSize 8] `nodeVisible`
          not (model ^. zebraOn)
        ]
    msgOverlay =
      alert CloseMsg $
      hstack
        [ filler
        , remixIcon remixErrorWarningFill `styleBasic`
          [textSize 32, textColor btnColor] `nodeVisible`
          (model ^. inError)
        , spacer
        , label $ fromMaybe "" (model ^. msg)
        , filler
        ]
    confirmOverlay =
      confirm_
        (model ^. confirmEvent)
        ConfirmCancel
        [ titleCaption $ fromMaybe "" $ model ^. confirmTitle
        , acceptCaption $ model ^. confirmAccept
        , cancelCaption $ model ^. confirmCancel
        ]
        (hstack [label "Name:", filler, textField_ mainInput [maxLength 25]])
    sendTxOverlay =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ box_
                          [alignMiddle]
                          (label "Send Zcash" `styleBasic`
                           [textFont "Bold", textSize 12])
                      , separatorLine `styleBasic` [fgColor btnColor]
                      , spacer
                      , hstack
                          [ label "Privacy Level:" `styleBasic`
                            [width 70, textFont "Bold"]
                          , spacer
                          , label "Full " `styleBasic` [width 40]
                          , radio Full privacyChoice
                          , spacer
                          , label "Medium " `styleBasic` [width 40]
                          , radio Medium privacyChoice
                          ]
                      , hstack
                          [ label "               " `styleBasic`
                            [width 70, textFont "Bold"]
                          , spacer
                          , label "Low " `styleBasic` [width 40]
                          , radio Low privacyChoice
                          , spacer
                          , label "None " `styleBasic` [width 40]
                          , radio None privacyChoice
                          ]
                      , spacer
                      , hstack
                          [ label "To:" `styleBasic` [width 50, textFont "Bold"]
                          , spacer
                          , textField_ sendRecipient [onChange CheckRecipient] `styleBasic`
                            [ width 150
                            , styleIf
                                (not $ model ^. recipientValid)
                                (textColor red)
                            ]
                          ]
                      , hstack
                          [ label "Amount:" `styleBasic`
                            [width 50, textFont "Bold"]
                          , spacer
                          , numericField_
                              sendAmount
                              [ decimals 8
                              , minValue 0.0
                              , maxValue
                                  (fromIntegral (model ^. balance) / 100000000.0)
                              , validInput amountValid
                              , onChange CheckAmount
                              ] `styleBasic`
                            [ width 150
                            , styleIf
                                (not $ model ^. amountValid)
                                (textColor red)
                            ]
                          ]
                      , hstack
                          [ label "Memo:" `styleBasic`
                            [width 50, textFont "Bold"]
                          , spacer
                          , textArea sendMemo `styleBasic`
                            [width 150, height 40]
                          ]
                      , spacer
                        -- Radio button group for privacy level
                      , box_
                          [alignMiddle]
                          (hstack
                             [ spacer
                             , button "Cancel" CancelSend
                             , spacer
                             , mainButton "Send" SendTx `nodeEnabled`
                               (model ^. amountValid && model ^. recipientValid)
                             , spacer
                             ])
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]
    seedOverlay =
      alert CloseSeed $
      vstack
        [ box_
            []
            (label "Seed Phrase" `styleBasic`
             [textFont "Bold", textSize 12, textColor white]) `styleBasic`
          [bgColor btnColor, radius 2, padding 3]
        , spacer
        , textAreaV_
            (maybe
               "None"
               (E.decodeUtf8Lenient .
                getBytes . getPhrase . zcashWalletSeedPhrase . entityVal)
               currentWallet)
            (const CloseSeed)
            [readOnly, maxLines 2] `styleBasic`
          [textSize 8]
        , spacer
        , hstack
            [ filler
            , box_
                [ onClick $
                  CopySeed $
                  maybe
                    "None"
                    (E.decodeUtf8Lenient .
                     getBytes . getPhrase . zcashWalletSeedPhrase . entityVal)
                    currentWallet
                ]
                (hstack
                   [ label "Copy" `styleBasic` [textColor white]
                   , remixIcon remixFileCopyLine `styleBasic` [textColor white]
                   ]) `styleBasic`
              [cursorHand, bgColor btnColor, radius 2, padding 3]
            , filler
            ]
        ]
    modalOverlay =
      box
        (label (fromMaybe "?" $ model ^. modalMsg) `styleBasic`
         [textSize 12, textFont "Bold"]) `styleBasic`
      [bgColor (white & L.a .~ 0.7)]
    txOverlay =
      case model ^. showTx of
        Nothing -> alert CloseTx $ label "N/A"
        Just i ->
          alert CloseTx $
          vstack
            [ box_
                [alignLeft]
                (hstack
                   [ label "Date  " `styleBasic` [width 60, textFont "Bold"]
                   , separatorLine `styleBasic` [fgColor btnColor]
                   , spacer
                   , label
                       (T.pack $
                        show $
                        posixSecondsToUTCTime $
                        fromIntegral $
                        userTxTime $ entityVal $ (model ^. transactions) !! i)
                   ]) `styleBasic`
              [padding 2, bgColor white, width 280, borderB 1 gray]
            , box_
                [alignLeft]
                (hstack
                   [ label "Tx ID " `styleBasic` [width 60, textFont "Bold"]
                   , separatorLine `styleBasic` [fgColor btnColor]
                   , spacer
                   , label_
                       (txtWrap $
                        toText $
                        getHex $
                        userTxHex $ entityVal $ (model ^. transactions) !! i)
                       [multiline]
                   , spacer
                   , box_
                       [ onClick $
                         CopyTx $
                         toText $
                         getHex $
                         userTxHex $ entityVal $ (model ^. transactions) !! i
                       ]
                       (remixIcon remixFileCopyFill `styleBasic`
                        [textColor white]) `styleBasic`
                     [cursorHand, bgColor btnColor, radius 2, padding 2]
                   ]) `styleBasic`
              [padding 2, bgColor white, width 280, borderB 1 gray]
            , box_
                [alignLeft]
                (hstack
                   [ label "Amount" `styleBasic` [width 60, textFont "Bold"]
                   , separatorLine `styleBasic` [fgColor btnColor]
                   , spacer
                   , label $
                     displayAmount (model ^. network) $
                     fromIntegral $
                     userTxAmount $ entityVal $ (model ^. transactions) !! i
                   ]) `styleBasic`
              [padding 2, bgColor white, width 280, borderB 1 gray]
            , box_
                [alignLeft]
                (hstack
                   [ label "Memo  " `styleBasic` [width 60, textFont "Bold"]
                   , separatorLine `styleBasic` [fgColor btnColor]
                   , spacer
                   , label_
                       (txtWrap $
                        userTxMemo $ entityVal $ (model ^. transactions) !! i)
                       [multiline]
                   ]) `styleBasic`
              [padding 2, bgColor white, width 280, borderB 1 gray]
            ]
    txIdOverlay =
      case model ^. showId of
        Nothing -> alert CloseTxId $ label "N/A"
        Just t ->
          alert CloseTxId $
          box_
            [alignLeft]
            (vstack
               [ box_ [alignMiddle] $
                 label "Transaction Sent!" `styleBasic` [textFont "Bold"]
               , spacer
               , hstack
                   [ label "Tx ID " `styleBasic` [width 60, textFont "Bold"]
                   , separatorLine `styleBasic` [fgColor btnColor]
                   , spacer
                   , label_ (txtWrap t) [multiline]
                   , spacer
                   , box_
                       [onClick $ CopyTx t]
                       (remixIcon remixFileCopyFill `styleBasic`
                        [textColor white]) `styleBasic`
                     [cursorHand, bgColor btnColor, radius 2, padding 2]
                   ]
               ]) `styleBasic`
          [padding 2, bgColor white, width 280, borderB 1 gray, borderT 1 gray]
    -- | 
    -- | Address Book overlays
    -- |
    adrbookOverlay =
      alert CloseAdrBook $
      vstack
        [ box_
            []
            (label "Address Book" `styleBasic`
             [textFont "Bold", textSize 12, textColor white]) `styleBasic`
          [bgColor btnColor, radius 2, padding 3]
        , boxShadow $
          box_
            [alignMiddle]
            (vstack
               [ vscroll
                   (vstack (zipWith abookRow [0 ..] (model ^. abaddressList))) `nodeKey`
                 "txScroll"
               ]) `styleBasic`
          [radius 2, padding 3, bgColor white]
        , spacer
        , hstack [button "New" NewAdrBkEntry]
        ]
    abookRow :: Int -> Entity AddressBook -> WidgetNode AppModel AppEvent
    abookRow idx ab =
      box_
        [ onClick $
          ShowABAddress
            (addressBookAbdescrip $ entityVal ab)
            (addressBookAbaddress $ entityVal ab)
        , alignLeft
        ]
        (hstack
           [ label (T.pack $ padWithZero 3 $ show (fromSqlKey (entityKey ab))) `styleBasic`
             [textFont "Bold"]
           , spacer
           , label (addressBookAbdescrip $ entityVal ab)
           ]) `styleBasic`
      [padding 2, borderB 1 gray]
    newAdrBkOverlay =
      alert CloseNewAdrBook $
      vstack
        [ box_
            []
            (label "New Address Book Entry" `styleBasic`
             [textFont "Bold", textSize 10, textColor white]) `styleBasic`
          [bgColor btnColor, radius 2, padding 3]
        , spacer
        , hstack
            [ label "Description: " `styleBasic` [width 80]
            , spacer
            , textField_ abdescrip [onChange CheckValidDescrip] `styleBasic`
              [ width 320
              , styleIf (not $ model ^. abDescripValid) (textColor red)
              ]
            ]
        , spacer
        , hstack
            [ label "Address:" `styleBasic` [width 50]
            , spacer
            , textField_ abaddress [onChange CheckValidAddress] `styleBasic`
              [ width 350
              , styleIf (not $ model ^. abAddressValid) (textColor red)
              ]
            ]
        , spacer
        , hstack
            [ button "Save" SaveNewABEntry `nodeEnabled`
              ((model ^. abAddressValid) && (model ^. abDescripValid))
            , spacer
            , button "Cancel" CloseNewAdrBook `nodeEnabled` True
            ]
        ]
    updateABAddressOverlay abd aba =
      alert CloseUpdABEntry $
      vstack
        [ box_
            []
            (label "Edit Address Description" `styleBasic`
             [textFont "Bold", textSize 10, textColor white]) `styleBasic`
          [bgColor btnColor, radius 2, padding 3]
        , spacer
        , hstack
            [ label "Description:" `styleBasic` [width 80]
            , spacer
            , textField_ abdescrip [onChange CheckValidDescrip] `styleBasic`
              [ width 320
              , styleIf (not $ model ^. abDescripValid) (textColor red)
              ]
            ]
        , spacer
        , hstack
            [ filler
            , button "Save" (UpdateABDescrip abd aba) `nodeEnabled`
              (model ^. abDescripValid)
            , spacer
            , button "Cancel" CloseUpdABEntry `nodeEnabled` True
            , filler
            ]
        ]
    showABAddressOverlay abd aba =
      alert CloseShowABAddress $
      vstack
        [ box_
            []
            (label "Address Book Entry" `styleBasic`
             [textFont "Bold", textColor white, textSize 12, padding 3]) `styleBasic`
          [bgColor btnColor, radius 2, padding 3]
        , spacer
        , hstack
            [ filler
            , label (txtWrapN abd 64) `styleBasic` [textFont "Bold"]
            , filler
            ]
        , spacer
        , hstack [filler, label_ (txtWrapN aba 64) [multiline], filler]
        , spacer
        , hstack
            [ filler
            , button "Edit Description" $ UpdateABEntry abd aba
            , spacer
            , button "Copy Address" $ CopyABAdress aba
            , spacer
            , button "Delete Entry" $ DeleteABEntry aba
            , filler
            ]
        ]
    msgAdrBookOverlay =
      alert CloseMsgAB $
      hstack
        [ filler
        , remixIcon remixErrorWarningFill `styleBasic`
          [textSize 32, textColor btnColor] `nodeVisible`
          (model ^. inError)
        , spacer
        , label_ (txtWrapN (fromMaybe "" (model ^. msgAB)) 64) [multiline]
        , filler
        ]
    dfBalOverlay =
      alert CloseFIATBalance $
      vstack
        [ box_
            []
            (label
               ("Account Balance in " <>
                (T.toUpper (c_currencyCode (model ^. configuration)))) `styleBasic`
             [textFont "Bold", textSize 12, textColor white]) `styleBasic`
          [bgColor btnColor, radius 2, padding 3]
        , filler
        , (label
             ("1 ZEC = " <>
              (T.pack (printf "%.2f" (model ^. zPrice))) <>
              " " <> (T.toUpper (c_currencyCode (model ^. configuration))))) `styleBasic`
          []
        , filler
        , (label
             ((T.pack (printf "%.8f" (model ^. aBal)) <>
               " ZEC = " <>
               (T.pack (printf "%.2f" ((model ^. zPrice) * (model ^. aBal)))) <>
               " " <> (T.toUpper (c_currencyCode (model ^. configuration)))))) `styleBasic`
          []
        ]
    showVKOverlay =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ box_
                          [alignMiddle]
                          (label ((model ^. vkTypeName) <> " Viewing Key") `styleBasic`
                           [ textFont "Bold"
                           , textColor white
                           , textSize 12
                           , padding 3
                           ]) `styleBasic`
                        [bgColor btnColor, radius 2, padding 3]
                      , spacer
                      , hstack
                          [ filler
                          , label_ (txtWrapN (model ^. vkData) 64) [multiline]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , button "Copy to Clipboard" $
                            CopyViewingKey
                              (model ^. vkTypeName)
                              (model ^. vkData)
                          , spacer
                          , button "Close" CloseShowVK
                          , filler
                          ]
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]
    nViewingkeyOverlay =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ box_
                          [alignMiddle]
                          (label "Import Viewing Key" `styleBasic`
                           [ textFont "Bold"
                           , textColor white
                           , textSize 12
                           , padding 3
                           ]) `styleBasic`
                        [bgColor btnColor, radius 2, padding 3]
                      , spacer
                      , hstack
                          [ filler
                          , label "Account Name:" `styleBasic`
                            [textFont "Bold", padding 3]
                          , spacer
                          , textField nVkName `styleBasic` [padding 3]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , label "Viewing Key:" `styleBasic`
                            [textFont "Bold", padding 3]
                          , spacer
                          , textField nVkString `styleBasic` [padding 3]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , box_
                              [alignMiddle]
                              ((label_
                                  (txtWrapN (model ^. nVkString) 80)
                                  [multiline]) `styleBasic`
                               [textSize 10]) `styleBasic`
                            [radius 4, border 2 blue, bgColor white, padding 4]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ spacer
                          , label "Wallet's Birthday Height:" `styleBasic`
                            [textFont "Bold", padding 3]
                          , spacer
                          , numericField_
                              nVkBirthday
                              [decimals 0, minValue 1687104] `styleBasic`
                            [width 150]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , button "Save Viewing Key" SaveViewKey
                          , spacer
                          , button "Close" CloseNVkShow
                          , filler
                          ]
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]
    nSeedPhraseOverlay =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ box_
                          [alignMiddle]
                          (label "Import Seed Phrase" `styleBasic`
                           [ textFont "Bold"
                           , textColor white
                           , textSize 12
                           , padding 3
                           ]) `styleBasic`
                        [bgColor btnColor, radius 2, padding 3]
                      , spacer
                      , hstack
                          [ filler
                          , label "Account Name:" `styleBasic`
                            [textFont "Bold", padding 3]
                          , spacer
                          , textField nSpName `styleBasic` [padding 3]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , label "Seed Phrase:" `styleBasic`
                            [textFont "Bold", padding 3]
                          , spacer
                          , textField nSpString `styleBasic` [padding 3]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , box_
                              [alignMiddle]
                              ((label_
                                  (txtWrapN (model ^. nSpString) 80)
                                  [multiline]) `styleBasic`
                               [textSize 10]) `styleBasic`
                            [radius 4, border 2 blue, bgColor white, padding 4]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ spacer
                          , label "Wallet's Birthday Height:" `styleBasic`
                            [textFont "Bold", padding 3]
                          , spacer
                          , numericField_
                              nSpBirthday
                              [decimals 0, minValue 1687104] `styleBasic`
                            [width 150]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , button "Save Seed Phrase" SaveSeedPhrase
                          , spacer
                          , button "Close" CloseNSpShow
                          , filler
                          ]
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]
    shieldOverlay =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ box_
                          [alignMiddle]
                          (label "Shield Zcash" `styleBasic`
                           [textFont "Bold", textSize 12])
                      , separatorLine `styleBasic` [fgColor btnColor]
                      , spacer
                      , label
                          ("Shield " <>
                           displayAmount (model ^. network) (model ^. tBalance) <>
                           "?") `styleBasic`
                        [width 50, textFont "Regular"]
                      , spacer
                      , box_
                          [alignMiddle]
                          (hstack
                             [ filler
                             , mainButton "Proceed" SendShield `nodeEnabled`
                               True
                             , spacer
                             , mainButton "Cancel" CloseShield `nodeEnabled`
                               True
                             , filler
                             ])
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]
    deShieldOverlay =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ box_
                          [alignMiddle]
                          (label "De-Shield Zcash" `styleBasic`
                           [textFont "Bold", textSize 12])
                      , separatorLine `styleBasic` [fgColor btnColor]
                      , spacer
                      , box_
                          []
                          (vstack
                             [ hstack
                                 [ label "Total Transparent : " `styleBasic`
                                   [textFont "Bold"]
                                 , label
                                     (displayAmount
                                        (model ^. network)
                                        (model ^. tBalance))
                                 ]
                             , spacer
                             , hstack
                                 [ label "Total Shielded : " `styleBasic`
                                   [textFont "Bold"]
                                 , label
                                     (displayAmount
                                        (model ^. network)
                                        (model ^. sBalance))
                                 ]
                             , spacer
                             , hstack
                                 [ label "Amount:" `styleBasic`
                                   [width 50, textFont "Bold"]
                                 , spacer
                                 , numericField_
                                     sendAmount
                                     [ decimals 8
                                     , minValue 0.0
                                     , maxValue
                                         (fromIntegral (model ^. sBalance) /
                                          100000000.0)
                                     , validInput sBalanceValid
                                     , onChange CheckAmount
                                     ] `styleBasic`
                                   [ width 150
                                   , styleIf
                                       (not $ model ^. sBalanceValid)
                                       (textColor red)
                                   ]
                                 ]
                             ])
                      , spacer
                      , box_
                          [alignMiddle]
                          (hstack
                             [ filler
                             , mainButton "Proceed" SendDeShield `nodeEnabled`
                               True
                             , spacer
                             , mainButton "Cancel" CloseDeShield `nodeEnabled`
                               True
                             , filler
                             ])
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]
    paymentURIOverlay =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ box_
                          []
                          (label "Create a Payment URI" `styleBasic`
                           [ textFont "Bold"
                           , textColor white
                           , textSize 10
                           , padding 3
                           ]) `styleBasic`
                        [bgColor btnColor, radius 2, padding 3]
                      , spacer
                      , hstack
                          [ filler
                          , label "Current Address:" `styleBasic`
                            [textFont "Bold"]
                          , spacer
                          , label_ (txtWrapN (model ^. uriAddr) 64) [multiline]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ label "Amount : " `styleBasic` [textFont "Bold"]
                          , numericField_ sendAmount [decimals 8] `nodeKey`
                            "floatInput" `styleBasic`
                            [ width 150
                            , styleIf
                                (model ^. sendAmount <= 0.0)
                                (textColor red)
                            ]
                          ]
                      , spacer
                      , hstack
                          [ label "Memo: " `styleBasic` [textFont "Bold"]
                          , spacer
                          , textField_ sendMemo [] `styleBasic` [width 300]
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , mainButton "Create URI" PrepareURIString `nodeEnabled`
                            (model ^. sendAmount > 0.0)
                          , spacer
                          , button "Cancel" ClosePaymentURIForm
                          , filler
                          ]
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]
    -- 
    showURIInProgress =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ spacer
                      , hstack
                          [ filler
                          , label
                              "Processing Payment URI, it will take a moment ....." `styleBasic`
                            [textFont "Bold", textSize 14]
                          , filler
                          ]
                      , spacer
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]
    --    
    showURIOverlay =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ box_
                          [alignMiddle]
                          (label "Payment URI" `styleBasic`
                           [ textFont "Bold"
                           , textColor white
                           , textSize 11
                           , padding 3
                           ]) `styleBasic`
                        [bgColor btnColor, radius 2, padding 3]
                      , spacer
                      , hstack
                          [ filler
                          , label_
                              (txtWrapN (model ^. uriString) 64)
                              [multiline]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , box_
                              [alignMiddle]
                              (case model ^. uriQRImage of
                                 Just img ->
                                   imageMem_
                                     "URIQRCode"
                                     (uriBytes img)
                                     (Size (uriWidth img) (uriHeight img))
                                     [fitWidth]
                                 Nothing ->
                                   image_
                                     (T.pack $
                                      (model ^. home) </>
                                      "Zenith/assets/cracked_qr.png")
                                     [fitHeight]) `styleBasic`
                            [bgColor white, height 120, width 120]
                          , filler
                          ]
                      , spacer
                      , hstack
                          [ filler
                          , button "Copy to Clipboard" $
                            CopyURIString (model ^. uriString)
                          , spacer
                          , button "Cancel" CloseShowURIOverlay
                          , filler
                          ]
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]
    --
    pmtUsingURIOverlay =
      box
        (vstack
           [ filler
           , hstack
               [ filler
               , box_
                   []
                   (vstack
                      [ box_
                          [alignMiddle]
                          (label "Pay using URI" `styleBasic`
                           [textColor white, textFont "Bold", textSize 12]) `styleBasic`
                        [bgColor btnColor]
                      , separatorLine `styleBasic` [fgColor btnColor]
                      , spacer
                      , hstack
                          [ label "URI :" `styleBasic`
                            [width 30, textFont "Bold"]
                          , spacer
                          , textArea uriString `styleBasic`
                            [width 170, height 30]
                          ]
                      , spacer
                      , box_
                          [alignMiddle]
                          (hstack
                             [ spacer
                             , button "Cancel" ClosePayUsingURI
                             , spacer
                             , mainButton "Process" ProcIfValidURI
                             , spacer
                             ])
                      ]) `styleBasic`
                 [radius 4, border 2 btnColor, bgColor white, padding 4]
               , filler
               ]
           , filler
           ]) `styleBasic`
      [bgColor (white & L.a .~ 0.5)]

--      alert CloseShowVK $
generateQRCodes :: Config -> IO ()
generateQRCodes config = do
  let dbFilePath = c_dbPath config
  pool <- runNoLoggingT $ initPool dbFilePath
  addrs <- getExternalAddresses pool
  mapM_ (checkExistingQrs pool) addrs
  where
    checkExistingQrs :: ConnectionPool -> Entity WalletAddress -> IO ()
    checkExistingQrs pool wAddr = do
      s <- getQrCodes pool (entityKey wAddr)
      if not (null s)
        then return ()
        else do
          generateOneQr pool OrchardPool wAddr
          generateOneQr pool SaplingPool wAddr
          generateOneQr pool TransparentPool wAddr
    generateOneQr ::
         ConnectionPool -> ZcashPool -> Entity WalletAddress -> IO ()
    generateOneQr p zp wAddr =
      case encodeText (defaultQRCodeOptions L) Utf8WithoutECI =<<
           dispAddr zp (entityVal wAddr) of
        Just qr -> do
          _ <-
            runNoLoggingT $
            saveQrCode p $
            QrCode
              (entityKey wAddr)
              zp
              (qrCodeData qr)
              (qrCodeH qr)
              (qrCodeW qr)
              (walletAddressName (entityVal wAddr) <> T.pack (show zp))
          return ()
        Nothing -> return ()
    qrCodeImg :: QRImage -> Image PixelRGBA8
    qrCodeImg qr = promoteImage (toImage 4 2 qr)
    qrCodeH :: QRImage -> Int
    qrCodeH qr = fromIntegral $ imageHeight $ qrCodeImg qr
    qrCodeW :: QRImage -> Int
    qrCodeW qr = fromIntegral $ imageWidth $ qrCodeImg qr
    qrCodeData :: QRImage -> BS.ByteString
    qrCodeData qr =
      BS.pack $
      pixelFold
        (\bs _ _ (PixelRGBA8 i j k l) -> bs <> [i, j, k, l])
        []
        (qrCodeImg qr)
    dispAddr :: ZcashPool -> WalletAddress -> Maybe T.Text
    dispAddr zp w =
      case zp of
        TransparentPool ->
          T.append "zcash:" .
          encodeTransparentReceiver
            (maybe
               TestNet
               ua_net
               ((isValidUnifiedAddress .
                 E.encodeUtf8 . getUA . walletAddressUAddress)
                  w)) <$>
          (t_rec =<<
           (isValidUnifiedAddress . E.encodeUtf8 . getUA . walletAddressUAddress)
             w)
        SaplingPool ->
          T.append "zcash:" <$>
          (getSaplingFromUA . E.encodeUtf8 . getUA . walletAddressUAddress) w
        OrchardPool ->
          Just $ (T.append "zcash:" . getUA . walletAddressUAddress) w
        SproutPool -> Nothing

handleEvent ::
     WidgetEnv AppModel AppEvent
  -> WidgetNode AppModel AppEvent
  -> AppModel
  -> AppEvent
  -> [AppEventResponse AppModel AppEvent]
handleEvent wenv node model evt =
  case evt of
    AppInit ->
      [Event NewWallet | isNothing currentWallet] <> [Producer timeTicker]
    ShowMsg t -> [Model $ model & msg ?~ t & menuPopup .~ False]
    ShowError t ->
      [ Model $
        model & msg ?~ t & menuPopup .~ False & inError .~ True & modalMsg .~
        Nothing
      ]
    ShowModal t -> [Model $ model & modalMsg ?~ t]
    WalletClicked -> [Model $ model & walPopup .~ True]
    AccountClicked -> [Model $ model & accPopup .~ True]
    MenuClicked -> [Model $ model & menuPopup .~ True]
    NewClicked -> [Model $ model & newPopup .~ not (model ^. newPopup)]
    NewInternalAddress acc -> [Task $ addNewAddress "Change" Internal acc]
    NewAddress acc ->
      [ Model $
        model & confirmTitle ?~ "New Address" & confirmAccept .~ "Create" &
        confirmCancel .~
        "Cancel" &
        confirmEvent .~
        SaveAddress acc &
        menuPopup .~
        False
      ]
    NewAccount wal ->
      if (zcashWalletLocal . entityVal <$> wal) == Just True
        then [ Model $
               model & confirmTitle ?~ "New Account" & confirmAccept .~ "Create" &
               confirmCancel .~
               "Cancel" &
               confirmEvent .~
               SaveAccount wal &
               menuPopup .~
               False
             ]
        else [ Event $ ShowError "Wallet is read-only."
             , Model $ model & menuPopup .~ False
             ]
    NewWallet ->
      [ Model $
        model & confirmTitle ?~ "New Wallet" & confirmAccept .~ "Create" &
        confirmCancel .~
        "Cancel" &
        confirmEvent .~
        SaveWallet &
        menuPopup .~
        False
      ]
    ConfirmCancel -> [Model $ model & confirmTitle .~ Nothing & mainInput .~ ""]
    ViewingKeysClicked ->
      [Model $ model & viewingKeyPopup .~ not (model ^. viewingKeyPopup)]
    ShowSeed -> [Model $ model & showSeed .~ True & menuPopup .~ False]
    ShowSend ->
      [ Model $
        model & openSend .~ True & privacyChoice .~ Full & recipientValid .~
        False
      ]
    SendTx ->
      case currentAccount of
        Nothing -> [Event $ ShowError "No account available", Event CancelSend]
        Just acc ->
          case currentWallet of
            Nothing ->
              [Event $ ShowError "No wallet available", Event CancelSend]
            Just wal ->
              [ Producer $
                sendTransaction
                  (model ^. configuration)
                  (model ^. network)
                  (entityKey acc)
                  (zcashWalletLastSync $ entityVal wal)
                  (fromFloatDigits $ model ^. sendAmount)
                  (model ^. sendRecipient)
                  (model ^. sendMemo)
                  (model ^. privacyChoice)
              , Event CancelSend
              ]
    CancelSend ->
      [ Model $
        model & openSend .~ False & sendRecipient .~ "" & sendAmount .~ 0.0 &
        sendMemo .~
        "" &
        uriString .~
        ""
      ]
    SaveAddress acc ->
      if T.length (model ^. mainInput) > 1
        then [ Task $ addNewAddress (model ^. mainInput) External acc
             , Event $ ShowModal "Generating QR codes..."
             , Event ConfirmCancel
             ]
        else [Event $ ShowError "Invalid input", Event ConfirmCancel]
    SaveAccount wal ->
      if T.length (model ^. mainInput) > 1
        then [ Task $ addNewAccount (model ^. mainInput) wal
             , Event ConfirmCancel
             ]
        else [Event $ ShowError "Invalid input", Event ConfirmCancel]
    SaveWallet ->
      if T.length (model ^. mainInput) > 1
        then [Task addNewWallet, Event ConfirmCancel]
        else [Event $ ShowError "Invalid input"]
    SetPool p ->
      [ Model $ model & selPool .~ p & modalMsg .~ Nothing
      , Task $
        SwitchQr <$> do
          dbPool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          case currentAddress of
            Nothing -> return Nothing
            Just wAddr -> getQrCode dbPool p $ entityKey wAddr
      , Task $
        LoadTxs <$> do
          dbPool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          case currentAddress of
            Nothing -> return []
            Just wAddr -> getUserTx dbPool $ entityKey wAddr
      ]
    SwitchQr q -> [Model $ model & qrCodeWidget .~ q]
    SwitchAddr i -> [Model $ model & selAddr .~ i, Event $ SetPool OrchardPool]
    SwitchAcc i ->
      [ Model $ model & selAcc .~ i
      , Task $
        LoadAddrs <$> do
          dbPool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          case selectAccount i of
            Nothing -> return []
            Just acc -> runNoLoggingT $ getAddresses dbPool $ entityKey acc
      , Task $
        UpdateBalance <$> do
          dbPool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          case selectAccount i of
            Nothing -> return (0, 0, 0, 0)
            Just acc -> do
              b <- getBalance dbPool $ entityKey acc
              u <- getUnconfirmedBalance dbPool $ entityKey acc
              s <- getShieldedBalance dbPool $ entityKey acc
              t <- getTransparentBalance dbPool $ entityKey acc
              return (b, u, s, t)
      , Event $ SetPool OrchardPool
      ]
    SwitchWal i ->
      [ Model $ model & selWallet .~ i & selAcc .~ 0 & selAddr .~ 0
      , Task $
        LoadAccs <$> do
          dbPool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          case selectWallet i of
            Nothing -> return []
            Just wal -> runNoLoggingT $ getAccounts dbPool $ entityKey wal
      ]
    UpdateBalance (b, u, s, t) ->
      [ Model $
        model & balance .~ b & sBalance .~ s & tBalance .~ t & unconfBalance .~
        (if u == 0
           then Nothing
           else Just u)
      ]
    CopyAddr a ->
      [ setClipboardData ClipboardEmpty
      , setClipboardData $
        ClipboardText $
        case model ^. selPool of
          OrchardPool ->
            maybe "None" (getUA . walletAddressUAddress . entityVal) a
          SaplingPool ->
            fromMaybe "None" $
            (getSaplingFromUA .
             E.encodeUtf8 . getUA . walletAddressUAddress . entityVal) =<<
            a
          SproutPool -> "None"
          TransparentPool ->
            maybe "None" (encodeTransparentReceiver (model ^. network)) $
            t_rec =<<
            (isValidUnifiedAddress .
             E.encodeUtf8 . getUA . walletAddressUAddress . entityVal) =<<
            a
      , Event $ ShowMsg "Copied address!"
      ]
    CopySeed s ->
      [ setClipboardData ClipboardEmpty
      , setClipboardData $ ClipboardText s
      , Event $ ShowMsg "Copied seed phrase!"
      ]
    CopyTx t ->
      [ setClipboardData ClipboardEmpty
      , setClipboardData $ ClipboardText t
      , Event $ ShowMsg "Copied transaction ID!"
      ]
    LoadTxs t -> [Model $ model & transactions .~ t]
    CheckChange ->
      [ Task $ do
          case currentAccount of
            Nothing -> return $ ShowError "No account available"
            Just acc -> do
              pool <-
                runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
              chgAddr <- getInternalAddresses pool $ entityKey acc
              if not (null chgAddr) ||
                 (zcashAccountType (entityVal acc) == IncomingViewKey)
                then return $ SetPool OrchardPool
                else return $ NewInternalAddress currentAccount
      ]
    LoadAddrs a ->
      if not (null a)
        then [ Model $ model & addresses .~ a
             , Event CheckChange
             , Event $ SwitchAddr $ model ^. selAddr
             , Event $ SetPool OrchardPool
             ]
        else [ Event $ NewAddress currentAccount
             , Event $ NewInternalAddress currentAccount
             ]
    LoadAccs a ->
      if not (null a)
        then [Model $ model & accounts .~ a, Event $ SwitchAcc 0]
        else [Event $ NewAccount currentWallet]
    LoadWallets a ->
      if not (null a)
        then [ Model $ model & wallets .~ a & modalMsg .~ Nothing
             , Event $ SwitchWal $ model ^. selWallet
             ]
        else [Event NewWallet]
    CloseMsg -> [Model $ model & msg .~ Nothing & inError .~ False]
    CloseSeed -> [Model $ model & showSeed .~ False]
    CloseTx -> [Model $ model & showTx .~ Nothing]
    CloseTxId -> [Model $ model & showId .~ Nothing]
    ShowTx i -> [Model $ model & showTx ?~ i]
    TickUp ->
      if isNothing (model ^. modalMsg)
        then if (model ^. timer) < 90
               then [Model $ model & timer .~ (1 + model ^. timer)]
               else if (model ^. barValue) == 1.0
                      then [ Model $
                             model & timer .~ 0 & barValue .~ 0.0 & modalMsg ?~
                             "Downloading blocks..."
                           , Producer $
                             runNoLoggingT .
                             scanZebra
                               (c_dbPath $ model ^. configuration)
                               (c_zebraHost $ model ^. configuration)
                               (c_zebraPort $ model ^. configuration)
                               (model ^. network)
                           ]
                      else [Model $ model & timer .~ 0]
        else [Model $ model & timer .~ 0]
    TreeSync -> [Model $ model & modalMsg ?~ "Updating commitment trees..."]
    StartSync ->
      [ Model $ model & modalMsg ?~ "Updating wallet..."
      , Task $ do
          case currentWallet of
            Nothing -> return $ ShowError "No wallet available"
            Just cW -> do
              runNoLoggingT $ syncWallet (model ^. configuration) cW
              pool <-
                runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
              wL <- getWallets pool (model ^. network)
              return $ LoadWallets wL
      ]
    SyncVal i ->
      if (i + model ^. barValue) >= 0.999
        then [Model $ model & barValue .~ 1.0 & modalMsg .~ Nothing]
        else [ Model $
               model & barValue .~ validBarValue (i + model ^. barValue) &
               modalMsg ?~
               ("Wallet Sync: " <>
                T.pack (printf "%.2f%%" (model ^. barValue * 100)))
             ]
    ResetRecipientValid -> [Model $ model & recipientValid .~ False]
    CheckRecipient a ->
      [ Model $
        model & recipientValid .~ isRecipientValidGUI (model ^. privacyChoice) a
      ]
    CheckAmount i ->
      [ Model $
        model & amountValid .~
        (i < (fromIntegral (model ^. balance) / 100000000.0))
      ]
    -- 
    ShowTxId tx -> [Model $ model & showId ?~ tx & modalMsg .~ Nothing]
    -- |
    -- | Address Book Events
    -- |
    CheckValidAddress a ->
      [Model $ model & abAddressValid .~ isZecAddressValid a]
    CheckValidDescrip a -> [Model $ model & abDescripValid .~ isValidString a]
    ShowAdrBook ->
      if null (model ^. abaddressList)
        then [Model $ model & newAdrBkEntry .~ True & menuPopup .~ False]
        else [Model $ model & showAdrBook .~ True & menuPopup .~ False]
    CloseAdrBook -> [Model $ model & showAdrBook .~ False]
    NewAdrBkEntry ->
      [Model $ model & newAdrBkEntry .~ True & menuPopup .~ False]
    CloseNewAdrBook -> do
      [Model $ model & newAdrBkEntry .~ False]
    UpdateABEntry d a ->
      [ Model $
        model & abdescrip .~ d & abaddress .~ a & updateABAddress .~ True &
        abDescripValid .~
        True &
        menuPopup .~
        False
      ]
    CloseUpdABEntry -> do
      [Model $ model & updateABAddress .~ False]
    SaveNewABEntry ->
      [ Task $
        saveAddrBook
          (model ^. configuration)
          (ZcashNetDB (model ^. network))
          (model ^. abdescrip)
          (model ^. abaddress)
      , Model $
        model & abdescrip .~ "" & abaddress .~ "" & newAdrBkEntry .~ False
      , Task $ do
          dbPool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          abList <- getAdrBook dbPool $ model ^. network
          return $ LoadAbList abList
      ]
    ShowABAddress d a ->
      [ Model $
        model & abdescrip .~ d & abaddress .~ a & showABAddress .~ True &
        menuPopup .~
        False
      ]
    CloseShowABAddress ->
      [Model $ model & showABAddress .~ False & inError .~ False]
    CopyABAdress a ->
      [ setClipboardData ClipboardEmpty
      , setClipboardData $ ClipboardText a
      , Event $ ShowMessage "Address copied!!"
      ]
    CopyViewingKey t v ->
      [ setClipboardData ClipboardEmpty
      , setClipboardData $ ClipboardText v
      , Event $ ShowMessage (t <> " viewing key copied!!")
      ]
    CopyURIString u ->
      [ setClipboardData ClipboardEmpty
      , setClipboardData $ ClipboardText u
      , Event $ ShowMessage "URI string copied to clipboard!!"
      ]
    DeleteABEntry a ->
      [ Task $ deleteAdrBook (model ^. configuration) a
      , Model $
        model & abdescrip .~ "" & abaddress .~ "" & showABAddress .~ False
      , Task $ do
          dbPool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          abList <- getAdrBook dbPool $ model ^. network
          return $ LoadAbList abList
      ]
    ShowMessage a -> [Model $ model & msgAB ?~ a & menuPopup .~ False]
    NotImplemented ->
      [ Model $
        model & msgAB ?~ "Function not implemented..." & menuPopup .~ False
      ]
    CloseMsgAB -> [Model $ model & msgAB .~ Nothing & inError .~ False]
    CloseShowVK ->
      [ Model $
        model & vkTypeName .~ "" & vkData .~ "" & viewingKeyDisplay .~ False
      ]
    --
    -- Show Import Viewing Key Form
    --
    SetTimerCount -> [Model $ model & timer .~ 85]
    ShowNViewingKey -> [Model $ model & nVkShow .~ True & menuPopup .~ False]
    CloseNVkShow -> [Model $ model & nVkShow .~ False]
    SaveViewKey ->
      [ Task $ do
          pool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          let inKey = parseVK $ model ^. nVkString
          case inKey of
            Nothing -> return $ ShowError "Invalid viewing key"
            Just k -> do
              x <-
                importViewingKey
                  pool
                  (model ^. nVkName)
                  (model ^. network)
                  k
                  (model ^. nVkBirthday)
              case x of
                Left e -> return $ ShowError e
                Right i -> return $ ReportVKeySaved
      ]
    --
    ReportVKeySaved ->
      [ Task $ do
          generateQRCodes $ model ^. configuration
          pool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          wL <- getWallets pool (model ^. network)
          return $ LoadWallets wL
      , Event $ CloseNVkShow
      , Event $ ShowMessage "Viewing Key imported!"
      , Event $ StartSync
      ]
    --
    -- Show Import Seed Phrase Form
    --
    SaveSeedPhrase ->
      [ Task $ do
          pool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          if isNotEmptyAfterTrim (model ^. nSpName)
            then if isNotEmptyAfterTrim (model ^. nSpName)
                   then if (model ^. nSpBirthday) >= 1687104
                          then do
                            let sp = Phrase (E.encodeUtf8 (model ^. nSpString))
                            x <-
                              importSeedPhrase
                                pool
                                (model ^. nSpName)
                                (model ^. network)
                                sp
                                (model ^. nSpBirthday)
                            case x of
                              Left e -> return $ ShowError e
                              Right i -> return $ ReportSeedSaved
                          else return $ ShowError "Invalid Birthday Block!"
                   else return $ ShowError "Seed Phrase not provided"
            else return $ ShowError "Seed Phrase name invalid!"
      ]
    --  
    ReportSeedSaved ->
      [ Task $ do
          generateQRCodes $ model ^. configuration
          pool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          wL <- getWallets pool (model ^. network)
          return $ LoadWallets wL
      , Event $ CloseNSpShow
      , Event $ ShowMessage "Seed Phrase imported!!"
      , Event $ SetTimerCount
      ]
    ShowNSeedPhrase ->
      [ Model $
        model & nSpShow .~ True & nSpName .~ "" & nSpString .~ "" & nSpBirthday .~
        0 &
        menuPopup .~
        False
      ]
    CloseNSpShow -> [Model $ model & nSpShow .~ False]
    --
    -- Show Balance in FIAT
    --
    DisplayFIATBalance zpr abal ->
      [ Model $
        model & zPrice .~ zpr & aBal .~ abal & displayFIATBalance .~ True &
        menuPopup .~
        False
      ]
    ShowFIATBalance ->
      if model ^. network == MainNet
        then [Task $ sfBalance (model ^. configuration)]
        else [ Model $ model & zPrice .~ 0.0 & aBal .~ 0.0
             , Event $ ShowError "Balance conversion not available for TestNet"
             ]
    CloseFIATBalance -> [Model $ model & displayFIATBalance .~ False]
    --
    -- Prepare Viewing Keys
    --
    PrepareViewingKey vkType cAcc ->
      case vkType of
        VkFull -> [Task $ getFullVkFromAcc cAcc]
        VkIncoming -> [Task $ getIncomingVkFromAcc cAcc]
        VkNone -> []
    --
    -- Show Viewing Keys
    --
    ShowViewingKey vkType vkText ->
      case vkType of
        VkFull ->
          [ Model $
            model & vkTypeName .~ "Full" & vkData .~ vkText & viewingKeyDisplay .~
            True &
            menuPopup .~
            False
          ]
        VkIncoming ->
          [ Model $
            model & vkTypeName .~ "Incoming" & vkData .~ vkText &
            viewingKeyDisplay .~
            True &
            menuPopup .~
            False
          ]
        VkNone -> []
    --
    -- Display PaymentURI Form
    --
    PreparePaymentURIForm -> [Task $ getCurrentAddress currentAddress]
    -- 
    DisplayPaymentURIForm ua ->
      [ Model $
        model & uriString .~ "" & uriAddr .~ ua & amountValid .~ False &
        sendAmount .~
        0.0 &
        sendMemo .~
        "" &
        paymentURIDisplay .~
        True &
        menuPopup .~
        False
      ]
    ClosePaymentURIForm ->
      [Model $ model & paymentURIDisplay .~ False & uriString .~ ""]
    --
    -- Generate URI 
    --
    PrepareURIString ->
      [ Task $
        genURIString
          (model ^. uriAddr)
          (model ^. sendAmount)
          (model ^. sendMemo)
      , Model $ model & uriQRInProgress .~ True
      ]
    ShowURIOverlay qr uStr ->
      [ Model $
        model & uriString .~ uStr & uriQRImage .~ qr & uriQRInProgress .~ True &
        paymentURIDisplay .~
        False &
        showURIDisplay .~
        True &
        uriQRInProgress .~
        False
      ]
    CloseShowURIOverlay ->
      [ Model $
        model & showURIDisplay .~ False & uriString .~ "" & uriQRInProgress .~
        False &
        uriQRImage .~
        Nothing
      ]
    QRImageLoaded -> [Model $ model & uriQRInProgress .~ False]
    --
    -- Display Pay using URI Form
    --
    DisplayPayUsingURI ->
      [ Model $
        model & usepmtURIOverlay .~ True & menuPopup .~ False & uriString .~ ""
      ]
    ClosePayUsingURI -> [Model $ model & usepmtURIOverlay .~ False]
    ProcIfValidURI -> do
      let zp = parseZcashPayment $ T.unpack (model ^. uriString)
      case zp of
        Right p -> do
          case uriAmount p of
            Just a ->
              [ Model $
                model & usepmtURIOverlay .~ False & openSend .~ True &
                privacyChoice .~
                Full &
                recipientValid .~
                True &
                sendRecipient .~
                T.pack (uriAddress p) &
                sendAmount .~
                realToFrac a &
                sendMemo .~
                (uriMemo p)
              , Event $ ClosePaymentURIForm
              ]
            Nothing ->
              [ Model $
                model & usepmtURIOverlay .~ False & openSend .~ False &
                uriString .~
                ""
              , Event $ ShowError "Invalid URI"
              ]
        Left e ->
          [ Model $
            model & usepmtURIOverlay .~ False & openSend .~ False & uriString .~
            ""
          , Event $ ShowError "Invalid URI"
          ]
    ShowShield ->
      if model ^. tBalance > 0
        then [Model $ model & shieldZec .~ True & menuPopup .~ False]
        else [Event $ ShowError "No transparent funds in this account"]
    CloseShield -> [Model $ model & shieldZec .~ False]
    ShowDeShield -> [Model $ model & deShieldZec .~ True & menuPopup .~ False]
    CloseDeShield -> [Model $ model & deShieldZec .~ False]
    LoadAbList a -> [Model $ model & abaddressList .~ a]
    UpdateABDescrip d a ->
      [ Task $ updAddrBookDescrip (model ^. configuration) d a
      , Model $
        model & abdescrip .~ "" & abaddress .~ "" & updateABAddress .~ False &
        showABAddress .~
        False
      , Task $ do
          dbPool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          abList <- getAdrBook dbPool $ model ^. network
          return $ LoadAbList abList
      ]
    SendDeShield ->
      case currentAccount of
        Nothing ->
          [Event $ ShowError "No account available", Event CloseDeShield]
        Just acc ->
          [ Producer $
            deshieldTransaction
              (model ^. configuration)
              (model ^. network)
              (entityKey acc)
              currentAddress
              (fromFloatDigits $ model ^. sendAmount)
          , Event CloseDeShield
          ]
    SendShield ->
      case currentAccount of
        Nothing -> [Event $ ShowError "No account available", Event CloseShield]
        Just acc ->
          [ Producer $
            shieldTransaction
              (model ^. configuration)
              (model ^. network)
              (entityKey acc)
          , Event CloseShield
          ]
  where
    currentWallet =
      if null (model ^. wallets)
        then Nothing
        else Just ((model ^. wallets) !! (model ^. selWallet))
    selectWallet i =
      if null (model ^. wallets)
        then Nothing
        else Just ((model ^. wallets) !! i)
    currentAccount =
      if null (model ^. accounts)
        then Nothing
        else Just ((model ^. accounts) !! (model ^. selAcc))
    selectAccount i =
      if null (model ^. accounts)
        then Nothing
        else Just ((model ^. accounts) !! i)
    currentAddress =
      if null (model ^. addresses)
        then Nothing
        else Just ((model ^. addresses) !! (model ^. selAddr))
    addNewAddress ::
         T.Text -> Scope -> Maybe (Entity ZcashAccount) -> IO AppEvent
    addNewAddress n scope acc = do
      case acc of
        Nothing -> return $ ShowError "No account available"
        Just a -> do
          pool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          maxAddr <- getMaxAddress pool (entityKey a) scope
          uA <-
            try $ createWalletAddress n (maxAddr + 1) (model ^. network) scope a :: IO
              (Either IOError WalletAddress)
          case uA of
            Left e -> return $ ShowError $ "Error: " <> T.pack (show e)
            Right uA' -> do
              nAddr <- saveAddress pool uA'
              case nAddr of
                Nothing -> return $ ShowError $ "Address already exists: " <> n
                Just _x -> do
                  generateQRCodes $ model ^. configuration
                  addrL <- runNoLoggingT $ getAddresses pool $ entityKey a
                  return $ LoadAddrs addrL
    addNewAccount :: T.Text -> Maybe (Entity ZcashWallet) -> IO AppEvent
    addNewAccount n w = do
      case w of
        Nothing -> return $ ShowError "No wallet available"
        Just w' -> do
          pool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
          accIx <- getMaxAccount pool $ entityKey w'
          newAcc <-
            try $
            createZcashAccount
              n
              (accIx + 1)
              (getNet $ zcashWalletNetwork $ entityVal w')
              w' :: IO (Either IOError ZcashAccount)
          case newAcc of
            Left e -> return $ ShowError "Failed to create account"
            Right newAcc' -> do
              r <- saveAccount pool newAcc'
              case r of
                Nothing -> return $ ShowError "Account already exists"
                Just _x -> do
                  aList <- runNoLoggingT $ getAccounts pool (entityKey w')
                  return $ LoadAccs aList
    addNewWallet :: IO AppEvent
    addNewWallet = do
      sP <- generateWalletSeedPhrase
      pool <- runNoLoggingT $ initPool $ c_dbPath $ model ^. configuration
      bc <-
        try $
        checkBlockChain
          (c_zebraHost $ model ^. configuration)
          (c_zebraPort $ model ^. configuration) :: IO
          (Either IOError ZebraGetBlockChainInfo)
      case bc of
        Left e1 -> return $ ShowError $ T.pack $ show e1
        Right chainInfo -> do
          r <-
            saveWallet pool $
            ZcashWallet
              (model ^. mainInput)
              (ZcashNetDB (model ^. network))
              (PhraseDB sP)
              (zgb_blocks chainInfo)
              0
              True
          case r of
            Nothing -> return $ ShowError "Wallet already exists"
            Just _ -> do
              wL <- getWallets pool (model ^. network)
              return $ LoadWallets wL
    -- |          
    -- | Address Book -> save new entry into database
    -- |
    saveAddrBook :: Config -> ZcashNetDB -> T.Text -> T.Text -> IO AppEvent
    saveAddrBook config n d a = do
      pool <- runNoLoggingT $ initPool $ c_dbPath config
      res <- liftIO $ saveAdrsInAdrBook pool $ AddressBook n d a
      case res of
        Nothing -> return $ ShowMessage "Error saving AddressBook entry..."
        Just _ -> return $ ShowMessage "New Address Book entry added!!"
    -- |
    -- | Address Book -> save new entry into database
    -- |
    deleteAdrBook :: Config -> T.Text -> IO AppEvent
    deleteAdrBook config a = do
      pool <- runNoLoggingT $ initPool $ c_dbPath config
      res <- liftIO $ deleteAdrsFromAB pool a
      return $ ShowMessage "Address Book entry deleted!!"
    -- |          
    -- | Address Book -> save new entry into database
    -- |
    updAddrBookDescrip :: Config -> T.Text -> T.Text -> IO AppEvent
    updAddrBookDescrip config d a = do
      pool <- runNoLoggingT $ initPool $ c_dbPath config
      res <- liftIO $ updateAdrsInAdrBook pool d a a
      return $ ShowMessage "Address Book entry updated!!"
    --
    dbal :: Integer -> Double
    dbal a = fromIntegral a
    -- 
    sfBalance :: Config -> IO AppEvent
    sfBalance config = do
      zpr <- liftIO $ getZcashPrice $ c_currencyCode config
      case zpr of
        Just zp -> do
          let zbal = (dbal (model ^. balance)) / 100000000
          return $ DisplayFIATBalance zp zbal
        Nothing ->
          return $
          ShowMessage
            ("Currency not supported [" <> c_currencyCode config <> "]")
    --
    procIfValidURI :: T.Text -> IO AppEvent
    procIfValidURI ustr = do
      return ShowSend
    --
    -- Get Full Viewing Key  
    --
    -- getFullVk :: ZcashNet -> Maybe (Entity ZcashAccount) -> IO AppEvent
    -- getFullVk n cAcc = do
    --   case cAcc of
    --     Nothing ->
    --       return $ ShowMessage "Viewing Key Error: No account selected!"
    --     Just acc -> do
    --       let osk =
    --             getOrchSK $ fromJust $ zcashAccountOrchSpendKey $ entityVal acc
    --       let ssk =
    --             getSapSK $ fromJust $ zcashAccountSapSpendKey $ entityVal acc
    --       let tsk =
    --             getTranSK $ fromJust $ zcashAccountTPrivateKey $ entityVal acc
    --       fvk <- deriveUfvk n (Just osk) (Just ssk) (Just tsk)
    --       return $ ShowViewingKey VkFull (encodeVK fvk)
    --
    -- Get Full Viewing Key from Account Record 
    --
    getFullVkFromAcc :: Maybe (Entity ZcashAccount) -> IO AppEvent
    getFullVkFromAcc cAcc = do
      case cAcc of
        Nothing ->
          return $ ShowMessage "Viewing Key Error: No account selected!"
        Just acc -> do
          case zcashAccountFvk $ entityVal acc of
            Nothing -> return $ ShowMessage "No full viewing key available."
            Just fvk ->
              return $ ShowViewingKey VkFull (encodeVK $ FullVk $ getFvk fvk)
    --
    -- Get Incoming Viewing Key from Account Record
    --
    getIncomingVkFromAcc :: Maybe (Entity ZcashAccount) -> IO AppEvent
    getIncomingVkFromAcc cAcc = do
      case cAcc of
        Nothing ->
          return $ ShowMessage "Viewing Key Error: No account selected!"
        Just acc -> do
          case zcashAccountIvk $ entityVal acc of
            Nothing -> return $ ShowMessage "No incoming viewing key available."
            Just ivk ->
              return $
              ShowViewingKey VkIncoming (encodeVK $ IncomingVk $ getIvk ivk)
    --
    -- Get curret zcash address 
    --   
    getCurrentAddress :: Maybe (Entity WalletAddress) -> IO AppEvent
    getCurrentAddress a = do
      let ua =
            case model ^. selPool of
              OrchardPool ->
                maybe "None" (getUA . walletAddressUAddress . entityVal) a
              SaplingPool ->
                fromMaybe "None" $
                (getSaplingFromUA .
                 E.encodeUtf8 . getUA . walletAddressUAddress . entityVal) =<<
                a
              SproutPool -> "None"
              TransparentPool ->
                maybe "None" (encodeTransparentReceiver (model ^. network)) $
                t_rec =<<
                (isValidUnifiedAddress .
                 E.encodeUtf8 . getUA . walletAddressUAddress . entityVal) =<<
                a
      return $ DisplayPaymentURIForm ua
    --
    -- Generate a QR code for a String and save it as an PNG image
    --
    genURIStringQR :: Int -> T.Text -> Maybe URIQrCode
    genURIStringQR scaleFactor uriStr = do
      let qrOptions = defaultQRCodeOptions L
      case encodeText qrOptions Utf8WithoutECI uriStr of
        Nothing -> Nothing
        Just qrCode -> do
          let qri = promoteImage (toImage 4 scaleFactor qrCode)
          let qrw = fromIntegral $ imageWidth qri
          let qrh = fromIntegral $ imageHeight qri
          let qrb =
                BS.pack $
                pixelFold
                  (\bs _ _ (PixelRGBA8 i j k l) -> bs <> [i, j, k, l])
                  []
                  qri
          Just URIQrCode {uriBytes = qrb, uriWidth = qrw, uriHeight = qrh}
    --
    -- Gen URI String
    --   
    genURIString :: T.Text -> Float -> T.Text -> IO AppEvent
    genURIString addr mAmt mMemo = do
      let mM =
            case mMemo of
              "" -> Nothing
              _ -> Just (T.unpack mMemo)
      let uriSt = createZip321 (T.unpack addr) (Just (realToFrac mAmt)) mM
      return $ ShowURIOverlay (genURIStringQR 3 (T.pack uriSt)) (T.pack uriSt)

scanZebra ::
     T.Text
  -> T.Text
  -> Int
  -> ZcashNet
  -> (AppEvent -> IO ())
  -> NoLoggingT IO ()
scanZebra dbPath zHost zPort net sendMsg = do
  bStatus <- liftIO $ checkBlockChain zHost zPort
  logDebugN "starting DB pool"
  pool <- liftIO $ runNoLoggingT $ initPool dbPath
  logDebugN "started DB pool!"
  b <- liftIO $ getMinBirthdayHeight pool $ ZcashNetDB net
  dbBlock <- liftIO $ getMaxBlock pool $ ZcashNetDB net
  chkBlock <- liftIO $ checkIntegrity dbPath zHost zPort net dbBlock 1
  logDebugN $ "dbBlock: " <> T.pack (show dbBlock)
  logDebugN $ "chkBlock: " <> T.pack (show chkBlock)
  syncChk <- liftIO $ isSyncing pool
  if syncChk
    then liftIO $ sendMsg (ShowError "Sync already in progress")
    else do
      let sb =
            if chkBlock == dbBlock
              then max dbBlock b
              else max chkBlock b
      unless (chkBlock == dbBlock || chkBlock == 1) $
        liftIO $ runNoLoggingT $ rewindWalletData pool sb $ ZcashNetDB net
      if sb > zgb_blocks bStatus || sb < 1
        then liftIO $ sendMsg (ShowError "Invalid starting block for scan")
        else do
          let bList = [(sb + 1) .. (zgb_blocks bStatus)]
          if not (null bList)
            then do
              let step = (1.0 :: Float) / fromIntegral (length bList)
              _ <- liftIO $ startSync pool
              mapM_ (liftIO . processBlock pool step) bList
              confUp <-
                liftIO $ try $ updateConfs zHost zPort pool :: NoLoggingT
                  IO
                  (Either IOError ())
              case confUp of
                Left _e0 -> do
                  _ <- liftIO $ completeSync pool Failed
                  liftIO $
                    sendMsg
                      (ShowError "Failed to update unconfirmed transactions")
                Right _ -> do
                  liftIO $ sendMsg TreeSync
                  _ <-
                    liftIO $
                    runNoLoggingT $
                    updateCommitmentTrees pool zHost zPort $ ZcashNetDB net
                  _ <- liftIO $ completeSync pool Successful
                  logDebugN "Starting wallet sync"
                  liftIO $ sendMsg StartSync
            else liftIO $ sendMsg (SyncVal 1.0)
  where
    processBlock :: ConnectionPool -> Float -> Int -> IO ()
    processBlock pool step bl = do
      r <-
        liftIO $
        makeZebraCall
          zHost
          zPort
          "getblock"
          [Data.Aeson.String $ showt bl, jsonNumber 1]
      case r of
        Left e1 -> do
          _ <- completeSync pool Failed
          sendMsg (ShowError $ showt e1)
        Right blk -> do
          bi <-
            saveBlock pool $
            ZcashBlock
              (fromIntegral $ bl_height blk)
              (HexStringDB $ bl_hash blk)
              (fromIntegral $ bl_confirmations blk)
              (fromIntegral $ bl_time blk)
              (ZcashNetDB net)
          mapM_ (processTx zHost zPort bi pool) $ bl_txs blk
          sendMsg (SyncVal step)

shieldTransaction ::
     Config -> ZcashNet -> ZcashAccountId -> (AppEvent -> IO ()) -> IO ()
shieldTransaction config znet accId sendMsg = do
  sendMsg $ ShowModal "Shielding funds..."
  let dbPath = c_dbPath config
  let zHost = c_zebraHost config
  let zPort = c_zebraPort config
  pool <- runNoLoggingT $ initPool dbPath
  bl <- getChainTip zHost zPort
  res <- runNoLoggingT $ shieldTransparentNotes pool zHost zPort znet accId bl
  ops <-
    mapM
      (\case
         Left e -> return $ T.pack $ show e
         Right x -> do
           thisOp <- getOperation pool x
           case thisOp of
             Nothing -> return ""
             Just o ->
               return $
               (U.toText . getUuid . operationUuid $ entityVal o) <>
               ": " <> (T.pack . show . operationStatus $ entityVal o))
      res
  sendMsg $ ShowMsg $ T.intercalate "\n" ops

deshieldTransaction ::
     Config
  -> ZcashNet
  -> ZcashAccountId
  -> Maybe (Entity WalletAddress)
  -> Scientific
  -> (AppEvent -> IO ())
  -> IO ()
deshieldTransaction config znet accId addR pnote sendMsg = do
  case addR of
    Nothing -> sendMsg $ ShowError "No address available"
    Just addr -> do
      sendMsg $ ShowModal "De-shielding funds..."
      let dbPath = c_dbPath config
      let zHost = c_zebraHost config
      let zPort = c_zebraPort config
      pool <- runNoLoggingT $ initPool dbPath
      bl <- getChainTip zHost zPort
      res <- runNoLoggingT $ deshieldNotes pool zHost zPort znet accId bl pnote
      case res of
        Left e -> sendMsg $ ShowError $ T.pack (show e)
        Right rawTx -> do
          sendMsg $ ShowModal "Transaction ready, sending to Zebra..."
          resp <-
            makeZebraCall
              zHost
              zPort
              "sendrawtransaction"
              [Data.Aeson.String $ toText rawTx]
          case resp of
            Left e1 -> sendMsg $ ShowError $ "Zebra error: " <> showt e1
            Right txId -> sendMsg $ ShowTxId txId

sendTransaction ::
     Config
  -> ZcashNet
  -> ZcashAccountId
  -> Int
  -> Scientific
  -> T.Text
  -> T.Text
  -> PrivacyPolicy
  -> (AppEvent -> IO ())
  -> IO ()
sendTransaction config znet accId bl amt ua memo policy sendMsg = do
  sendMsg $ ShowModal "Preparing transaction..."
  case parseAddress (E.encodeUtf8 ua) of
    Nothing -> sendMsg $ ShowError "Incorrect address"
    Just addr -> do
      let dbPath = c_dbPath config
      let zHost = c_zebraHost config
      let zPort = c_zebraPort config
      pool <- runNoLoggingT $ initPool dbPath
      res <-
        runNoLoggingT $
        prepareTxV2
          pool
          zHost
          zPort
          znet
          accId
          bl
          [ ProposedNote
              (ValidAddressAPI addr)
              amt
              (if memo == ""
                 then Nothing
                 else Just memo)
          ]
          policy
      case res of
        Left e -> sendMsg $ ShowError $ T.pack $ show e
        Right rawTx -> do
          sendMsg $ ShowModal "Transaction ready, sending to Zebra..."
          resp <-
            makeZebraCall
              zHost
              zPort
              "sendrawtransaction"
              [Data.Aeson.String $ toText rawTx]
          case resp of
            Left e1 -> sendMsg $ ShowError $ "Zebra error: " <> showt e1
            Right txId -> sendMsg $ ShowTxId txId

timeTicker :: (AppEvent -> IO ()) -> IO ()
timeTicker sendMsg = do
  sendMsg TickUp
  threadDelay $ 1000 * 1000
  timeTicker sendMsg

txtWrapN :: T.Text -> Int -> T.Text
txtWrapN t n = wrapText (WrapSettings False True NoFill FillAfterFirst) n t

txtWrap :: T.Text -> T.Text
txtWrap = wrapText (WrapSettings False True NoFill FillAfterFirst) 32

runZenithGUI :: Config -> IO ()
runZenithGUI config = do
  homeDir <- try getHomeDirectory :: IO (Either IOError FilePath)
  case homeDir of
    Left e -> print e
    Right hD -> do
      let host = c_zebraHost config
      let port = c_zebraPort config
      let dbFilePath = c_dbPath config
      pool <- runNoLoggingT $ initPool dbFilePath
      w <- try $ checkZebra host port :: IO (Either IOError ZebraGetInfo)
      case w of
        Right zebra -> do
          bc <-
            try $ checkBlockChain host port :: IO
              (Either IOError ZebraGetBlockChainInfo)
          case bc of
            Left e1 -> throwIO e1
            Right chainInfo -> do
              x <- runNoLoggingT $ initDb dbFilePath
              _ <- upgradeQrTable pool
              _ <- runNoLoggingT $ upgradeAccountTable pool
              case x of
                Left e2 -> throwIO $ userError e2
                Right x' -> do
                  when x' $ rescanZebra host port dbFilePath
                  generateQRCodes config
                  walList <- getWallets pool $ zgb_net chainInfo
                  accList <-
                    if not (null walList)
                      then runNoLoggingT $
                           getAccounts pool $ entityKey $ head walList
                      else return []
                  addrList <-
                    if not (null accList)
                      then runNoLoggingT $
                           getAddresses pool $ entityKey $ head accList
                      else return []
                  txList <-
                    if not (null addrList)
                      then getUserTx pool $ entityKey $ head addrList
                      else return []
                  qr <-
                    if not (null addrList)
                      then getQrCode pool OrchardPool $
                           entityKey $ head addrList
                      else return Nothing
                  bal <-
                    if not (null accList)
                      then getBalance pool $ entityKey $ head accList
                      else return 0
                  unconfBal <-
                    if not (null accList)
                      then getUnconfirmedBalance pool $ entityKey $ head accList
                      else return 0
                  abList <- getAdrBook pool (zgb_net chainInfo)
                  shieldBal <-
                    if not (null accList)
                      then getShieldedBalance pool $ entityKey $ head accList
                      else return 0
                  transBal <-
                    if not (null accList)
                      then getTransparentBalance pool $ entityKey $ head accList
                      else return 0
                  let model =
                        AppModel
                          config
                          (zgb_net chainInfo)
                          walList
                          0
                          accList
                          0
                          addrList
                          0
                          txList
                          0
                          Nothing
                          True
                          bal
                          (if unconfBal == 0
                             then Nothing
                             else Just unconfBal)
                          OrchardPool
                          qr
                          False
                          False
                          False
                          False
                          ""
                          Nothing
                          ""
                          ""
                          (SaveAddress $
                           if not (null accList)
                             then Just (head accList)
                             else Nothing)
                          False
                          False
                          Nothing
                          Nothing
                          0
                          1.0
                          False
                          ""
                          0.0
                          ""
                          False
                          False
                          Nothing
                          hD
                          False
                          False
                          ""
                          ""
                          False
                          False
                          abList
                          Nothing
                          False
                          False
                          Full
                          False
                          False
                          transBal
                          False
                          shieldBal
                          False
                          False
                          0.0
                          0.0
                          False
                          False
                          ""
                          ""
                          False
                          False
                          False
                          ""
                          ""
                          Nothing
                          False
                          ""
                          ""
                          0
                          False
                          ""
                          ""
                          0
                          False
                  startApp model handleEvent buildUI (params hD)
        Left _e -> print "Zebra not available"
  where
    params hd =
      [ appWindowTitle "Zenith - Zcash Full Node Wallet - 0.9.0.0-beta"
      , appWindowState $ MainWindowNormal (1000, 700)
      , appTheme zenithTheme
      , appFontDef
          "Regular"
          (T.pack $
           hd </>
           "Zenith/assets/Atkinson-Hyperlegible-Regular-102.ttf" --"./assets/DejaVuSansMono.ttf" 
           )
      , appFontDef
          "Bold"
          (T.pack $ hd </> "Zenith/assets/Atkinson-Hyperlegible-Bold-102.ttf")
      , appFontDef
          "Italic"
          (T.pack $ hd </> "Zenith/assets/Atkinson-Hyperlegible-Italic-102.ttf")
      , appFontDef "Remix" (T.pack $ hd </> "Zenith/assets/remixicon.ttf")
      , appDisableAutoScale True
      , appScaleFactor 2.0
      , appInitEvent AppInit
      ]
