{-# LANGUAGE TemplateHaskell #-}
module States (module States, GenIO) where

import Brick (Widget, EventM, Next)
import Brick.Forms (Form)
import Brick.Widgets.FileBrowser
import Brick.Widgets.List (List)
import Data.Char (isDigit)
import Data.Map.Strict (Map)
import Lens.Micro.Platform
import System.Random.MWC (GenIO)
import Stack hiding (head)
import Types
import qualified Data.List.NonEmpty as NE
import qualified Data.Map.Strict as M
import qualified Graphics.Vty as V

data Name = HintsField
          | ControlsField
          | EscapeCodeField
          | MaxRecentsField
          | Ordinary
  deriving (Eq, Ord, Show)
type Event = ()

data Mode  = MainMenu
           | Settings
           | Info
           | CardSelector
           | FileBrowser
           | Cards
  deriving (Show, Eq, Ord)

data State = MainMenuState     MMS
           | SettingsState     SS
           | InfoState         IS
           | CardSelectorState CSS
           | FileBrowserState  FBS
           | CardsState        CS

data Chunk = Chunk Int Int

instance Show Chunk where
  show (Chunk i n) = show i <> "/" <> show n

instance Read Chunk where
  readsPrec _ input =
    let (i', rest1) = span isDigit input
        i = read i' :: Int
        (c:rest2) = rest1
        (n', rest3) = span isDigit rest2
        n = read n' :: Int
    in [(Chunk i n, rest3) | c `elem` ['/', ' '] && n >= i && i >= 1]

data GlobalState = GlobalState
  { _mwc        :: GenIO
  , _doShuffle  :: Bool
  , _subset     :: Maybe Int
  , _chunk      :: Chunk
  , _stack      :: Stack Mode
  , _states     :: Map Mode State
  , _doReview   :: Bool
  }

data CardState =
    DefinitionState
  { _flipped        :: Bool }
  | MultipleChoiceState
  { _highlighted    :: Int
  , _number         :: Int
  , _tried          :: Map Int Bool      -- indices of tried choices
  }
  | MultipleAnswerState
  { _highlighted    :: Int
  , _selected       :: Map Int Bool
  , _number         :: Int
  , _entered        :: Bool
  }
  | OpenQuestionState
  { _gapInput       :: Map Int String
  , _highlighted    :: Int
  , _number         :: Int
  , _entered        :: Bool
  , _correctGaps    :: Map Int Bool
  , _failed         :: Bool
  }
  | ReorderState
  { _highlighted    :: Int
  , _grabbed        :: Bool
  , _order          :: Map Int (Int, String)
  , _entered        :: Bool
  , _number         :: Int
  }

defaultCardState :: Card -> CardState
defaultCardState Definition{} = DefinitionState { _flipped = False }
defaultCardState (MultipleChoice _ _ ics) = MultipleChoiceState
  { _highlighted = 0
  , _number = length ics + 1
  , _tried = M.fromList [(i, False) | i <- [0..length ics]] }
defaultCardState (OpenQuestion _ perforated) = OpenQuestionState
  { _gapInput = M.empty
  , _highlighted = 0
  , _number = nGapsInPerforated perforated
  , _entered = False
  , _correctGaps = M.fromList [(i, False) | i <- [0..nGapsInPerforated perforated - 1]]
  , _failed = False }
defaultCardState (MultipleAnswer _ answers) = MultipleAnswerState
  { _highlighted = 0
  , _selected = M.fromList [(i, False) | i <- [0..NE.length answers-1]]
  , _entered = False
  , _number = NE.length answers }
defaultCardState (Reorder _ elements) = ReorderState
  { _highlighted = 0
  , _grabbed = False
  , _order = M.fromList (zip [0..] (NE.toList elements))
  , _entered = False
  , _number = NE.length elements }

data CS = CS
  { _cards          :: [Card]     -- list of flashcards
  , _index          :: Int        -- current card index
  , _nCards         :: Int        -- number of cards
  , _currentCard    :: Card
  , _cardState      :: CardState
  , _showHints      :: Bool
  , _showControls   :: Bool
  , _reviewMode     :: Bool
  , _correctCards   :: [Int]      -- list of indices of correct cards
  , _popup          :: Maybe (Popup CS)
  , _pathToFile     :: FilePath
  }

data Popup s = Popup
  { drawPopup        :: s -> Widget Name
  , handlePopupEvent :: GlobalState -> s -> V.Event -> EventM Name (Next GlobalState)
  , _popupState      :: PopupState
  }

data PopupState =
    CorrectPopup
      { _popupSelected :: Int }
  | FinalPopup
  | DeckMakerPopup
      { _popupSelected     :: Int
      , _makeDeckIncorrect :: Bool
      , _makeDeckCorrect   :: Bool }
  deriving Eq

newtype MMS = MMS
  { _l  :: List Name String }

type IS = ()

data Settings = FormState
  { _hints           :: Bool
  , _controls        :: Bool
  , _escapeCode      :: Bool
  , _maxRecents      :: Int }
  deriving (Read, Show)

type SS = Form Settings Event Name

data CSS = CSS
  { _list             :: List Name String
  , _exception        :: Maybe String
  , _recents          :: Stack FilePath
  , _maxRecentsToShow :: Int
  }

data FBS = FBS
  { _fb          :: FileBrowser Name
  , _exception'  :: Maybe String
  , _parsedCards :: [Card]
  , _filePath    :: Maybe FilePath
  , _showHidden  :: Bool
  }

makeLenses ''State
makeLenses ''MMS
makeLenses ''GlobalState
makeLenses ''CardState
makeLenses ''CS
makeLenses ''Settings
makeLenses ''CSS
makeLenses ''FBS
makeLenses ''Popup
makeLenses ''PopupState