{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}


-- | A sphere with a radius in metres.
module Data.Geo.Geodetic.Sphere(
  Sphere
, AsSphere(..)
, earthMean
) where

import Control.Category(Category(id))
import Control.Lens(Optic', Profunctor, iso)
import Data.Eq(Eq)
import Data.Functor(Functor)
import Data.List((++))
import Data.Ord(Ord(..))
import Text.Printf(printf)
import Prelude(Double, Show(showsPrec), showParen, showString)

-- $setup
-- >>> import Control.Lens((#), (^.))
-- >>> import Data.Foldable(all)
-- >>> import Prelude(Eq((==)))

newtype Sphere =
  Sphere Double
  deriving (Eq, Ord)

-- | A show instance that prints to 4 decimal places.
-- This is to take floating-point rounding errors into account.
instance Show Sphere where
  showsPrec n (Sphere d) =
    showParen (n > 10) (showString ("Sphere " ++ printf "%0.4f" d))

earthMean ::
  Sphere
earthMean =
  Sphere 6367450

class AsSphere p f s where
  _Sphere ::
    Optic' p f s Sphere

instance AsSphere p f Sphere where
  _Sphere =
    id

-- | An isomorphism on sphere to a double.
--
-- >>> (7 :: Double) ^. _Sphere
-- Sphere 7.0000
--
-- >>> (0 :: Double) ^. _Sphere
-- Sphere 0.0000
--
-- >>> (-7 :: Double) ^. _Sphere
-- Sphere -7.0000
instance (Functor f, Profunctor p) => AsSphere p f Double where
  _Sphere =
    iso 
      Sphere
      (\(Sphere d) -> d)