module Dodge.Placement.Instance.Wall
    where
import Dodge.Data
import Dodge.LevelGen.Data
import Dodge.Default.Wall
import Geometry
import Shape
import Color

import Data.List
import Control.Lens

heightWallPS :: PlacementSpot -> Float -> [Point2] -> Placement
heightWallPS spot h ps = psj spot (PutShape . colorSH col $ upperPrismPoly h ps)
    $ sps spot $ PutWall ps theWall
  where
    col = _wlColor defaultWall
    theWall = defaultWall
        { _wlOpacity = SeeAbove
        , _wlDraw = False
        , _wlHeight = h
        }

heightWall :: Float -> [Point2] -> Placement
heightWall h ps = ps0j (PutShape . colorSH col $ upperPrismPoly h ps)
    $ sps0 $ PutWall ps theWall
  where
    col = _wlColor defaultWall
    theWall = defaultWall
        { _wlOpacity = SeeAbove
        , _wlDraw = False
        , _wlHeight = h
        }

midWall :: [Point2] -> Placement
midWall ps = ps0j (PutShape . colorSH col $ upperPrismPoly 50 ps)
    $ sps0 $ PutWall ps theWall
  where
    col = _wlColor defaultWall
    theWall = defaultWall
        { _wlOpacity = SeeAbove
        , _wlDraw = False
        , _wlHeight = 50
        }


singleBlock :: Point2 -> [Placement]
singleBlock a = 
    [sPS a 0 
        $ PutBlock StoneBlock 5 [20,20] baseBlockPane
        $ reverse 
        $ square 10
    ]
{-
Places a line of blocks between two points.
Width 9, also extends out from each point by 9.
 -}
blockLine :: Point2 -> Point2 -> Placement
blockLine a b = sps0 $ PutLineBlock baseBlockPane StoneBlock 9 9 a b

{-
Places an breakable window between two points.
Width 8, also extends out from each point by 8.
 -}
windowLine :: Point2 -> Point2 -> Placement
windowLine a b = sps0 $ PutLineBlock baseWindowPane GlassBlock 8 8 a b
{-
Places an unbreakable window between two points.
Width 7, also extends out from each point by 7.
 -}
crystalLine :: Point2 -> Point2 -> Placement
crystalLine a b = sps0 $ PutWall ps defaultCrystalWall
  where
    ps = 
        [ a +.+ left +.+ up
        , (a +.+ left) -.- up
        , (b -.- left) -.- up
        , (b -.- left) +.+ up
        ]
    left = 7 *.* normalizeV (a-.-b)
    up   = vNormal left
{- Places an unbreakable wall between two points.
Depth 15, does not extend wider than points.
 -}
wallLine :: Point2 -> Point2 -> Placement
wallLine a b = sps0 $ PutWall ps defaultWall
  where
    ps = [ a  +.+ up
        , a  -.- up
        , b  -.- up
        , b  +.+ up
        ]
    left = 15 *.* normalizeV (a-.-b)
    up   = vNormal left

windowLineType :: Point2 -> Point2 -> PSType
windowLineType = PutLineBlock baseWindowPane GlassBlock 8 8

baseBlockPane :: Wall
baseBlockPane = defaultWall
    { _wlLine = (V2 0 0,V2 50 0)
    , _wlID = 0
    , _wlColor = greyN 0.5
    , _wlSeen = False
    , _wlOpacity = Opaque
    , _wlDraw = True
    , _wlFireThrough = True
    }
dirtWall :: Wall
dirtWall = defaultWall
    { _wlLine = (V2 0 0,V2 50 0)
    , _wlID = 0
    , _wlColor = V4 (150/256) ( 75/256)  0 ( 250/256)
    , _wlSeen = False
    , _wlOpacity = Opaque
    , _wlRotateTo = False
    , _wlDraw = True
    , _wlFireThrough = True
    }
baseWindowPane :: Wall
baseWindowPane = defaultWall
    { _wlLine = (V2 0 0,V2 50 0)
    , _wlID = 0
    , _wlColor = withAlpha 0.2 cyan
    , _wlSeen = False
    , _wlOpacity = SeeThrough
    , _wlDraw = True
    , _wlFireThrough = True
    }
-- TODO find home for this
{- Replaces instances of a given 'PutID' with 'PSType's drawn from a list.  -}
replacePutID 
    :: Int      -- ^ The id of 'PutID' to be replaced
    -> [PSType] -- ^ List of replacements
    -> Room 
    -> Room
replacePutID i psts r =
    r & rmPmnts %~ flip (subZipWith (isPutID i) (\ps pt -> ps & plType .~ pt)) psts
{- Partition a list by a predicate, apply a zip to those elements 
that satisfy the predicate, concatenate 
the new zipped list and the other (unchanged) half.  -}
subZipWith 
    :: (a -> Bool)      -- ^ Filter: elements to apply zip to
    -> (a -> b -> a)    -- ^ Combining function
    -> [a]              -- ^ List to be partitioned
    -> [b]              -- ^ Modifying list
    -> [a]
subZipWith f g xs ys =
    let (zs,ws) = partition f xs
    in  zipWith g zs ys ++ ws

isPutID :: Int -> Placement -> Bool
isPutID i ps = Just i == ps ^? plType . putID

putBlockRect :: Float -> Float -> Float -> Float -> [Placement]
putBlockRect a x b y =
    [ blockLine (V2 a b) (V2 a y)
    , blockLine (V2 a y) (V2 x y)
    , blockLine (V2 x y) (V2 x b)
    , blockLine (V2 x b) (V2 a b)
    ]
putBlockV :: Float -> Float -> Float -> Float -> [Placement]
putBlockV a x b y = 
    [ blockLine (V2 a b) (V2 a y)
    , blockLine (V2 x b) (V2 a b)
    ]
putBlockC :: Float -> Float -> Float -> Float -> [Placement]
putBlockC a x b y = 
    [ blockLine (V2 a b) (V2 a y)
    , blockLine (V2 x b) (V2 a b)
    , blockLine (V2 a y) (V2 x y)
    ]
putBlockN :: Float -> Float -> Float -> Float -> [Placement]
putBlockN a x b y = 
    [ blockLine (V2 a b) (V2 a y)
    , blockLine (V2 x b) (V2 a b)
    , blockLine (V2 x y) (V2 x b)
    ]
