{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}

module Database.Esqueleto.Postgis.Geometry
  ( SpatialType(..)
  , HasPgType(..)
  , Postgis(..)
  , PostgisGeometry
  ) where

import Data.List.NonEmpty (NonEmpty)
import Data.LineString (LineString)
import Data.LinearRing (LinearRing)
import Data.Text (Text)

-- | Guarantees we don't accidently mix curved space with flat space.
--   Postgis will catch this too, this just put's it in the type system.
data SpatialType = Geometry -- ^ assume a flat space.
                 | Geography -- ^ assume curvature of the earth.

-- | technical typeclass to bind a spatial type to a string value.
--   because we represent the constructors as a datakind, we need
--   this to go back to a value.
class HasPgType (spatialType :: SpatialType) where
  pgType :: proxy spatialType -> Text

instance HasPgType 'Geometry where
  pgType :: forall (proxy :: SpatialType -> *). proxy 'Geometry -> Text
pgType proxy 'Geometry
_ = Text
"geometry"

instance HasPgType 'Geography where
  pgType :: forall (proxy :: SpatialType -> *). proxy 'Geography -> Text
pgType proxy 'Geography
_ = Text
"geography"

-- | backwards compatibility, initial version only dealt in geometry
type PostgisGeometry = Postgis 'Geometry

-- | like 'GeospatialGeometry' but not partial, eg no empty geometries.
--   Also can put an inveriant on dimensions if a function requires it.
--   for example 'st_intersects' 'PostgisGeometry' 'PointXY' can't work with 'PostgisGeometry' 'PointXYZ'.
--   PointXY indicates a 2 dimension space, and PointXYZ a three dimension space.
data Postgis (spatialType :: SpatialType) point
  = Point point
  | MultiPoint (NonEmpty point)
  | Line (LineString point)
  | Multiline (NonEmpty (LineString point))
  | Polygon (LinearRing point)
  | MultiPolygon (NonEmpty (LinearRing point))
  | Collection (NonEmpty (PostgisGeometry point))
  deriving (Int -> Postgis spatialType point -> ShowS
[Postgis spatialType point] -> ShowS
Postgis spatialType point -> String
(Int -> Postgis spatialType point -> ShowS)
-> (Postgis spatialType point -> String)
-> ([Postgis spatialType point] -> ShowS)
-> Show (Postgis spatialType point)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall (spatialType :: SpatialType) point.
Show point =>
Int -> Postgis spatialType point -> ShowS
forall (spatialType :: SpatialType) point.
Show point =>
[Postgis spatialType point] -> ShowS
forall (spatialType :: SpatialType) point.
Show point =>
Postgis spatialType point -> String
$cshowsPrec :: forall (spatialType :: SpatialType) point.
Show point =>
Int -> Postgis spatialType point -> ShowS
showsPrec :: Int -> Postgis spatialType point -> ShowS
$cshow :: forall (spatialType :: SpatialType) point.
Show point =>
Postgis spatialType point -> String
show :: Postgis spatialType point -> String
$cshowList :: forall (spatialType :: SpatialType) point.
Show point =>
[Postgis spatialType point] -> ShowS
showList :: [Postgis spatialType point] -> ShowS
Show, (forall a b.
 (a -> b) -> Postgis spatialType a -> Postgis spatialType b)
-> (forall a b.
    a -> Postgis spatialType b -> Postgis spatialType a)
-> Functor (Postgis spatialType)
forall a b. a -> Postgis spatialType b -> Postgis spatialType a
forall a b.
(a -> b) -> Postgis spatialType a -> Postgis spatialType b
forall (spatialType :: SpatialType) a b.
a -> Postgis spatialType b -> Postgis spatialType a
forall (spatialType :: SpatialType) a b.
(a -> b) -> Postgis spatialType a -> Postgis spatialType b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall (spatialType :: SpatialType) a b.
(a -> b) -> Postgis spatialType a -> Postgis spatialType b
fmap :: forall a b.
(a -> b) -> Postgis spatialType a -> Postgis spatialType b
$c<$ :: forall (spatialType :: SpatialType) a b.
a -> Postgis spatialType b -> Postgis spatialType a
<$ :: forall a b. a -> Postgis spatialType b -> Postgis spatialType a
Functor, Postgis spatialType point -> Postgis spatialType point -> Bool
(Postgis spatialType point -> Postgis spatialType point -> Bool)
-> (Postgis spatialType point -> Postgis spatialType point -> Bool)
-> Eq (Postgis spatialType point)
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall (spatialType :: SpatialType) point.
Eq point =>
Postgis spatialType point -> Postgis spatialType point -> Bool
$c== :: forall (spatialType :: SpatialType) point.
Eq point =>
Postgis spatialType point -> Postgis spatialType point -> Bool
== :: Postgis spatialType point -> Postgis spatialType point -> Bool
$c/= :: forall (spatialType :: SpatialType) point.
Eq point =>
Postgis spatialType point -> Postgis spatialType point -> Bool
/= :: Postgis spatialType point -> Postgis spatialType point -> Bool
Eq)