-- | Monadic operations on game messages.
module Game.LambdaHack.Client.UI.MsgM
  ( msgAddDuplicate, msgAddDistinct, msgAdd, msgLnAdd
  , promptMainKeys, recordHistory, tutorialHintMsgAdd
  ) where

import Prelude ()

import Game.LambdaHack.Core.Prelude

import           Game.LambdaHack.Client.MonadClient
import           Game.LambdaHack.Client.State
import qualified Game.LambdaHack.Client.UI.HumanCmd as HumanCmd
import qualified Game.LambdaHack.Client.UI.Key as K
import           Game.LambdaHack.Client.UI.MonadClientUI
import           Game.LambdaHack.Client.UI.Msg
import           Game.LambdaHack.Client.UI.SessionUI
import           Game.LambdaHack.Client.UI.TutorialHints
  (TutorialHints, renderTutorialHints)
import           Game.LambdaHack.Client.UI.UIOptions
import           Game.LambdaHack.Common.ActorState
import           Game.LambdaHack.Common.MonadStateRead
import           Game.LambdaHack.Common.State
import           Game.LambdaHack.Definition.Defs

sniffMessages :: Bool
sniffMessages = False

-- | Add a shared message to the current report. Say if it was a duplicate.
msgAddDuplicate :: (MonadClientUI m, MsgShared a) => a -> Text -> m Bool
msgAddDuplicate msgClass t = do
  sUIOptions <- getsSession sUIOptions
  time <- getsState stime
  history <- getsSession shistory
  curTutorial <- getsSession scurTutorial
  overrideTut <- getsSession soverrideTut
  usedHints <- getsSession susedHints
  lid <- getArenaUI
  condInMelee <- condInMeleeM lid
  smuteMessages <- getsSession smuteMessages
  let displayHints = fromMaybe curTutorial overrideTut
      msg = toMsgShared (uMessageColors sUIOptions) msgClass t
      (nusedHints, nhistory, duplicate) =
        addToReport usedHints displayHints condInMelee history msg time
  unless smuteMessages $ do
    modifySession $ \sess -> sess {shistory = nhistory, susedHints = nusedHints}
    when sniffMessages $ clientPrintUI t
  return duplicate

-- | Add a message comprising of two different texts, one to show, the other
-- to save to messages log, to the current report.
msgAddDistinct :: MonadClientUI m => MsgClassDistinct -> (Text, Text) -> m ()
msgAddDistinct msgClass (t1, t2) = do
  sUIOptions <- getsSession sUIOptions
  time <- getsState stime
  history <- getsSession shistory
  curTutorial <- getsSession scurTutorial
  overrideTut <- getsSession soverrideTut
  usedHints <- getsSession susedHints
  lid <- getArenaUI
  condInMelee <- condInMeleeM lid
  smuteMessages <- getsSession smuteMessages
  let displayHints = fromMaybe curTutorial overrideTut
      msg = toMsgDistinct (uMessageColors sUIOptions) msgClass t1 t2
      (nusedHints, nhistory, _) =
        addToReport usedHints displayHints condInMelee history msg time
  unless smuteMessages $ do
    modifySession $ \sess -> sess {shistory = nhistory, susedHints = nusedHints}
    when sniffMessages $ clientPrintUI t1

-- | Add a message to the current report.
msgAdd :: (MonadClientUI m, MsgShared a) => a -> Text -> m ()
msgAdd msgClass t = void $ msgAddDuplicate msgClass t

-- | Add a tutorial hint message to the current report.
tutorialHintMsgAdd :: MonadClientUI m => TutorialHints -> m ()
tutorialHintMsgAdd = msgAdd MsgTutorialHint . renderTutorialHints

-- | Add a message to the current report. End previously collected report,
-- if any, with newline.
msgLnAdd :: (MonadClientUI m, MsgShared a) => a -> Text -> m ()
msgLnAdd msgClass t = do
  smuteMessages <- getsSession smuteMessages
  unless smuteMessages $
    modifySession $ \sess -> sess {shistory = addEolToNewReport $ shistory sess}
  msgAdd msgClass t

-- | Add a prompt with basic keys description.
promptMainKeys :: MonadClientUI m => m ()
promptMainKeys = do
  side <- getsClient sside
  ours <- getsState $ fidActorNotProjGlobalAssocs side
  revCmd <- revCmdMap
  let kmHelp = revCmd HumanCmd.Hint
      kmViewStash = revCmd (HumanCmd.ChooseItemMenu (MStore CStash))
      kmItemStash = revCmd (HumanCmd.MoveItem [CGround, CEqp] CStash
                                              Nothing False)
      kmXhairPointerFloor = revCmd HumanCmd.XhairPointerFloor
  saimMode <- getsSession saimMode
  UIOptions{uVi, uLeftHand} <- getsSession sUIOptions
  xhair <- getsSession sxhair
  miniHintAiming <- getMiniHintAiming
  -- The silly "axwdqezc" name of keys is chosen to match "hjklyubn",
  -- which the usual way of writing them.
  let moveKeys | uVi && uLeftHand = "keypad or axwdqezc or hjklyubn"
               | uLeftHand = "keypad or axwdqezc"
               | uVi = "keypad or hjklyubn"
               | otherwise = "keypad"
      manyTeammates = length ours > 1
      -- @Tab@ here is not a button, which we would write consistently
      -- as @TAB@, just as in our internal in-game key naming, but a key name
      -- as written on the keyboard, hence most useful to a newbie.
      keepTab = if manyTeammates
                then "Switch to another teammate with Tab, while all others auto-melee foes, if adjacent, but normally don't chase them."
                else ""
      describePos = if describeIsNormal
                    then "Describe map position with MMB or RMB."
                    else ""
      viewEquip = if stashKeysAreNormal
                  then "View shared 'I'nventory stash and stash items into the 'i'nventory."
                  else ""
      moreHelp = "Press '" <> tshow kmHelp <> "' for more help."
      describeIsNormal = kmXhairPointerFloor == K.middleButtonReleaseKM
      stashKeysAreNormal = kmViewStash == K.mkChar 'I'
                           && kmItemStash == K.mkChar 'i'
      keys | isNothing saimMode =
        "Explore with" <+> moveKeys <+> "or mouse."
        <+> describePos
        <+> viewEquip
        <+> keepTab
        <+> moreHelp
           | otherwise =
        miniHintAiming
        <+> tgtKindVerb xhair
        <+> "with" <+> moveKeys <+> "keys or mouse."
        <+> keepTab
        <+> moreHelp
  void $ msgAdd MsgPromptGeneric keys

tgtKindVerb :: Maybe Target -> Text
tgtKindVerb mtgt = case mtgt of
  Just TEnemy{} -> "Aim at enemy"
  Just TNonEnemy{} -> "Aim at non-enemy"
  Just TPoint{} -> "Aim at position"
  Just TVector{} -> "Indicate a move vector"
  Nothing -> "Start aiming"

-- | Store new report in the history and archive old report.
recordHistory :: MonadClientUI m => m ()
recordHistory =
  modifySession $ \sess -> sess {shistory = archiveReport $ shistory sess}
