{-# LANGUAGE CPP #-}
module DMS where

#if __GLASGOW_HASKELL__ < 710
import Control.Applicative
#endif

import Control.Monad.Except
import Datum
import LatLng

data LatitudeDMS = North DMS -- ^ Latitude is north of the equator.
                   | South DMS -- ^ Latitude is south of the equator.

data LongitudeDMS = East DMS -- ^ Longitude is east of the prime meridian.
                    | West DMS -- ^ Longitude is west of the prime meridian.

data DMS = DMS { degrees :: Double
               , minutes :: Double
               , seconds :: Double
               }


toLatLng :: LatitudeDMS -> LongitudeDMS -> Datum -> Except String LatLng
toLatLng lat lng dtm = do
  lt <- withExcept (const "Invalid latitude") (evalLatitude lat)
  ln <- withExcept (const "Invalid longitude") (evalLongitude lng)
  pure LatLng { latitude = lt, longitude = ln, height = 0, datum = dtm }
  where evalLatitude :: LatitudeDMS -> Except String Double
        evalLatitude (North p) = dmsToLatLngPoint p 1
        evalLatitude (South p) = dmsToLatLngPoint p (-1)

        evalLongitude :: LongitudeDMS -> Except String Double
        evalLongitude (East p) = dmsToLatLngPoint p 1
        evalLongitude (West p) = dmsToLatLngPoint p (-1)

        dmsToLatLngPoint :: DMS -> Double -> Except String Double
        dmsToLatLngPoint DMS { degrees = d, minutes = m, seconds = s } cardinal
          | d < 0.0 || m < 0.0 || s < 0.0 || d > 90.0 || m >= 60.0 || s >= 60.0 = throwError "Invalid point"
          | otherwise = pure (cardinal * (d + m / 60.0 + s / 3600.0))