{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}

-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Sum types that represent menu options,
-- modal dialogs, and buttons.
module Swarm.TUI.Model.Menu where

import Brick.Widgets.Dialog (Dialog)
import Brick.Widgets.List qualified as BL
import Control.Lens hiding (from, (<.>))
import Data.List.Extra (enumerate)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Text (Text)
import Data.Vector qualified as V
import Swarm.Game.Achievement.Definitions
import Swarm.Game.Entity as E
import Swarm.Game.Ingredients
import Swarm.Game.Scenario.Status (ScenarioPath (..))
import Swarm.Game.ScenarioInfo (
  ScenarioCollection,
  ScenarioItem (..),
  ScenarioWith,
  scenarioCollectionToList,
 )
import Swarm.Game.World.Gen (Seed)
import Swarm.TUI.Model.Name

------------------------------------------------------------
-- Menus and dialogs
------------------------------------------------------------

data ScenarioOutcome = WinModal | LoseModal
  deriving (Int -> ScenarioOutcome -> ShowS
[ScenarioOutcome] -> ShowS
ScenarioOutcome -> String
(Int -> ScenarioOutcome -> ShowS)
-> (ScenarioOutcome -> String)
-> ([ScenarioOutcome] -> ShowS)
-> Show ScenarioOutcome
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ScenarioOutcome -> ShowS
showsPrec :: Int -> ScenarioOutcome -> ShowS
$cshow :: ScenarioOutcome -> String
show :: ScenarioOutcome -> String
$cshowList :: [ScenarioOutcome] -> ShowS
showList :: [ScenarioOutcome] -> ShowS
Show, ScenarioOutcome -> ScenarioOutcome -> Bool
(ScenarioOutcome -> ScenarioOutcome -> Bool)
-> (ScenarioOutcome -> ScenarioOutcome -> Bool)
-> Eq ScenarioOutcome
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ScenarioOutcome -> ScenarioOutcome -> Bool
== :: ScenarioOutcome -> ScenarioOutcome -> Bool
$c/= :: ScenarioOutcome -> ScenarioOutcome -> Bool
/= :: ScenarioOutcome -> ScenarioOutcome -> Bool
Eq)

data MidScenarioModalType
  = HelpModal
  | RecipesModal
  | CommandsModal
  | MessagesModal
  | StructuresModal
  | EntityPaletteModal
  | TerrainPaletteModal
  | RobotsModal
  | DescriptionModal Entity
  | GoalModal
  deriving (Int -> MidScenarioModalType -> ShowS
[MidScenarioModalType] -> ShowS
MidScenarioModalType -> String
(Int -> MidScenarioModalType -> ShowS)
-> (MidScenarioModalType -> String)
-> ([MidScenarioModalType] -> ShowS)
-> Show MidScenarioModalType
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> MidScenarioModalType -> ShowS
showsPrec :: Int -> MidScenarioModalType -> ShowS
$cshow :: MidScenarioModalType -> String
show :: MidScenarioModalType -> String
$cshowList :: [MidScenarioModalType] -> ShowS
showList :: [MidScenarioModalType] -> ShowS
Show, MidScenarioModalType -> MidScenarioModalType -> Bool
(MidScenarioModalType -> MidScenarioModalType -> Bool)
-> (MidScenarioModalType -> MidScenarioModalType -> Bool)
-> Eq MidScenarioModalType
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MidScenarioModalType -> MidScenarioModalType -> Bool
== :: MidScenarioModalType -> MidScenarioModalType -> Bool
$c/= :: MidScenarioModalType -> MidScenarioModalType -> Bool
/= :: MidScenarioModalType -> MidScenarioModalType -> Bool
Eq)

