-- |

-- Module:      Data.Geo.Jord.Speed

-- Copyright:   (c) 2020 Cedric Liegeois

-- License:     BSD3

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

-- Stability:   experimental

-- Portability: portable

--

-- Types and functions for working with speed in metres per second, kilometres per hour, miles per hour, knots or feet per second.

--

-- In order to use this module you should start with the following imports:

--

-- @

-- import Data.Geo.Jord.Speed (Speed)

-- import qualified Data.Geo.Jord.Speed as Speed

-- @

--

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

      Speed
    -- * Smart constructors

    , average
    , metresPerSecond
    , kilometresPerHour
    , milesPerHour
    , knots
    , feetPerSecond
    -- * Read

    , speed
    , read
    -- * Conversions

    , toMetresPerSecond
    , toKilometresPerHour
    , toMilesPerHour
    , toKnots
    , toFeetPerSecond
    -- * Misc

    , add
    , subtract
    , zero
    ) where

import Control.Applicative ((<|>))
import Prelude hiding (read, subtract)
import Text.ParserCombinators.ReadP (ReadP, pfail, readP_to_S, skipSpaces, string)
import Text.Read (readMaybe)

import Data.Geo.Jord.Duration (Duration)
import qualified Data.Geo.Jord.Duration as Duration (toHours)
import Data.Geo.Jord.Length (Length)
import qualified Data.Geo.Jord.Length as Length (toMillimetres)
import Data.Geo.Jord.Parser

-- | A speed with a resolution of 1 millimetre per hour.

newtype Speed =
    Speed
        { Speed -> Int
mmPerHour :: Int
        }
    deriving (Speed -> Speed -> Bool
(Speed -> Speed -> Bool) -> (Speed -> Speed -> Bool) -> Eq Speed
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Speed -> Speed -> Bool
$c/= :: Speed -> Speed -> Bool
== :: Speed -> Speed -> Bool
$c== :: Speed -> Speed -> Bool
Eq)

-- | See 'speed'.

instance Read Speed where
    readsPrec :: Int -> ReadS Speed
readsPrec Int
_ = ReadP Speed -> ReadS Speed
forall a. ReadP a -> ReadS a
readP_to_S ReadP Speed
speed

-- | Speed is shown in kilometres per hour.

instance Show Speed where
    show :: Speed -> String
show Speed
s = Double -> String
forall a. Show a => a -> String
show (Speed -> Double
toKilometresPerHour Speed
s) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"km/h"

instance Ord Speed where
    <= :: Speed -> Speed -> Bool
(<=) (Speed Int
s1) (Speed Int
s2) = Int
s1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
s2

-- | Adds 2 speeds.

add :: Speed -> Speed -> Speed
add :: Speed -> Speed -> Speed
add Speed
a Speed
b = Int -> Speed
Speed (Speed -> Int
mmPerHour Speed
a Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Speed -> Int
mmPerHour Speed
b)

-- | Subtracts 2 speeds.

subtract :: Speed -> Speed -> Speed
subtract :: Speed -> Speed -> Speed
subtract Speed
a Speed
b = Int -> Speed
Speed (Speed -> Int
mmPerHour Speed
a Int -> Int -> Int
forall a. Num a => a -> a -> a
- Speed -> Int
mmPerHour Speed
b)

-- | 0 speed.

zero :: Speed
zero :: Speed
zero = Int -> Speed
Speed Int
0

-- | 'Speed' from covered distance and duration.

average :: Length -> Duration -> Speed
average :: Length -> Duration -> Speed
average Length
d Duration
t = Int -> Speed
Speed (Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round (Double
mm Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
h))
  where
    mm :: Double
mm = Length -> Double
Length.toMillimetres Length
d
    h :: Double
h = Duration -> Double
Duration.toHours Duration
t

-- | 'Speed' from given amount of metres per second.

metresPerSecond :: Double -> Speed
metresPerSecond :: Double -> Speed
metresPerSecond Double
mps = Int -> Speed
Speed (Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round (Double
mps Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
3600000.0))

-- | 'Speed' from given amount of kilometres per hour.

