module Data.Internal.Wkt.Point
  ( point
  , multiPoint
  , justPointsXY
  , justPointsXYZ
  , justPointsXYZM
  , emptyPoint
  , emptyMultiPoint
  ) where

import           Control.Applicative      ((<|>))
import qualified Data.Geospatial          as Geospatial
import qualified Data.Internal.Wkt.Common as Wkt
import qualified Data.Scientific          as Scientific
import qualified Data.Sequence            as Sequence
import qualified Text.Trifecta            as Trifecta

point :: Trifecta.Parser Geospatial.GeoPoint
point = do
  _ <- Trifecta.string "point"
  _ <- Trifecta.spaces
  (Trifecta.string "empty" >> pure emptyPoint) <|> nonEmptyPoints

nonEmptyPoints :: Trifecta.Parser Geospatial.GeoPoint
nonEmptyPoints =
  (Trifecta.string "zm" >> fmap Geospatial.GeoPoint (bracketedPoint justPointsXYZM))
  <|> (Trifecta.string "z" >> fmap Geospatial.GeoPoint (bracketedPoint justPointsXYZ))
  <|> fmap Geospatial.GeoPoint (bracketedPoint justPointsXY)

multiPoint :: Trifecta.Parser Geospatial.GeoMultiPoint
multiPoint = do
  _ <- Trifecta.string "multipoint"
  _ <- Trifecta.spaces
  xl <- Wkt.emptySet <|> nonEmptyMultipoints
  pure $ Geospatial.GeoMultiPoint xl

nonEmptyMultipoints :: Trifecta.Parser (Sequence.Seq Geospatial.GeoPositionWithoutCRS)
nonEmptyMultipoints =
  (Trifecta.string "zm" >> manyPoints justPointsXYZM)
  <|> (Trifecta.string "z" >> manyPoints justPointsXYZ)
  <|> manyPoints justPointsXY

manyPoints :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS -> Trifecta.Parser (Sequence.Seq Geospatial.GeoPositionWithoutCRS)
manyPoints pointParser = do
  _ <- Trifecta.spaces >> Trifecta.char '(' >> Trifecta.spaces
  xl <- unbracketedPoints pointParser <|> bracketedPoints pointParser
  _ <- Trifecta.spaces >> Trifecta.char ')' >> Trifecta.spaces
  pure xl

unbracketedPoints :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS -> Trifecta.Parser (Sequence.Seq Geospatial.GeoPositionWithoutCRS)
unbracketedPoints pointParser = do
  x <- pointParser
  xs <- Trifecta.many (Trifecta.char ',' >> Trifecta.spaces >> pointParser )
  pure $ x Sequence.<| Sequence.fromList xs

bracketedPoints :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS -> Trifecta.Parser (Sequence.Seq Geospatial.GeoPositionWithoutCRS)
bracketedPoints pointParser = do
  x <- bracketedPoint pointParser
  xs <- Trifecta.many (Trifecta.char ',' >> Trifecta.spaces >> bracketedPoint pointParser)
  pure $ x Sequence.<| Sequence.fromList xs

bracketedPoint :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS -> Trifecta.Parser Geospatial.GeoPositionWithoutCRS
bracketedPoint pointParser = do
  _ <- Trifecta.spaces >> Trifecta.char '(' >> Trifecta.spaces
  x <- pointParser
  _ <- Trifecta.spaces >> Trifecta.char ')' >> Trifecta.spaces
  pure x

firstXy :: Trifecta.Parser (Scientific.Scientific, Scientific.Scientific)
firstXy = do
  x <- Wkt.number
  _ <- Trifecta.spaces
  y <- Wkt.number
  pure (x, y)

firstXyz :: Trifecta.Parser (Scientific.Scientific, Scientific.Scientific, Scientific.Scientific)
firstXyz = do
  (x, y) <- firstXy
  _ <- Trifecta.spaces
  z <- Wkt.number
  pure (x, y, z)

justPointsXY :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS
justPointsXY = do
  (x,y) <- firstXy
  let xy = Geospatial.PointXY (Scientific.toRealFloat x) (Scientific.toRealFloat y)
  pure $ Geospatial.GeoPointXY xy

justPointsXYZ :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS
justPointsXYZ = do
  (x,y,z) <- firstXyz
  let xyz = Geospatial.PointXYZ (Scientific.toRealFloat x) (Scientific.toRealFloat y) (Scientific.toRealFloat z)
  pure $ Geospatial.GeoPointXYZ xyz

justPointsXYZM :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS
justPointsXYZM = do
  (x,y,z) <- firstXyz
  _ <- Trifecta.spaces
  m <- Wkt.number
  let xyzm = Geospatial.PointXYZM (Scientific.toRealFloat x) (Scientific.toRealFloat y) (Scientific.toRealFloat z) (Scientific.toRealFloat m)
  pure $ Geospatial.GeoPointXYZM xyzm

emptyPoint :: Geospatial.GeoPoint
emptyPoint = Geospatial.GeoPoint Geospatial.GeoEmpty

emptyMultiPoint :: Geospatial.GeoMultiPoint
emptyMultiPoint = Geospatial.GeoMultiPoint Sequence.empty