data EndScenarioModalType
  = ScenarioFinishModal ScenarioOutcome
  | QuitModal
  | KeepPlayingModal
  deriving (Int -> EndScenarioModalType -> ShowS
[EndScenarioModalType] -> ShowS
EndScenarioModalType -> String
(Int -> EndScenarioModalType -> ShowS)
-> (EndScenarioModalType -> String)
-> ([EndScenarioModalType] -> ShowS)
-> Show EndScenarioModalType
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> EndScenarioModalType -> ShowS
showsPrec :: Int -> EndScenarioModalType -> ShowS
$cshow :: EndScenarioModalType -> String
show :: EndScenarioModalType -> String
$cshowList :: [EndScenarioModalType] -> ShowS
showList :: [EndScenarioModalType] -> ShowS
Show, EndScenarioModalType -> EndScenarioModalType -> Bool
(EndScenarioModalType -> EndScenarioModalType -> Bool)
-> (EndScenarioModalType -> EndScenarioModalType -> Bool)
-> Eq EndScenarioModalType
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: EndScenarioModalType -> EndScenarioModalType -> Bool
== :: EndScenarioModalType -> EndScenarioModalType -> Bool
$c/= :: EndScenarioModalType -> EndScenarioModalType -> Bool
/= :: EndScenarioModalType -> EndScenarioModalType -> Bool
Eq)

data ModalType
  = MidScenarioModal MidScenarioModalType
  | EndScenarioModal EndScenarioModalType
  deriving (Int -> ModalType -> ShowS
[ModalType] -> ShowS
ModalType -> String
(Int -> ModalType -> ShowS)
-> (ModalType -> String)
-> ([ModalType] -> ShowS)
-> Show ModalType
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ModalType -> ShowS
showsPrec :: Int -> ModalType -> ShowS
$cshow :: ModalType -> String
show :: ModalType -> String
$cshowList :: [ModalType] -> ShowS
showList :: [ModalType] -> ShowS
Show, ModalType -> ModalType -> Bool
(ModalType -> ModalType -> Bool)
-> (ModalType -> ModalType -> Bool) -> Eq ModalType
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ModalType -> ModalType -> Bool
== :: ModalType -> ModalType -> Bool
$c/= :: ModalType -> ModalType -> Bool
/= :: ModalType -> ModalType -> Bool
Eq)

data ButtonAction
  = Cancel
  | KeepPlaying
  | StartOver Seed (ScenarioWith ScenarioPath)
  | QuitAction
  | Next (NonEmpty (ScenarioWith ScenarioPath))

data Modal = Modal
  { Modal -> ModalType
_modalType :: ModalType
  , Modal -> Dialog ButtonAction Name
_modalDialog :: Dialog ButtonAction Name
  }

makeLenses ''Modal

