module Dodge.Menu
    ( scodeToChar
    , popScreen
    , pushScreen
    , pauseMenu
    , gameOverMenu
    )
    where
import Dodge.Data
import Dodge.Save
import Dodge.Config.Data
import Dodge.Config.Update
import SDL
import SDL.Internal.Numbered
import Dodge.Layout
import Dodge.Floor
import Dodge.Initialisation
import Preload.Update
import Dodge.Base.Window
import Dodge.SoundLogic
--import Picture

import Control.Lens
optionMenu :: ScreenLayer
optionMenu = OptionScreen
    { _scTitle = const "OPTIONS"
    , _scOptions = optionsOptions
    , _scDefaultEff = popScreen
    , _scOptionFlag = NormalOptions
    }
optionsOptions :: [MenuOption]
optionsOptions =
    [ Toggle ScancodeV (pushScreen soundMenu)    (const "VOLUME")
    , Toggle ScancodeG (pushScreen graphicsMenu) (const "GRAPHICS")
    , Toggle ScancodeP (pushScreen gameplayOptionsMenu) (const "GAMEPLAY")
    ]
--levelMenuOptions :: [MenuOption]
--levelMenuOptions =
--    [  InvisibleToggle ScancodeEscape (const Nothing)
--    ,  InvisibleToggle ScancodeO      (pushScreen optionMenu)
--    ,  InvisibleToggle ScancodeC      (pushScreen displayControls)
--    ]
gameplayOptionsMenu :: ScreenLayer
gameplayOptionsMenu  = OptionScreen
    { _scTitle = const "OPTIONS:GAMEPLAY"
    , _scOptions = gameplayMenuOptions
    , _scDefaultEff = popScreen . writeConfig
    , _scOptionFlag = NormalOptions
    }
gameplayMenuOptions :: [MenuOption]
gameplayMenuOptions =
    [ Toggle 
        ScancodeR 
        (Just . (config . rotate_to_wall %~ not))
        (\w -> "ROTATE TO WALL:" ++ show (_rotate_to_wall $ _config w))
    , Toggle 
        ScancodeS 
        (Just . (config . show_sound %~ not))
        (\w -> "SHOW VISUAL SOUNDS:" ++ show (_show_sound $ _config w))
    ]

soundMenu :: ScreenLayer
soundMenu = OptionScreen
    { _scTitle = const "OPTIONS:VOLUME"
    , _scOptions = soundMenuOptions
    , _scDefaultEff = popScreen . writeConfig
    , _scOptionFlag = NormalOptions
    }
soundMenuOptions :: [MenuOption]
soundMenuOptions =
    [ Toggle2 ScancodeY (master dec . sw)
        ScancodeU (master inc . sw) (\w -> "MASTER VOLUME:" ++ mavol w)
    , Toggle2 ScancodeH (soundEffs dec . sw)
        ScancodeJ (soundEffs inc . sw) (\w -> "EFFECTS VOLUME:" ++ snvol w)
    , Toggle2 ScancodeN (music dec . sw)
        ScancodeM (music inc . sw) (\w -> "MUSIC VOLUME:" ++ muvol w)
    ]
  where
    dec x = max 0 (x - 0.1)
    inc x = min 1 (x + 0.1)
    --sw w = w & sideEffects %~ (setVol (_config w) : )
    sw w = w & sideEffects %~ setVol' (_config w)
    master g    = Just . (config . volume_master %~ g)
    soundEffs g = Just . (config . volume_sound %~ g)
    music g     = Just . (config . volume_music %~ g)
    cfig w = _config w
    mavol w = f $ _volume_master $ cfig w
    snvol w = f $ _volume_sound  $ cfig w
    muvol w = f $ _volume_music  $ cfig w
    f x = show (round $ 10 * x :: Int)

pushScreen :: ScreenLayer -> World -> Maybe World
pushScreen ml w = Just $ w & menuLayers %~ (ml :)

popScreen :: World -> Maybe World
popScreen = Just . (menuLayers %~ tail)

writeConfig :: World -> World
writeConfig w = w & sideEffects %~ saveConfig (_config w)

graphicsMenu :: ScreenLayer
graphicsMenu = OptionScreen
    { _scTitle = const "OPTIONS:GRAPHICS"
    , _scOptions = graphicsMenuOptions
    , _scDefaultEff = popScreen . writeConfig
    , _scOptionFlag = NormalOptions
    }
