content: module Level.Level
      (Level(Level),
       LevelData(LevelData),
       levelData,
       levelWidth,
       levelHeight,
       levelItemCounts,
       levelEnd,
       levelCat,
       levelFireHydrantsL,
       levelFireHydrantsR,
       levelPuddles,
       levelRects,
       levelPolys,
       levelBackgrounds,
       openLevel) where
  
  import Nxt.Types
  import System.IO
  import qualified Control.Exception as CE
  import qualified Error.Error as E
  import Settings.DisplaySettings
  import Nxt.Graphics hiding (end)
  import Control.Arrow (second)
  
  data LevelData = LevelData
          {
          levelEnd            :: Rect,
          levelCat            :: Rect,
          levelFireHydrantsL  :: [Rect],
          levelFireHydrantsR  :: [Rect],
          levelPuddles        :: [Rect],
          levelRects          :: [Rect],
          levelPolys          :: [Poly],
          levelBackgrounds    :: [(Vector2d, Nxt.Types.Texture)]
          }
  data Level = Level
          {
          levelWidth      :: Int,
          levelHeight     :: Int,
          levelItemCounts :: [Int],
          levelData       :: LevelData
          }
  
  -- readInt
  readInt' :: String -> Int
  readInt' = read
  
  -- readDouble
  readDouble' :: String -> Double
  readDouble' = read
  
  -- openLevel
  openLevel :: String -> IO Level
  openLevel file = do
      inh <- openFile file ReadMode
      level <- parseLevel inh
      hClose inh
      return level
  
  -- parseLevel
  parseLevel :: Handle -> IO Level
  parseLevel inh = do
      levelDimensionS <- hGetLine inh
      let levelDimension = map readInt' (words levelDimensionS)
  
      itemCountsS <- hGetLine inh
      let itemCountsList = map readInt' (words itemCountsS)
          --itemCounts = initItemCount itemCountsList
  
      numObjectS <- hGetLine inh
      let numObject = readInt' numObjectS
      let dummyData = LevelData (Rect 0 0 0 0) (Rect 0 0 0 0) [] [] [] [] [] []
      lvlData <- parseShape numObject inh dummyData
      let levelDataT = transformCoord lvlData
      let level = Level (head levelDimension) (last levelDimension) itemCountsList levelDataT
  
      return level
  
  -- transform coordinates from using top left as (0,0) to bottom left as (0,0)
  transformCoord :: LevelData -> LevelData
  transformCoord (LevelData end cat fireHydrantsL fireHydrantsR puddles rects polys bgTex) =
      LevelData (transformR end) (transformR cat) (map transformR fireHydrantsL) (map transformR fireHydrantsR) (map transformR puddles) (map transformR rects) (map transformP polys) bgTex
      where transformR (Rect rx ry rw rh) = let sh' = fromGLdouble screenResHeight
                                            in Rect rx (sh' - ry) rw (-rh)
            transformP (Poly polyS polyVs) = let sh' = fromGLdouble screenResHeight
                                             in Poly polyS (map (second (sh' -)) polyVs)
  
  -- parseShape
  parseShape :: Int -> Handle -> LevelData -> IO LevelData
  parseShape numShapes inh (leveldata@(LevelData _ _ fireHydrantsL fireHydrantsR puddles rects polys _)) = do
      ineof <- hIsEOF inh
      if ineof || numShapes <= 0
        then return leveldata
        else
          do
              coordS <- hGetLine inh
              let toks = words coordS
              let coord = map readDouble' (tail $ words coordS)
              let verts = parseVerts coord
              let poly = Poly (length verts) verts
              let obj = head toks
              let newLevelData = case obj of
                                   "rectangle"        -> leveldata {levelRects = parseRect coord : rects}
                                   "cat"              -> leveldata {levelCat = parseRect coord}
                                   "end"              -> leveldata {levelEnd = parseRect coord}
                                   "firehydrantLeft"  -> leveldata {levelFireHydrantsL = parseRect coord : fireHydrantsL}
                                   "firehydrantRight" -> leveldata {levelFireHydrantsR = parseRect coord : fireHydrantsR}
                                   "puddle"           -> leveldata {levelPuddles = parseRect coord : puddles}
                                   "polygon"          -> leveldata {levelPolys = poly : polys}
                                   _                  -> CE.throw (E.BadLevelData obj)
              parseShape (numShapes-1) inh newLevelData
  
  -- parseVerts
  parseVerts :: [Double] -> [Vector2d]
  parseVerts [] = []
  parseVerts (_x:[]) = CE.throw E.BadVerticesData
  parseVerts (x:y:vs) = (x,y):parseVerts vs
  
  -- parseRect
  parseRect :: [Double] -> Rect
  parseRect coords =
      if length coords /= 8
      then CE.throw E.BadRectData
      else Rect bottomLX bottomLY width height
          where bottomLX = head coords
                bottomLY = coords !! 1
                width = (coords !! 2) - bottomLX
                height = (coords !! 7) - bottomLY
  
  