data MainMenuEntry
  = NewGame
  | Tutorial
  | Achievements
  | Messages
  | About
  | Quit
  deriving (MainMenuEntry -> MainMenuEntry -> Bool
(MainMenuEntry -> MainMenuEntry -> Bool)
-> (MainMenuEntry -> MainMenuEntry -> Bool) -> Eq MainMenuEntry
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MainMenuEntry -> MainMenuEntry -> Bool
== :: MainMenuEntry -> MainMenuEntry -> Bool
$c/= :: MainMenuEntry -> MainMenuEntry -> Bool
/= :: MainMenuEntry -> MainMenuEntry -> Bool
Eq, Eq MainMenuEntry
Eq MainMenuEntry =>
(MainMenuEntry -> MainMenuEntry -> Ordering)
-> (MainMenuEntry -> MainMenuEntry -> Bool)
-> (MainMenuEntry -> MainMenuEntry -> Bool)
-> (MainMenuEntry -> MainMenuEntry -> Bool)
-> (MainMenuEntry -> MainMenuEntry -> Bool)
-> (MainMenuEntry -> MainMenuEntry -> MainMenuEntry)
-> (MainMenuEntry -> MainMenuEntry -> MainMenuEntry)
-> Ord MainMenuEntry
MainMenuEntry -> MainMenuEntry -> Bool
MainMenuEntry -> MainMenuEntry -> Ordering
MainMenuEntry -> MainMenuEntry -> MainMenuEntry
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: MainMenuEntry -> MainMenuEntry -> Ordering
compare :: MainMenuEntry -> MainMenuEntry -> Ordering
$c< :: MainMenuEntry -> MainMenuEntry -> Bool
< :: MainMenuEntry -> MainMenuEntry -> Bool
$c<= :: MainMenuEntry -> MainMenuEntry -> Bool
<= :: MainMenuEntry -> MainMenuEntry -> Bool
$c> :: MainMenuEntry -> MainMenuEntry -> Bool
> :: MainMenuEntry -> MainMenuEntry -> Bool
$c>= :: MainMenuEntry -> MainMenuEntry -> Bool
>= :: MainMenuEntry -> MainMenuEntry -> Bool
$cmax :: MainMenuEntry -> MainMenuEntry -> MainMenuEntry
max :: MainMenuEntry -> MainMenuEntry -> MainMenuEntry
$cmin :: MainMenuEntry -> MainMenuEntry -> MainMenuEntry
min :: MainMenuEntry -> MainMenuEntry -> MainMenuEntry
Ord, Int -> MainMenuEntry -> ShowS
[MainMenuEntry] -> ShowS
MainMenuEntry -> String
(Int -> MainMenuEntry -> ShowS)
-> (MainMenuEntry -> String)
-> ([MainMenuEntry] -> ShowS)
-> Show MainMenuEntry
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> MainMenuEntry -> ShowS
showsPrec :: Int -> MainMenuEntry -> ShowS
$cshow :: MainMenuEntry -> String
show :: MainMenuEntry -> String
$cshowList :: [MainMenuEntry] -> ShowS
showList :: [MainMenuEntry] -> ShowS
Show, ReadPrec [MainMenuEntry]
ReadPrec MainMenuEntry
Int -> ReadS MainMenuEntry
ReadS [MainMenuEntry]
(Int -> ReadS MainMenuEntry)
-> ReadS [MainMenuEntry]
-> ReadPrec MainMenuEntry
-> ReadPrec [MainMenuEntry]
-> Read MainMenuEntry
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS MainMenuEntry
readsPrec :: Int -> ReadS MainMenuEntry
$creadList :: ReadS [MainMenuEntry]
readList :: ReadS [MainMenuEntry]
$creadPrec :: ReadPrec MainMenuEntry
readPrec :: ReadPrec MainMenuEntry
$creadListPrec :: ReadPrec [MainMenuEntry]
readListPrec :: ReadPrec [MainMenuEntry]
Read, MainMenuEntry
MainMenuEntry -> MainMenuEntry -> Bounded MainMenuEntry
forall a. a -> a -> Bounded a
$cminBound :: MainMenuEntry
minBound :: MainMenuEntry
$cmaxBound :: MainMenuEntry
maxBound :: MainMenuEntry
Bounded, Int -> MainMenuEntry
MainMenuEntry -> Int
MainMenuEntry -> [MainMenuEntry]
MainMenuEntry -> MainMenuEntry
MainMenuEntry -> MainMenuEntry -> [MainMenuEntry]
MainMenuEntry -> MainMenuEntry -> MainMenuEntry -> [MainMenuEntry]
(MainMenuEntry -> MainMenuEntry)
-> (MainMenuEntry -> MainMenuEntry)
-> (Int -> MainMenuEntry)
-> (MainMenuEntry -> Int)
-> (MainMenuEntry -> [MainMenuEntry])
-> (MainMenuEntry -> MainMenuEntry -> [MainMenuEntry])
-> (MainMenuEntry -> MainMenuEntry -> [MainMenuEntry])
-> (MainMenuEntry
    -> MainMenuEntry -> MainMenuEntry -> [MainMenuEntry])
-> Enum MainMenuEntry
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: MainMenuEntry -> MainMenuEntry
succ :: MainMenuEntry -> MainMenuEntry
$cpred :: MainMenuEntry -> MainMenuEntry
pred :: MainMenuEntry -> MainMenuEntry
$ctoEnum :: Int -> MainMenuEntry
toEnum :: Int -> MainMenuEntry
$cfromEnum :: MainMenuEntry -> Int
fromEnum :: MainMenuEntry -> Int
$cenumFrom :: MainMenuEntry -> [MainMenuEntry]
enumFrom :: MainMenuEntry -> [MainMenuEntry]
$cenumFromThen :: MainMenuEntry -> MainMenuEntry -> [MainMenuEntry]
enumFromThen :: MainMenuEntry -> MainMenuEntry -> [MainMenuEntry]
$cenumFromTo :: MainMenuEntry -> MainMenuEntry -> [MainMenuEntry]
enumFromTo :: MainMenuEntry -> MainMenuEntry -> [MainMenuEntry]
$cenumFromThenTo :: MainMenuEntry -> MainMenuEntry -> MainMenuEntry -> [MainMenuEntry]
enumFromThenTo :: MainMenuEntry -> MainMenuEntry -> MainMenuEntry -> [MainMenuEntry]
Enum)

