module Dodge.Debug.Terminal
    where
import MaybeHelp
import ListHelp (safeHead)
import Dodge.Inventory.Add
import Dodge.Creature
import Dodge.Creature.Damage
--import Dodge.Creature.State
--import Dodge.Creature.YourControl
import Dodge.Data
import Dodge.Menu.PushPop
import Dodge.Item
import Control.Monad
--import Dodge.Base

import Control.Lens
import Text.Read (readMaybe)
import Data.List --(isPrefixOf, isInfixOf, intercalate)
import Data.Maybe
import LensHelp
import qualified Data.Text as T
import qualified IntMapHelp as IM
--import qualified Debug.Trace
--import Graphics.Rendering.OpenGL.GL.Shaders (validateProgram)
--import Dodge.Data (Universe(Universe))

applyTerminalString :: [String] -> Universe -> Universe
applyTerminalString ss = case ss of
    [] -> id
    [s] -> applyTerminalCommand s
    (s:ss') -> applyTerminalCommandArguments s ss'

applyTerminalCommand :: String -> Universe -> Universe
applyTerminalCommand s = case s of
    "NOCLIP" -> config . debug_booleans . at Noclip %~ toggleJust
    "LOADME" -> (uvWorld . creatures . ix 0 . crInv .~ stackedInventory)
        . (uvWorld . creatures . ix 0 . crInvCapacity .~ 50)
    "LM"    -> applyTerminalCommand "LOADME"
    "LT"    -> applyTerminalCommand "LOADTEST"
    ['L',x]     -> (uvWorld . creatures . ix 0 . crInv .~ IM.fromList (zip [0..] $ inventoryX x))
        . (uvWorld . creatures . ix 0 . crInvCapacity .~ 50)
    "GODON"     -> uvWorld . creatures . ix 0 . crApplyDamage .~ applyNoDamage
    "GODOFF"    -> uvWorld . creatures . ix 0 . crApplyDamage .~ defaultApplyDamage
    "LOADTEST"  -> (uvWorld . creatures . ix 0 . crInv .~ testInventory)
        . (uvWorld . creatures . ix 0 . crInvCapacity .~ 50)
    _ -> id
--applyTerminalString' ('s': 'e': 't': '_': 'h': 'p': ' ': hp)
--    | isNothing (readMaybe hp :: Maybe Int) = id
--    | otherwise = uvWorld . creatures . ix 0 . crHP .~ read hp
--applyTerminalString' ('s': 'e': 't': '_': 'i': 'n': 'v': 'c': 'a': 'p': ' ': n)
--    | isNothing (readMaybe n :: Maybe Int) = id
--    | otherwise = uvWorld . creatures . ix 0 . crInvCapacity .~ read n
--applyTerminalString' ('s': 'e': 't': ' ': var)
--    | var /= [] = applySetTerminalString var
--    | otherwise = id
--applyTerminalString' ('s': 'p': 'a': 'w': 'n': ' ': var)
--    | var /= [] = id -- work out how to spawn stuff
--    | otherwise = id
--applyTerminalString' _   = id

applyTerminalCommandArguments :: String -> [String] -> Universe -> Universe
applyTerminalCommandArguments command args u = case command of
    "ITEM" -> fromMaybe u $ do
        ibt <- safeHead args >>= readMaybe 
        return $ u & uvWorld %~ (snd . createPutItem (itemFromBase ibt))
    _ -> u

showTerminalError :: String -> String -> Universe -> Universe
showTerminalError cmd s = menuLayers .:~ InputScreen (T.pack cmd) s

applySetTerminalString :: String -> Universe -> Universe
applySetTerminalString [] = id
applySetTerminalString var = case key' of
    "" -> showTerminalError ("set "++var) ("Unable to read as argument as float: " ++ val)
    "hp" -> uvWorld . creatures . ix 0 . crHP .~ round (fromJust val')
    "invcap" -> uvWorld . creatures . ix 0 . crInvCapacity .~ round (fromJust val')
    "mass" -> uvWorld . creatures . ix 0 . crMass .~ fromJust val'
    "mvspeed" -> uvWorld . creatures . ix 0 . crMvType .mvSpeed .~ fromJust val'
    _ -> showTerminalError ("set "++var) ("Invalid set command: " ++ key) -- never reached?
    where
        (key, val) = getSplitString var
        val' = readMaybe val :: Maybe Float
        key' = if isNothing val' then "" else key

autoCompleteTerminal :: String -> String -> Universe -> IO (Maybe Universe)
autoCompleteTerminal s _ = return . 
    (popScreen' >=> pushScreen' (InputScreen (T.pack input_str) valid_commands))
  where
    (key, val) = getSplitString $ tail s
    command_options = case val of
        "" -> filter (isInfixOf key) (validTerminalCommands "")
        _ -> filter (isInfixOf val) (validTerminalCommands key)
    -- basic autocomplete if single option available (or as far as possible)
    input_str = case (key, val) of
        (_, "") -> if length command_options == 1 
            then ">" ++ head command_options ++ " " 
            else ">" ++ longestCommonPrefix command_options
        _ -> if length command_options == 1 
            then ">" ++ key ++ " " ++ head command_options ++ " " 
            else if null command_options 
                then s 
                else ">" ++ key ++ " " ++ longestCommonPrefix command_options
    command_options' = if not (null command_options) && head command_options == key 
        then validTerminalCommands key 
        else command_options

    --val' = Debug.Trace.trace key tail val
    valid_commands = "Options: " ++ intercalate ", " command_options'

getSplitString :: [Char] -> ([Char], [Char])
getSplitString str = case break (==' ') str of
    (a, _:b) -> (a, b)
    (a, _) -> (a, "")

isValidCommand :: String -> String -> Bool
isValidCommand arg1 arg2 = arg2 `elem` validTerminalCommands arg1

validTerminalCommands :: String -> [String]
validTerminalCommands "set" = ["hp", "invcap", "invsel", "mass", "mvspeed"]
validTerminalCommands "god" = ["on", "off"]
validTerminalCommands _ = ["set", "spawn", "god"]

loadme :: a
loadme = undefined

longestCommonPrefix :: Eq a => [[a]] -> [a]
longestCommonPrefix [] = []
longestCommonPrefix lists = foldr1 commonPrefix lists

commonPrefix :: Eq a => [a] -> [a] -> [a]
commonPrefix (x:xs) (y:ys)
    | x == y    = x : commonPrefix xs ys
commonPrefix _ _ = []