graphicsMenuOptions :: [MenuOption]
graphicsMenuOptions =
    [ Toggle ScancodeW (Just . (config . wall_textured %~ not))  wtextstring
    , Toggle ScancodeS upf resostring
    , Toggle ScancodeD (Just . (config . cloud_shadows %~ not)) cshadstring
    ]
  where
    wtextstring w = "WALL TEXTURES:" ++ show (_wall_textured $ _config w)
    resostring w  = "RESOLUTION: 1/" ++ show (_resolution_factor $ _config w)
    upf w = Just $ updateFramebufferSize $ w & config . resolution_factor %~ cycleResolution
    cshadstring w = "CLOUD SHADOWS:" ++ show (_cloud_shadows $ _config w)

cycleResolution :: (Eq a, Num a, Num p) => a -> p
cycleResolution 1 = 2
cycleResolution 2 = 4
cycleResolution 4 = 1
cycleResolution _ = 1

gameOverMenu :: ScreenLayer
gameOverMenu = OptionScreen
    { _scTitle = const "GAME OVER"
    , _scOptions = pauseMenuOptions
    , _scDefaultEff = Just
    , _scOptionFlag = GameOverOptions
    }

pauseMenu :: ScreenLayer
pauseMenu = OptionScreen
    { _scTitle = const "PAUSED"
    , _scOptions = pauseMenuOptions
    , _scDefaultEff = unpause
    , _scOptionFlag = NormalOptions
    }
pauseMenuOptions :: [MenuOption]
pauseMenuOptions =
    [ Toggle ScancodeN startNewGame (const "NEW LEVEL")
    , Toggle ScancodeR (Just . loadStoredLevel) (const "RESTART")
    , Toggle ScancodeO (pushScreen optionMenu ) (const "OPTIONS")
    , Toggle ScancodeC (pushScreen displayControls) (const "CONTROLS")
    , InvisibleToggle ScancodeEscape (const Nothing)
    ]
startNewGame :: World -> Maybe World
startNewGame w = Just $ w
    & menuLayers .~ [WaitScreen (const "GENERATING...") 1]
    & worldEvents .~ const aNewGame
  where
    aNewGame :: World
    aNewGame = updateFramebufferSize $ generateLevelFromRoomList levx $ initialWorld 
        & randGen .~ _randGen w
        & config .~ _config w
        & preloadData .~ _preloadData w

-- | hacky
scodeToChar :: Scancode -> Char
scodeToChar = toEnum . (+ 61) . fromIntegral . toNumber

--charToScode :: Char -> Scancode
--charToScode = Scancode . fromIntegral . (\x -> x - 61) . fromEnum

updateFramebufferSize :: World -> World
updateFramebufferSize w = w & sideEffects %~ up
  where 
    (x,y) = (round $ getWindowX w, round $ getWindowY w)
    divRes = w ^. config . resolution_factor
    up :: (World -> IO World) -> World -> IO World
    up f w' = do
        pdata <- pdataResizeUpdate (x `div` divRes) (y `div` divRes) x y (_preloadData w')
        f $ w' & preloadData .~ pdata
    
--levelMenu :: Int -> ScreenLayer
--levelMenu x = OptionScreen
--    { _scTitle = const $ "LEVEL "++ show x 
--    , _scOptions = levelMenuOptions
--    , _scDefaultEff = unpause . storeLevel
--    , _scOptionFlag = NormalOptions
--    }
unpause :: World -> Maybe World
unpause w = Just . resumeSound $ w {_menuLayers = []}

displayControls :: ScreenLayer
displayControls = ColumnsScreen "CONTROLS" listControls

listControls :: [(String,String)]
listControls =
    [("wasd",           "MOVEMENT")
    ,("[rmb]",          "AIM")
    ,("[rmb+lmb]",      "SHOOT OR USE ITEM")
    ,("[wheelscroll]" , "SELECT ITEM"  )
    ,("[space]" ,       "PICKUP ITEM"  )
    ,("m" ,             "DISPLAY MAP"    )
    ,("f" ,             "DROP ITEM"    )
    ,("c[esc]" ,        "PAUSE"        )
    ,("qe" ,            "ROTATE CAMERA")
    ,("F5" ,            "QUICKSAVE")
    ,("F9" ,            "QUICKLOAD")
    ]