kilometresPerHour :: Double -> Speed
kilometresPerHour :: Double -> Speed
kilometresPerHour Double
kph = Int -> Speed
Speed (Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round (Double
kph Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
1e+6))

-- | 'Speed' from given amount of miles per hour.

milesPerHour :: Double -> Speed
milesPerHour :: Double -> Speed
milesPerHour Double
mph = Int -> Speed
Speed (Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round (Double
mph Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
1609344.0))

-- | 'Speed' from given amount of knots.

knots :: Double -> Speed
knots :: Double -> Speed
knots Double
kt = Int -> Speed
Speed (Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round (Double
kt Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
1852000.0))

-- | 'Speed' from given amount of feet per second.

feetPerSecond :: Double -> Speed
feetPerSecond :: Double -> Speed
feetPerSecond Double
fps = Int -> Speed
Speed (Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round (Double
fps Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
1097280.0))

-- | Reads a 'Speed' from the given string using 'speed'.

read :: String -> Maybe Speed
read :: String -> Maybe Speed
read String
s = String -> Maybe Speed
forall a. Read a => String -> Maybe a
readMaybe String
s :: (Maybe Speed)

-- | @toMetresPerSecond s@ converts @s@ to metres per second.

toMetresPerSecond :: Speed -> Double
toMetresPerSecond :: Speed -> Double
toMetresPerSecond (Speed Int
s) = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
s Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
3600000.0

-- | @toKilometresPerHour s@ converts @s@ to kilometres per hour.

toKilometresPerHour :: Speed -> Double
toKilometresPerHour :: Speed -> Double
toKilometresPerHour (Speed Int
s) = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
s Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1e+6

-- | @toMilesPerHour s@ converts @s@ to miles per hour.

toMilesPerHour :: Speed -> Double
toMilesPerHour :: Speed -> Double
toMilesPerHour (Speed Int
s) = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
s Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1609344.0

-- | @toKnots s@ converts @s@ to knots.

toKnots :: Speed -> Double
toKnots :: Speed -> Double
toKnots (Speed Int
s) = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
s Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1852000.0

-- | @toFeetPerSecond s@ converts @s@ to feet per second.

toFeetPerSecond :: Speed -> Double
toFeetPerSecond :: Speed -> Double
toFeetPerSecond (Speed Int
s) = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
s Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1097280.0

-- | Parses and returns a 'Speed' formatted as (-)float[m/s|km/h|mph|kt].

-- e.g. 300m/s, 250km/h, -154mph, 400kt or 100ft/s.

speed :: ReadP Speed
speed :: ReadP Speed
speed = do
    Double
s <- ReadP Double
number
    ReadP ()
skipSpaces
    String
u <- String -> ReadP String
string String
"m/s" ReadP String -> ReadP String -> ReadP String
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> String -> ReadP String
string String
"km/h" ReadP String -> ReadP String -> ReadP String
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> String -> ReadP String
string String
"mph" ReadP String -> ReadP String -> ReadP String
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> String -> ReadP String
string String
"kt" ReadP String -> ReadP String -> ReadP String
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> String -> ReadP String
string String
"ft/s"
    case String
u of
        String
"m/s" -> Speed -> ReadP Speed
forall (m :: * -> *) a. Monad m => a -> m a
return (Double -> Speed
metresPerSecond Double
s)
        String
"km/h" -> Speed -> ReadP Speed
forall (m :: * -> *) a. Monad m => a -> m a
return (Double -> Speed
kilometresPerHour Double
s)
        String
"mph" -> Speed -> ReadP Speed
forall (m :: * -> *) a. Monad m => a -> m a
return (Double -> Speed
milesPerHour Double
s)
        String
"kt" -> Speed -> ReadP Speed
forall (m :: * -> *) a. Monad m => a -> m a
return (Double -> Speed
knots Double
s)
        String
"ft/s" -> Speed -> ReadP Speed
forall (m :: * -> *) a. Monad m => a -> m a
return (Double -> Speed
feetPerSecond Double
s)
        String
_ -> ReadP Speed
forall a. ReadP a
pfail