{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
module Dodge.LevelGen where
import Dodge.Data
import Dodge.Floor
import Dodge.Save
import Dodge.Layout
import Dodge.Initialisation
--import Dodge.RandomHelp
--import Dodge.LevelGen.Data
import Dodge.Tree
import Geometry.ConvexPoly
--import Dodge.LevelGen.LevelStructure

import Data.Foldable
import System.Directory
import System.Random
import Control.Lens
import Control.Monad.State
import qualified Data.IntMap.Strict as IM

generateWorldFromSeed :: Int -> IO World
generateWorldFromSeed i = do
    createDirectoryIfMissing True "log"
    writeFile "log/attemptedSeeds" ""
    writeFile "log/treeCluster" ""
    writeFile "log/aGeneratedRoomLayout" ""
    (roomList,bounds) <- layoutLevelFromSeed 0 i
    return $ saveLevelStartSlot 
        $ postGenerationProcessing
        $ generateLevelFromRoomList roomList initialWorld{_randGen=mkStdGen i}
        & roomClipping .~ bounds

postGenerationProcessing :: World -> World
postGenerationProcessing w = foldl' assignPushDoors w (_doors w)

assignPushDoors :: World -> Door -> World
assignPushDoors w dr = case dr ^?! drPushedBy of
    PushedBy i -> w & doors . ix i . drPushes ?~ _drID dr
    _ -> w

layoutLevelFromSeed :: Int -> Int -> IO (IM.IntMap Room,[ConvexPoly])
layoutLevelFromSeed i seed = do
    let i' = i + 1
    putStrLnAppend "log/attemptedSeeds" $ "Generating level with seed " ++ show seed
    appendFile "log/attemptedSeeds" "\n"
    let g = mkStdGen seed
    let treecluster = evalState initialRoomTree (g,0)
    let labts = decomposeSelfTree $ numSelfTree $ combineTree _rmName treecluster
    appendFile "log/treeCluster" ("Seed: "++ show seed ++ "\n")
    mapM_ (putStrLnAppend "log/treeCluster" . smallDrawTree . fmap showIntsString) labts
    let tc = composeTree treecluster
    let rmtree = inorderNumberTree tc
    putStrLn "Room layout (compact): "
    putStrLn $ compactDrawTree $ fmap (show . snd) rmtree
    let nameshow (r,rid) = _rmName r ++ "-" ++ show rid
    appendFile "log/aGeneratedRoomLayout" ("Seed: "++ show seed)
    putStrLnAppend "log/aGeneratedRoomLayout" "Layout with room names:"
    putStrLnAppend "log/aGeneratedRoomLayout" $ smallDrawTree $ fmap nameshow rmtree
    mrs <- positionRoomsFromTree rmtree
    case mrs of
        Just (PosRooms bounds rs) -> do
            putStrLnAppend "log/attemptedSeeds" $ "After " ++ show i' 
                ++ " attempt(s), Successful generation of level with seed " 
                ++ show seed
            putStrLnAppend "log/attemptedSeeds" $ show (Prelude.length rs) ++ " rooms in total"
            return (IM.fromList $ map reversePair rs , bounds)
        Nothing -> do
            putStrLnAppend "log/attemptedSeeds" $ "Level generation with seed " ++ show seed ++ " failed: "
            let (seed',_) = random g
            layoutLevelFromSeed i' seed'

putStrLnAppend :: FilePath -> String -> IO ()
putStrLnAppend fp str = appendFile fp (str ++ "\n") >> putStrLn str

reversePair :: (a,b) -> (b,a)
reversePair (a,b) = (b,a)

compactDrawTree :: Tree String -> String
compactDrawTree = unlines . compactDraw

smallDrawTree :: Tree String -> String
smallDrawTree = unlines . compactDraw'

compactDraw :: Tree String -> [String]
compactDraw (Node x [Node y ts]) = compactDraw (Node (x++","++y) ts)
compactDraw (Node x ts0) = lines x ++ drawSubTrees ts0
  where
    drawSubTrees [] = []
    drawSubTrees [t] =
        "|" : compactDraw t
    drawSubTrees (t:ts) =
        "|" : shift "+- " "|  " (compactDraw t) ++ drawSubTrees ts
    shift first other = zipWith (++) (first : repeat other)

compactDraw' :: Tree String -> [String]
compactDraw' (Node x [Node y ts]) = lines x ++ ["|"] ++ compactDraw' (Node y ts)
compactDraw' (Node x ts0) = lines x ++ drawSubTrees ts0
  where
    drawSubTrees [] = []
    drawSubTrees [t] =
        "|" : compactDraw' t
    drawSubTrees (t:ts) =
        "|" : shift "+- " "|  " (compactDraw' t) ++ drawSubTrees ts
    shift first other = zipWith (++) (first : repeat other)
