{-# LANGUAGE CPP #-}
{-# LANGUAGE MultiWayIf #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}

module Test.Sandwich.Formatters.TerminalUI.Draw.TopBox (
  topBox
  ) where

import Brick
import qualified Brick.Widgets.List as L
import Control.Monad.Logger
import qualified Data.List as L
import Data.Maybe
import Lens.Micro
import Test.Sandwich.Formatters.TerminalUI.AttrMap
import Test.Sandwich.Formatters.TerminalUI.Keys
import Test.Sandwich.Formatters.TerminalUI.Types
import Test.Sandwich.RunTree
import Test.Sandwich.Types.RunTree
import Test.Sandwich.Types.Spec


topBox app = hBox [columnPadding settingsColumn
                  , columnPadding actionsColumn
                  , columnPadding otherActionsColumn]
  where
    settingsColumn = keybindingBox [keyIndicator (L.intersperse '/' [unKChar nextKey, unKChar previousKey, '↑', '↓']) "Navigate"
                                   , keyIndicatorHasSelected app (showKeys toggleKeys) "Open/close node"
                                   , keyIndicatorHasSelectedOpen app scrollNodeLabel "Scroll node"
                                   , keyIndicatorHasSelected app (unKChar closeNodeKey : '/' : [unKChar openNodeKey]) "Fold/unfold node"
                                   , keyIndicator "Meta + [0-9]" "Unfold top # nodes"
                                   , keyIndicator (unKChar nextFailureKey : '/' : [unKChar previousFailureKey]) "Next/previous failure"
                                   ]

    actionsColumn = keybindingBox [hBox [str "["
                                         , highlightKeyIfPredicate selectedTestRunning app (str $ showKey cancelSelectedKey)
                                         , str "/"
                                         , highlightKeyIfPredicate someTestRunning app (str $ showKey cancelAllKey)
                                         , str "] "
                                         , withAttr hotkeyMessageAttr $ str "Cancel "
                                         , highlightMessageIfPredicate selectedTestRunning app (str "selected")
                                         , str "/"
                                         , highlightMessageIfPredicate someTestRunning app (str "all")
                                         ]
                                  , hBox [str "["
                                         , highlightKeyIfPredicate selectedTestDone app (str $ showKey runSelectedKey)
                                         , str "/"
                                         , highlightKeyIfPredicate noTestsRunning app (str $ showKey runAllKey)
                                         , str "] "
                                         , withAttr hotkeyMessageAttr $ str "Run "
                                         , highlightMessageIfPredicate selectedTestDone app (str "selected")
                                         , str "/"
                                         , highlightMessageIfPredicate noTestsRunning app (str "all")
                                         ]
                                  , hBox [str "["
                                         , highlightKeyIfPredicate selectedTestDone app (str $ showKey clearSelectedKey)
                                         , str "/"
                                         , highlightKeyIfPredicate allTestsDone app (str $ showKey clearAllKey)
                                         , str "] "
                                         , withAttr hotkeyMessageAttr $ str "Clear "
                                         , highlightMessageIfPredicate selectedTestDone app (str "selected")
                                         , str "/"
                                         , highlightMessageIfPredicate allTestsDone app (str "all")
                                         ]
                                  , hBox [str "["
                                         , highlightKeyIfPredicate someTestSelected app (str $ showKey openSelectedFolderInFileExplorer)
                                         , str "/"
                                         , highlightKeyIfPredicate (const True) app (str $ showKey openTestRootKey)
                                         , str "] "
                                         , withAttr hotkeyMessageAttr $ str "Open "
                                         , highlightMessageIfPredicate someTestSelected app (str "selected")
                                         , str "/"
                                         , highlightMessageIfPredicate (const True) app (str "root")
                                         , withAttr hotkeyMessageAttr $ str " folder"
                                         ]
                                  , hBox [str "["
                                         , highlightKeyIfPredicate someTestSelected app (str $ showKey openTestInEditorKey)
                                         , str "/"
                                         , highlightKeyIfPredicate someTestSelected app (str $ showKey openLogsInEditorKey)
                                         , str "/"
                                         , highlightKeyIfPredicate someTestSelected app (str $ showKey openFailureInEditorKey)
                                         , str "] "
                                         , withAttr hotkeyMessageAttr $ str "Edit "
                                         , highlightMessageIfPredicate someTestSelected app (str "test")
                                         , str "/"
                                         , highlightMessageIfPredicate someTestSelected app (str "logs")
                                         , str "/"
                                         , highlightMessageIfPredicate selectedTestHasCallStack app (str "failure")
                                         ]
                                  ]

    otherActionsColumn = keybindingBox [keyIndicator' (showKey cycleVisibilityThresholdKey) (visibilityThresholdWidget app)
                                       , hBox [str "["
                                              , str $ showKey toggleShowRunTimesKey
                                              , str "/"
                                              , str $ showKey toggleFileLocationsKey
                                              , str "/"
                                              , str $ showKey toggleVisibilityThresholdsKey
                                              , str "/"
                                              , str $ showKey toggleShowLogSizesKey
                                              , str "] "
                                              , highlightMessageIfPredicate (^. appShowRunTimes) app (str "Times")
                                              , str "/"
                                              , highlightMessageIfPredicate (^. appShowFileLocations) app (str "locations")
                                              , str "/"
                                              , highlightMessageIfPredicate (^. appShowVisibilityThresholds) app (str "thresholds")
                                              , str "/"
                                              , highlightMessageIfPredicate (^. appShowLogSizes) app (str "log sizes")
                                         ]
                                       , hBox [str "["
                                              , highlightIfLogLevel app LevelDebug [unKChar debugKey]
                                              , str "/"
                                              , highlightIfLogLevel app LevelInfo [unKChar infoKey]
                                              , str "/"
                                              , highlightIfLogLevel app LevelWarn [unKChar warnKey]
                                              , str "/"
                                              , highlightIfLogLevel app LevelError [unKChar errorKey]
                                              , str "] "
                                              , str "Log level"]

                                       , keyIndicator "q" "Exit"]

scrollNodeLabel :: String
#ifdef darwin_HOST_OS
scrollNodeLabel = "Control-PgUp/PgDown"
#else
scrollNodeLabel = "Control-v/Meta-v"
#endif

visibilityThresholdWidget app = hBox $
  [withAttr hotkeyMessageAttr $ str "Visibility threshold ("]
  <> L.intersperse (str ", ") [withAttr (if x == app ^. appVisibilityThreshold then visibilityThresholdSelectedAttr else visibilityThresholdNotSelectedAttr) $ str $ show x | x <- (app ^. appVisibilityThresholdSteps)]
  <> [(str ")")]

columnPadding = padLeft (Pad 1) . padRight (Pad 3) -- . padTop (Pad 1)

keybindingBox = vBox

highlightIfLogLevel app desiredLevel thing =
  if | app ^. appLogLevel == Just desiredLevel -> withAttr visibilityThresholdSelectedAttr $ str thing
     | otherwise -> withAttr hotkeyAttr $ str thing

highlightKeyIfPredicate p app x = case p app of
  True -> withAttr hotkeyAttr x
  False -> withAttr disabledHotkeyAttr x

highlightMessageIfPredicate p app x = case p app of
  True -> withAttr hotkeyMessageAttr x
  False -> withAttr disabledHotkeyMessageAttr x

keyIndicator key msg = keyIndicator' key (withAttr hotkeyMessageAttr $ str msg)

keyIndicator' key label = hBox [str "[", withAttr hotkeyAttr $ str key, str "] ", label]

keyIndicatorHasSelected app = keyIndicatorContextual app someTestSelected

keyIndicatorHasSelectedOpen app = keyIndicatorContextual app selectedTestToggled

keyIndicatorContextual app p key msg = case p app of
  True -> hBox [str "[", withAttr hotkeyAttr $ str key, str "] ", withAttr hotkeyMessageAttr $ str msg]
  False -> hBox [str "[", withAttr disabledHotkeyAttr $ str key, str "] ", withAttr disabledHotkeyMessageAttr $ str msg]


-- * Predicates

selectedTestRunning s = case L.listSelectedElement (s ^. appMainList) of
  Nothing -> False
  Just (_, MainListElem {..}) -> isRunning status

selectedTestDone s = case L.listSelectedElement (s ^. appMainList) of
  Nothing -> False
  Just (_, MainListElem {..}) -> isDone status

selectedTestHasCallStack s = case L.listSelectedElement (s ^. appMainList) of
  Nothing -> False
  Just (_, MainListElem {..}) -> case status of
    (Done _ _ _ _ (Failure failureReason)) -> isJust $ failureCallStack failureReason
    _ -> False

selectedTestToggled s = case L.listSelectedElement (s ^. appMainList) of
  Nothing -> False
  Just (_, MainListElem {..}) -> toggled

noTestsRunning s = all (not . isRunning . runTreeStatus . runNodeCommon) (s ^. appRunTree)

someTestRunning s = any (isRunning . runTreeStatus . runNodeCommon) (s ^. appRunTree)

allTestsDone s = all (isDone . runTreeStatus . runNodeCommon) (s ^. appRunTree)

someTestSelected s = isJust $ L.listSelectedElement (s ^. appMainList)
