module Data.Geo.WKT.Parser where

import Data.Geo.WKT.Types
import Linear hiding (unit)
import Control.Applicative
import Control.Monad (ap)
import Text.ParserCombinators.Parsec hiding (many)
import Text.ParserCombinators.Parsec.Number hiding (number)

object :: String -> Parser a -> Parser a
object keyword parser = do
    string keyword
    between (char '[') (char ']') parser
   
quotedString :: Parser String
quotedString = do
    char '"'
    manyTill anyChar (char '"')

fieldSep :: Parser ()
fieldSep = char ',' >> spaces

number :: Parser Double
number = sign `ap` floating2 True

twinAxes = do
    ax1 <- axis
    fieldSep 
    ax2 <- axis
    fieldSep
    return (ax1, ax2)

unit :: Parser Unit
unit = object "UNIT" $ do
    name <- quotedString
    fieldSep
    conv <- number
    auth <- optionMaybe $ fieldSep *> authority
    return $ Unit name conv auth
    
parameter :: Parser Parameter
parameter = object "PARAMETER" $ do
    name <- quotedString
    fieldSep
    value <- number
    return $ Parameter name value 

authority :: Parser Authority
authority = object "AUTHORITY" $ do
    name <- quotedString
    fieldSep
    code <- quotedString
    return $ Authority name code

axis :: Parser Axis
axis = object "AXIS" $ do
    name <- quotedString
    fieldSep
    dir <- choice [ string "NORTH" >> return North
                  , string "SOUTH" >> return South
                  , string "EAST"  >> return East
                  , string "WEST"  >> return West
                  , string "UP"    >> return Up
                  , string "DOWN"  >> return Down
                  , string "OTHER" >> return Other
                  ]
    return $ Axis name dir

projection :: Parser Projection
projection = object "PROJECTION" $ do
    name <- quotedString
    auth <- optionMaybe $ fieldSep *> authority
    return $ Proj name auth

spheroid :: Parser Spheroid
spheroid = object "SPHEROID" $ do
    name <- quotedString
    fieldSep
    semiMajor <- number
    fieldSep
    invFlat <- number
    auth <- optionMaybe $ fieldSep *> authority
    return $ Spheroid name semiMajor invFlat auth
     
datum :: Parser Datum
datum = object "DATUM" $ do             
    name <- quotedString
    fieldSep
    s <- spheroid
    wgs <- optionMaybe $ fieldSep *> toWGS84
    auth <- optionMaybe $ fieldSep *> authority
    return $ Datum name s wgs auth

primeMeridian :: Parser PrimeMeridian
primeMeridian = object "PRIMEM" $ do
    name <- quotedString
    fieldSep
    long <- number
    auth <- optionMaybe $ fieldSep *> authority
    return $ PrimeMeridian name long auth
    
toWGS84 :: Parser ToWGS84
toWGS84 = object "TOWGS84" $ do
    d <- V3 <$> number <*> number <*> number        
    e <- V3 <$> number <*> number <*> number        
    ppm <- number
    return $ ToWGS84 d e ppm

projectedCS :: Parser ProjectedCS
projectedCS = object "PROJCS" $ do
    name <- quotedString
    fieldSep
    geogcs <- geographicCS
    fieldSep
    proj <- projection
    fieldSep
    params <- many $ parameter <* fieldSep
    linearUnit <- unit
    axes <- optionMaybe $ fieldSep *> twinAxes
    auth <- optionMaybe $ fieldSep *> authority
    return $ ProjCS name geogcs proj params linearUnit axes auth
    
geographicCS :: Parser GeographicCS
geographicCS = object "GEOGCS" $ do
    name <- quotedString
    fieldSep
    dat <- datum
    fieldSep
    primem <- primeMeridian
    fieldSep
    angularUnit <- unit
    axes <- optionMaybe $ fieldSep *> twinAxes
    auth <- optionMaybe $ fieldSep *> authority
    return $ GeogCS name dat primem angularUnit axes auth