module World.World
    (WorldState(WorldState),
     gameState,
     keysStateRef,
     mousePosRef,
     curLevel,
     mainPanel,
     itemPanel,
     messagePanel,
     menuTextures,
     updateMessagePanel,
     updateItem,
     loadLevel,
     loadLevelBackgrounds) where

import qualified Graphics.UI.GLUT as Glut
import Data.IORef
import Input.InputState as InputState
import Panels.MainPanel hiding (itemList)
import Panels.ItemPanel
import Panels.MessagePanel
import Items.Items
import Level.Level
import Level.FireHydrant
import Level.EndMarker
import Game.GameState
import Nxt.Types
import Cat.Cat
import Nxt.Graphics
import Settings.Path

data WorldState = WorldState
    {
        gameState       :: GameState,
        keysStateRef    :: IORef InputState.KeysState,
        mousePosRef     :: IORef InputState.MousePos,
        curLevel        :: Level,
        mainPanel       :: MainPanel,
        itemPanel       :: ItemPanel,
        messagePanel    :: MessagePanel,
        menuTextures    :: (Nxt.Types.Texture, Nxt.Types.Texture)
    }

-- updateMessagePanel
updateMessagePanel :: WorldState -> IO MessagePanel
updateMessagePanel worldState = do
    mousePos <- readIORef (mousePosRef worldState)
    Glut.Size winW winH <- Glut.get Glut.windowSize
    let (mousex, mousey) = translateMousePos mousePos winW winH
    let mousePos' = MousePos (floor mousex) (floor mousey)
    let itemList = itemButtonList (itemPanel worldState)

    let messageDisplay' = foldr (\item str -> if mouseOverItemBut mousePos' item then itemButDesc item else str) "" itemList

    return (MessagePanel messageDisplay')

-- updateItem
updateItem :: WorldState -> IO Item
updateItem worldState = do
    mousePos <- readIORef (mousePosRef worldState)
    Glut.Size winW winH <- Glut.get Glut.windowSize
    let (mousex, mousey) = translateMousePos mousePos winW winH
    let mousePos' = MousePos (floor mousex) (floor mousey)
    let itemList = itemButtonList (itemPanel worldState)
    let cItem = curItem (mainPanel worldState)
    let newitem = foldr (\itemB r -> if mouseOverItemBut mousePos' itemB then itemButItem itemB else r) cItem itemList
    return newitem

-- updateItemCounts
updateItemCounts :: [ItemButton] -> [Int] -> [ItemButton]
updateItemCounts itemBtnList itemCounts =
    map (\(itemBut, count) -> itemBut {itemButCount = count}) (zip (init itemBtnList) itemCounts)
    ++
    [last itemBtnList]

-- loadLevel
loadLevel :: WorldState -> String -> IO WorldState
loadLevel worldState levelPath = do
    lvl <- openLevel levelPath
    let lvlData = levelData lvl
        lvlRect = levelRects lvlData
        lvlPoly = levelPolys lvlData
        lvlPuddles = levelPuddles lvlData
        lvlEndRect = levelEnd lvlData
        lvlFireHydrantsL = levelFireHydrantsL lvlData
        lvlFireHydrantsR = levelFireHydrantsR lvlData
        lvlItemCounts = levelItemCounts lvl
        catStartRect = levelCat lvlData
        catStart = (rectX catStartRect, rectY catStartRect)
    lvlFireHydrants <- sequence $ map (\fhRect -> initFireHydrant (rectX fhRect, rectY fhRect) DirLeft) lvlFireHydrantsL
                                  ++
                                  map (\fhRect -> initFireHydrant (rectX fhRect, rectY fhRect) DirRight) lvlFireHydrantsR
    levelend <- initEndMarker (rectX lvlEndRect, rectY lvlEndRect)

    let cat' = (cat $ mainPanel worldState) {catPos = catStart}

    let initCameraPos = (0, 0)
        item = itemButItem $ last $ itemButtonList $ itemPanel worldState
        m = musak $ mainPanel worldState

    -- update item button limits/uses
    let itemButtonList' = updateItemCounts (itemButtonList $ itemPanel worldState) lvlItemCounts

    let curBGTex = backgroundTexture $ mainPanel worldState
        mainPanel' = MainPanel initCameraPos [] curBGTex cat' lvlRect lvlPoly lvlPuddles lvlFireHydrants levelend [] [] [] item Nothing m
        goStopBtn = goStopButton $ itemPanel worldState
        itemPanel' = (itemPanel worldState) {itemButtonList = itemButtonList', goStopButton = goStopBtn {goStopState = GoState}}

    return (worldState {curLevel = lvl, mainPanel = mainPanel', itemPanel = itemPanel',
                        gameState = GameRunningState})

-- loadLevelBackgrounds (NO TIME TO IMPLEMENT THIS IN FILE FORMAT PROPERLY!)
loadLevelBackgrounds :: String -> Level -> IO [(Vector2d, Nxt.Types.Texture)]
loadLevelBackgrounds levelPath _ = do
    dataPath <- getDataDir
    -- let lvlData = levelData level

    -- this doesn't seem to actually do anything..
    -- free previous level's textures
    -- mapM_ (\(_, oldBg) -> freeTexture oldBg) (levelBackgrounds lvlData)

    let lvlPos = case drop (length dataPath) levelPath of
                    "/data/levels/water1/water1.lvl"         -> [(0.0, 0.0), (1024.0, 0.0)]
                    "/data/levels/movement1/movement1.lvl"   -> [(-15.0, -265.0), (1009.0, -265.0), (2033, -265.0)]
                    "/data/levels/water2/water2.lvl"         -> [(0.0, -200.0), (1024.0, -200.0)]
                    "/data/levels/movement2/movement2.lvl"   -> [(5.0, -70.0), (1029.0, -70.0)]
                    "/data/levels/pool/pool.lvl"             -> [(53.0, -295.0), (1077.0, -295.0)]
                    "/data/levels/rift/rift.lvl"             -> [(10.0, -545.0), (1034.0, -545.0), (10.0, 479.0), (1034.0, 479.0)]
                    "/data/levels/skyline/skyline.lvl"       -> [(-40.0, -685.0), (984.0, -685.0), (2008.0, -685.0),
                                                                    (-40.0, 339.0), (984.0, 339.0), (2008.0, 339.0)]
                    "/data/levels/river/river.lvl"           -> [(10.0, -315.0), (1034.0, -315.0), (2058.0, -315.0)]
                    "/data/levels/pinball/pinball.lvl"       -> [(110.0, -330.0), (1134.0, -330.0)]
                    _                                        -> []

    lvlBgs <- case drop (length dataPath) levelPath of
                 "/data/levels/water1/water1.lvl"        -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/water1/water1_0_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/water1/water1_1_0.png")]
                 "/data/levels/movement1/movement1.lvl"  -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/movement1/movement1_0_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/movement1/movement1_1_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/movement1/movement1_2_0.png")]
                 "/data/levels/water2/water2.lvl"        -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/water2/water2_0_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/water2/water2_1_0.png")]
                 "/data/levels/movement2/movement2.lvl"  -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/movement2/movement2_0_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/movement2/movement2_1_0.png")]
                 "/data/levels/pool/pool.lvl"            -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/pool/pool_0_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/pool/pool_1_0.png")]
                 "/data/levels/rift/rift.lvl"            -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/rift/rift_0_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/rift/rift_1_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/rift/rift_0_1.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/rift/rift_1_1.png")]
                 "/data/levels/skyline/skyline.lvl"      -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/skyline/skyline_0_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/skyline/skyline_1_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/skyline/skyline_2_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/skyline/skyline_0_1.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/skyline/skyline_1_1.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/skyline/skyline_2_1.png")]
                 "/data/levels/river/river.lvl"          -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/river/river_0_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/river/river_1_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/river/river_2_0.png")]
                 "/data/levels/pinball/pinball.lvl"      -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/pinball/pinball_0_0.png"),
                                                                         Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/pinball/pinball_1_0.png")]
                 -- HACK FIX:This is warning fix and might be usefull if variable levels count can be used.
                 -- Image unknown.png doesn't exist, so exeption will be raised.
                 _                                       -> sequence [Nxt.Graphics.loadTexture (dataPath ++ "/data/levels/unknown_level/unknown.png")]

    return (zip lvlPos lvlBgs)