data Menu
  = -- | We started playing directly from command line, no menu to show
    NoMenu
  | MainMenu (BL.List Name MainMenuEntry)
  | -- | Stack of scenario item lists. INVARIANT: the currently selected
    -- menu item is ALWAYS the same as the scenario currently being played.
    -- See https://github.com/swarm-game/swarm/issues/1064 and
    -- https://github.com/swarm-game/swarm/pull/1065.
    NewGameMenu (NonEmpty (BL.List Name (ScenarioItem ScenarioPath)))
  | AchievementsMenu (BL.List Name CategorizedAchievement)
  | MessagesMenu
  | AboutMenu

mainMenu :: MainMenuEntry -> BL.List Name MainMenuEntry
mainMenu :: MainMenuEntry -> List Name MainMenuEntry
mainMenu MainMenuEntry
e = Name -> Vector MainMenuEntry -> Int -> List Name MainMenuEntry
forall (t :: * -> *) n e.
Foldable t =>
n -> t e -> Int -> GenericList n t e
BL.list Name
MenuList ([MainMenuEntry] -> Vector MainMenuEntry
forall a. [a] -> Vector a
V.fromList [MainMenuEntry]
forall a. (Enum a, Bounded a) => [a]
enumerate) Int
1 List Name MainMenuEntry
-> (List Name MainMenuEntry -> List Name MainMenuEntry)
-> List Name MainMenuEntry
forall a b. a -> (a -> b) -> b
& MainMenuEntry -> List Name MainMenuEntry -> List Name MainMenuEntry
forall e (t :: * -> *) n.
(Eq e, Foldable t, Splittable t) =>
e -> GenericList n t e -> GenericList n t e
BL.listMoveToElement MainMenuEntry
e

makePrisms ''Menu

-- | Create a brick 'BL.List' of scenario items from a 'ScenarioCollection'.
mkScenarioList :: ScenarioCollection a -> BL.List Name (ScenarioItem a)
mkScenarioList :: forall a. ScenarioCollection a -> List Name (ScenarioItem a)
mkScenarioList =
  (Vector (ScenarioItem a) -> Int -> List Name (ScenarioItem a))
-> Int -> Vector (ScenarioItem a) -> List Name (ScenarioItem a)
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Name
-> Vector (ScenarioItem a) -> Int -> List Name (ScenarioItem a)
forall (t :: * -> *) n e.
Foldable t =>
n -> t e -> Int -> GenericList n t e
BL.list Name
ScenarioList) Int
1
    (Vector (ScenarioItem a) -> List Name (ScenarioItem a))
-> (ScenarioCollection a -> Vector (ScenarioItem a))
-> ScenarioCollection a
-> List Name (ScenarioItem a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ScenarioItem a] -> Vector (ScenarioItem a)
forall a. [a] -> Vector a
V.fromList
    ([ScenarioItem a] -> Vector (ScenarioItem a))
-> (ScenarioCollection a -> [ScenarioItem a])
-> ScenarioCollection a
-> Vector (ScenarioItem a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ScenarioCollection a -> [ScenarioItem a]
forall a. ScenarioCollection a -> [ScenarioItem a]
scenarioCollectionToList

------------------------------------------------------------
-- Inventory list entries
------------------------------------------------------------

-- | An entry in the inventory list displayed in the info panel.  We
--   can either have an entity with a count in the robot's inventory,
--   an entity equipped on the robot, or a labelled separator.  The
--   purpose of the separators is to show a clear distinction between
--   the robot's /inventory/ and its /equipped devices/.
data InventoryListEntry
  = Separator Text
  | InventoryEntry Count Entity
  | EquippedEntry Entity
  deriving (InventoryListEntry -> InventoryListEntry -> Bool
(InventoryListEntry -> InventoryListEntry -> Bool)
-> (InventoryListEntry -> InventoryListEntry -> Bool)
-> Eq InventoryListEntry
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: InventoryListEntry -> InventoryListEntry -> Bool
== :: InventoryListEntry -> InventoryListEntry -> Bool
$c/= :: InventoryListEntry -> InventoryListEntry -> Bool
/= :: InventoryListEntry -> InventoryListEntry -> Bool
Eq)

makePrisms ''InventoryListEntry