{- | Given a tree of rooms, tries to shift them into place in such a way that their
links connect and that none of them clip.
Creates a list of rooms; after this step the structure is determined by the actual positions of rooms.  -}
module Dodge.Tree.Shift
    ( positionRoomsFromTree
    ) where
import Dodge.LevelGen.Data
import Dodge.Room.Link
import Dodge.Tree.Polymorphic
import Dodge.RoomLink
import Geometry.ConvexPoly
import Geometry.Data
import Padding

import qualified Data.Set as S
import Data.Tree
import Data.Sequence hiding (zipWith)
import Data.List (delete)
import Data.Bifunctor
import Control.Lens hiding (Empty, (<|) , (|>))

type RoomInt = (Room,Int)

positionRoomsFromTree :: Tree RoomInt -> IO (Maybe [RoomInt])
positionRoomsFromTree (Node (r,i) ts) = printHeader 
    >> posRms (map pointsToPoly $ _rmBound r) (r,i) (zipCount ts) Empty

posRms :: [ConvexPoly]
    -> RoomInt
    -> [(Int,Tree RoomInt)]     -- the list of children, with indices
    -> Seq (Tree RoomInt)
    -> IO (Maybe [RoomInt])
posRms bounds (parent,i) [] st = case st of
    Empty                   -> return $ Just [(parent,i)]
    Node childi ts :<| tseq -> fmap ((parent,i):) <$> posRms bounds childi (zipCount ts) tseq
posRms bounds parenti@(parent,_) ( (numChild,t@(Node childi _) ):its) tseq = do
    printInfoCheckNum parenti numChild childi
    tryParentLinks outlinks
  where
    child = fst childi
    outlinks = zipCount 
        . Prelude.filter (not . S.null . S.intersection (_rmConnectsTo child) . _rlType) 
        $ _rmLinks parent 
    tryParentLinks [] = putStrLn "no viable link pairs, backtrack" >> return Nothing
    tryParentLinks ((j,outlnk):ls) = tryChildLinks $ zipCount (rmInLinks $ fst childi)
      where
        tryChildLinks [] = tryParentLinks ls
        tryChildLinks ((numinlink,il):ils) 
            | clipping  = tryChildLinks ils
            | otherwise = do
                putStrLn $ show j ++ "-"++show numinlink
                mayrs <- posRms (newBounds ++ bounds) 
                    (first updateparent parenti) its (tseq |> shiftedt)
                case mayrs of
                    Just rms -> return $ Just rms
                    Nothing -> printInfo parenti numChild childi
                        >> tryChildLinks ils
          where
            newBounds = map pointsToPoly $ _rmBound r'
            clipping = or (convexPolysOverlap <$> newBounds <*> bounds)
            updateparent rm = doLnkEff (lnkPosDir outlnk) $ rm 
                & rmLinks %~ delete outlnk 
                & rmPos %~ (uncurry (UsedOutLink numChild (snd childi)) (lnkPosDir outlnk) :)
                & rmChildren %~ (snd childi :)
            shiftedoutlink = shiftLinkBy (_rmShift parent) outlnk
            r' = doRoomShift . fst $ rootLabel shiftedt
            updatechild rm = rm 
                & rmMParent ?~ snd parenti
                & rmMID     ?~ snd childi
                & rmLinks %~ delete il 
                & rmPos %~ ( (uncurry (UsedInLink 0) (lnkPosDir il):)
                    . (map (uncurry UnusedLink . lnkPosDir) (delete il (_rmLinks rm)) ++) )
            shiftedt = applyToRoot 
                (first $ updatechild . shiftRoomShiftToLink (lnkPosDir shiftedoutlink) (lnkPosDir il)) t

zipCount :: [a] -> [(Int,a)]
zipCount = Prelude.zip [0..]

printColumns :: [Int] -> [String] -> IO ()
printColumns (i:is) (s:strs) = putStr (rightPad i ' ' s) >> printColumns is strs
printColumns _ strs = putStrLn $ concat strs

theColumns :: [String] -> IO ()
theColumns = printColumns [20,5,20,20]

printHeader :: IO ()
printHeader = theColumns ["Parent","Cnum","Child","Link pair"]

printInfo :: RoomInt -> Int -> RoomInt -> IO ()
printInfo (parentrm,parenti) childn (childrm,childi) = do
    putStr $ rpns 20 (_rmName parentrm ++ "-" ++ show parenti )
          ++ rpns 5 (show childn ++ " ")
          ++ rpns 20 (_rmName childrm ++ "-" ++ show childi )
  where
    rpns x s = rightPadNoSquash x ' ' s
printInfoCheckNum :: RoomInt -> Int -> RoomInt -> IO ()
printInfoCheckNum p 0 c = printInfo p 0 c
printInfoCheckNum _ childn (childrm,childi) = do
    putStr $ rpns 20 ""
          ++ rpns 5 (show childn ++ " ")
          ++ rpns 20 (_rmName childrm ++ "-" ++ show childi )
  where
    rpns x s = rightPadNoSquash x ' ' s

doLnkEff :: (Point2,Float) -> Room -> Room
doLnkEff x rm = case _rmLinkEff rm of
    (eff:effs) -> eff x $ rm & rmLinkEff .~ effs
    _ -> rm
