{- | Creation, update and destruction of destructible walls.  -}
module Dodge.Placement.PlaceSpot.Block 
    ( plBlock
    , plLineBlock
    )
    where
import Dodge.Data
import Dodge.Path
import Dodge.Block.Debris
--import Dodge.LevelGen.LevelStructure
import Dodge.Base
--import Dodge.Zone
import Geometry
import qualified IntMapHelp as IM

import Control.Lens
import Data.List
import qualified Data.IntSet as IS

plBlock 
    :: [Point2]     -- ^ Block polygon
    -> Block
    -> Wall         -- ^ Base Pane
    -> World 
    -> (Int,World)
plBlock [] _ _ _ = error "Trying to add a block with incomplete polygon"
plBlock (p:ps) bl wl w = (,) blid $ w
    & blocks . at blid ?~ bl 
        { _blID = blid
        , _blWallIDs = IS.fromList is
        , _blShadows = []
        , _blFootprint = p:ps
        }
    & insertWalls blid wls
  where 
    blid = IM.newKey $ _blocks w
    lns = zip (p:ps) (ps ++ [p])
    i = IM.newKey $ _walls w
    is = [i.. i + length lns-1]
    wls = zipWith 
        (\j ln -> wl & wlLine .~ ln & wlID .~ j & wlStructure .~ BlockPart blid) 
        is 
        lns 
        
{- | Splits a line into many four cornered blocks. -}
plLineBlock 
    :: Wall     -- ^ Base pane
    -> Float
    -> Point2   -- ^ Start point (symmetric)
    -> Point2   -- ^ End point (symmetric)
    -> World 
    -> (Int, World)
plLineBlock basePane blwidth a b gw = ( 0 
--    , foldr insertWall (insertBlocks gw) listWalls
    , insertBlocks gw
    )
  where 
    depth = blwidth
    psOnLine = divideLineOddNumPoints blwidth a b
    halfBlockWidth = dist a b / fromIntegral (length psOnLine - 1)
    blockCenPs = snd $ evenOddSplit psOnLine
    numBlocks = length blockCenPs
    is = [0.. numBlocks - 1]
    cornerPoints = reverse $ rectWH halfBlockWidth depth -- goes clockwise around the block
    cornersAt p = fmap ( (p +.+) . rotateV (argV (b -.- a)) ) cornerPoints
    linesAt p = loopPairs $ cornersAt p
    wlid = IM.newKey $ _walls gw
    blid = IM.newKey $ _blocks gw
    insertBlock (i,p) = 
        insertWalls (i + blid) (makeWallAt p i)
        . over blocks (IM.insert (i+blid) Block 
        { _blID = i + blid, _blWallIDs = IS.fromList $ ksAtI i
        , _blHP = 1000, _blShadows = shadowsAt i
        , _blDir = 0    -- THIS IS NOT SENSIBLE. TODO rethink block positioning
        , _blFootprint = cornersAt p -- TODO check winding (clockwise, anticlockwise)
        , _blObstructs = []
        , _blPos = p, _blDraw = const mempty , _blDeath = makeBlockDebris}
        )
    insertBlocks = flip (foldr insertBlock) $ zip is blockCenPs
    ksAtI i = map ( + (wlid + i*4) ) [0,1,2,3]
    visibilityAt i 
        | i == 0             = [False,True,True ,True]
        | i == numBlocks - 1 = [True ,True,False,True]
        | otherwise          = [False,True,False,True]
    shadowsAt i
        | i == 0             = ksAtI 1
        | i == numBlocks - 1 = ksAtI $ numBlocks - 2
        | otherwise          = ksAtI (i-1) ++ ksAtI (i+1)
    makeWallAt p i = zipWith3 (makePane i) (visibilityAt i) (ksAtI i) (linesAt p)
    makePane i visStatus k ps = basePane
        {_wlID = k
        ,_wlStructure = BlockPart $ i + blid
        ,_wlLine = ps
        ,_wlDraw = visStatus
        }

-- | Must be done after inserting the block
insertWalls :: Int -> [Wall] -> World -> World
insertWalls blid wls w = w' & blocks . ix blid . blObstructs .~ concat paths
  where
    (w',paths) = mapAccumR (flip insertWall) w wls

insertWall :: Wall -> World -> (World,[(Int,Int,PathEdge)])
insertWall wl = uncurry (obstructPathsCrossing BlockObstacle) (_wlLine wl)
    . (walls . at (_wlID wl) ?~ wl)
