module Hob.Ui.CommandEntry (newCommandEntry, newCommandEntryDetached) where

import           Control.Monad.Reader
import           Data.IORef
import           Data.Text                            (unpack)
import           Graphics.UI.Gtk
import qualified Graphics.UI.Gtk.General.StyleContext as GtkSc

import Hob.Context
import Hob.Control

type PreviewResetState = (PreviewCommandHandler -> App(), App())

commandPreviewResetState :: App PreviewResetState
commandPreviewResetState = do
    state <- liftIO $ newIORef Nothing
    return (
        liftIO . writeIORef state . Just,
        do
            resetCommand <- liftIO $ readIORef state
            maybeDo previewReset resetCommand
            liftIO $ writeIORef state Nothing
        )

newCommandEntry :: Entry -> App ()
newCommandEntry cmdEntry = do
    ctx <- ask
    (onChange, onReturn) <- newCommandEntryDetached cmdEntry
    let liftedOn a b c = liftIO $ on a b c
    _ <- cmdEntry `liftedOn` editableChanged $ deferredRunner ctx onChange
    _ <- cmdEntry `liftedOn` keyPressEvent $ do
        modifier <- eventModifier
        key <- eventKeyName
        case (modifier, unpack key) of
            ([], "Return") -> liftIO $ deferredRunner ctx onReturn >> return True
            _ -> return False
    return ()

newCommandEntryDetached :: Entry -> App (App(), App())
newCommandEntryDetached cmdEntry = do
    previewResetState <- commandPreviewResetState
    return (onChanged previewResetState, onReturn previewResetState)
    where
        onChanged =
            previewCmd cmdEntry
        onReturn previewResetState = do
            runCmd cmdEntry previewResetState
            liftIO $ entrySetText cmdEntry ""


previewCmd :: Entry -> PreviewResetState -> App ()
previewCmd cmdEntry (setLastPreviewCmd, dispatchLastPreviewReset) = do
    dispatchLastPreviewReset
    text <- liftIO $ entryGetText cmdEntry
    if text == "" then
        setOkStatus cmdEntry
    else do
        cmdMatcher <- getActiveCommands
        let command = matchCommand cmdMatcher text
        maybe notifyFailure handleCommand command
    where
        notifyFailure = setErrorStatus cmdEntry
        handleCommand command = do
            setOkStatus cmdEntry
            maybeDo invokePreview $ commandPreview command
        invokePreview prev = do
            previewExecute prev
            setLastPreviewCmd prev

runCmd :: Entry -> PreviewResetState -> App ()
runCmd cmdEntry (_, dispatchLastPreviewReset) = do
    dispatchLastPreviewReset
    text <- liftIO $ entryGetText cmdEntry
    cmdMatcher <- getActiveCommands
    let command = matchCommand cmdMatcher text
    maybeDo commandExecute command

setErrorStatus :: Entry -> App ()
setErrorStatus cmdEntry = do
    cmdEntryStyleContext <- liftIO $ widgetGetStyleContext cmdEntry
    liftIO $ GtkSc.styleContextAddClass cmdEntryStyleContext "error"

setOkStatus :: Entry -> App ()
setOkStatus cmdEntry = do
    cmdEntryStyleContext <- liftIO $ widgetGetStyleContext cmdEntry
    liftIO $ GtkSc.styleContextRemoveClass cmdEntryStyleContext "error"