module Data.Internal.Wkb.GeometryCollection
  ( getGeometryCollection
  , getEnclosedFeature
  , builderGeometryCollection
  ) where

import qualified Control.Monad              as Monad
import qualified Data.Binary.Get            as BinaryGet
import qualified Data.ByteString.Builder    as ByteStringBuilder
import qualified Data.Foldable              as Foldable
import qualified Data.Geospatial            as Geospatial
import           Data.Monoid                ((<>))
import qualified Data.Sequence              as Sequence

import qualified Data.Internal.Wkb.Endian   as Endian
import qualified Data.Internal.Wkb.Geometry as Geometry


-- Binary parsers

getGeometryCollection :: BinaryGet.Get Geospatial.GeospatialGeometry
                          -> Endian.EndianType
                          -> Geometry.CoordinateType
                          -> BinaryGet.Get Geospatial.GeospatialGeometry
getGeometryCollection getGeospatialGeometry endianType _ = do
  numberOfGeometries <- Endian.getFourBytes endianType
  geoSpatialGeometries <- Sequence.replicateM (fromIntegral numberOfGeometries) getGeospatialGeometry
  pure $ Geospatial.Collection geoSpatialGeometries

getEnclosedFeature :: (Endian.EndianType -> BinaryGet.Get Geometry.WkbGeometryType)
                      -> Geometry.GeometryType
                      -> (Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get feature)
                      -> BinaryGet.Get feature
getEnclosedFeature getWkbGeom expectedGeometryType getFeature = do
  endianType <- Endian.getEndianType
  geometryTypeWithCoords <- getWkbGeom endianType
  let (Geometry.WkbGeom geoType coordType) = geometryTypeWithCoords
  if geoType == expectedGeometryType then
    getFeature endianType coordType
  else
    Monad.fail "Wrong geometry type of enclosed feature"


-- Binary builders

type BuilderGeospatialFeature = Geometry.BuilderWkbGeometryType -> Endian.EndianType ->  Geospatial.GeospatialGeometry -> ByteStringBuilder.Builder

builderGeometryCollection :: BuilderGeospatialFeature
                              -> Geometry.BuilderWkbGeometryType
                              -> Endian.EndianType
                              -> Sequence.Seq Geospatial.GeospatialGeometry
                              -> ByteStringBuilder.Builder
builderGeometryCollection builderGeospatialFeature builderWkbGeom endianType geometryCollection =
  Endian.builderEndianType endianType
    <> builderWkbGeom endianType (Geometry.WkbGeom Geometry.GeometryCollection Geometry.TwoD)
    <> Endian.builderFourBytes endianType (fromIntegral $ length geometryCollection)
    <> Foldable.foldMap (builderGeospatialFeature builderWkbGeom endianType) geometryCollection