-- | Rectangular areas of levels and their basic operations.
module Game.LambdaHack.Server.DungeonGen.Area
  ( Area, toArea, fromArea, trivialArea, grid, shrink
  ) where

import Data.Binary

import Game.LambdaHack.Common.Point

-- | The type of areas. The bottom left and the top right points.
data Area = Area !X !Y !X !Y
  deriving Show

-- | Checks if it's an area with at least one field.
toArea :: (X, Y, X, Y) -> Maybe Area
toArea (x0, y0, x1, y1) = if x0 <= x1 && y0 <= y1
                          then Just $ Area x0 y0 x1 y1
                          else Nothing

fromArea :: Area -> (X, Y, X, Y)
fromArea (Area x0 y0 x1 y1) = (x0, y0, x1, y1)

trivialArea :: Point -> Area
trivialArea (Point x y) = Area x y x y

-- | Divide uniformly a larger area into the given number of smaller areas
-- overlapping at the edges.
grid :: (X, Y) -> Area -> [(Point, Area)]
grid (nx, ny) (Area x0 y0 x1 y1) =
  let xd = x1 - x0  -- not +1, because we need overlap
      yd = y1 - y0
  in [ (Point x y, Area (x0 + xd * x `div` nx)
                          (y0 + yd * y `div` ny)
                          (x0 + xd * (x + 1) `div` nx)
                          (y0 + yd * (y + 1) `div` ny))
     | x <- [0..nx-1], y <- [0..ny-1] ]

-- | Enlarge (or shrink) the given area on all fours sides by the amount.
shrink :: Area -> Maybe Area
shrink (Area x0 y0 x1 y1) = toArea (x0 + 1, y0 + 1, x1 - 1, y1 - 1)

instance Binary Area where
  put (Area x0 y0 x1 y1) = do
    put x0
    put y0
    put x1
    put y1
  get = do
    x0 <- get
    y0 <- get
    x1 <- get
    y1 <- get
    return (Area x0 y0 x1 y1)