```-- |

-- Module:      Data.Geo.Jord.Length

-- Copyright:   (c) 2018 Cedric Liegeois

-- Maintainer:  Cedric Liegeois <ofmooseandmen@yahoo.fr>

-- Stability:   experimental

-- Portability: portable

--

-- Types and functions for working with (signed) lengths in metres, kilometres, nautical miles or feet.

--

module Data.Geo.Jord.Length
(
-- * The 'Length' type

Length
-- * Smart constructors

, feet
, kilometres
, metres
, nauticalMiles

-- * Conversions

, toFeet
, toKilometres
, toMetres
, toNauticalMiles
) where

import Control.Applicative
import Data.Geo.Jord.Parse
import Data.Geo.Jord.Quantity
import Prelude hiding (fail, length)

-- | A length with a resolution of 0.1 millimetre.

newtype Length = Length
{ tenthOfMm :: Int
} deriving (Eq)

-- | Length is shown in metres when absolute value is <= 10,000 m and in kilometres otherwise.

instance Show Length where
show l
| abs m <= 10000.0 = show m ++ "m"
| otherwise = show (m / 1000.0) ++ "km"
where
m = toMetres l

instance Quantity Length where
add a b = Length (tenthOfMm a + tenthOfMm b)
sub a b = Length (tenthOfMm a - tenthOfMm b)
zero = Length 0

-- | 'Length' from given amount of feet.

feet :: Double -> Length
feet ft = Length (round (ft * 3048.0))

-- | 'Length' from given amount of kilometres.

kilometres :: Double -> Length
kilometres km = Length (round (km * 10000000.0))

-- | 'Length' from given amount of metres.

metres :: Double -> Length
metres m = Length (round (m * 10000.0))

-- | 'Length' from given amount of nautical miles.

nauticalMiles :: Double -> Length
nauticalMiles nm = Length (round (nm * 18520000.0))

-- | Obtains a 'Length' from the given string formatted as (-)float[m|km|nm|ft] - e.g. 3000m, 2.5km, -154nm or 10000ft.

--

-- This simply calls @read s :: Length@ so 'error' should be handled at the call site.

--

-- | Same as 'readLength' but returns a 'Either'.

readLengthE :: String -> Either String Length
Nothing -> Left ("couldn't read length " ++ s)
Just l -> Right l

in case p of
Left e -> fail e
Right l -> return l

-- | @toFeet l@ converts @l@ to feet.

toFeet :: Length -> Double
toFeet (Length l) = fromIntegral l / 3048.0

-- | @toKilometres l@ converts @l@ to kilometres.

toKilometres :: Length -> Double
toKilometres (Length l) = fromIntegral l / 10000000.0

-- | @toMetres l@ converts @l@ to metres.

toMetres :: Length -> Double
toMetres (Length l) = fromIntegral l / 10000.0

-- | @toNauticalMiles l@ converts @l@ to nautical miles.

toNauticalMiles :: Length -> Double
toNauticalMiles (Length l) = fromIntegral l / 18520000.0

-- | Parses and returns a 'Length'.