{-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses #-}

-- | For measuring the vertex of angles.
--
-- @
--     Data.Measure.Angle\> 127 ..> degrees <.. radians
--     2.2165681500327987
-- @
--
-- @
--     Data.Measure.Angle\> 2478 ..> mils <.. grads
--     154.875
-- @
--
-- @
--     Data.Measure.Angle\> 24 ..> arcminutes <.. sextants
--     6.666666666666666e-3
-- @
module Data.Measure.Angle(AngleMeasure,
                          rad,
                          radians,
                          degrees,
                          mils,
                          arcminutes,
                          arcseconds,
                          grads,
                          octants,
                          quadrants,
                          sextants,
                          signs,
                          Angle,
                          rad',
                          angle) where

import Data.Measure.RelativeDouble
import Data.Measure.ConvertDouble

-- | A ratio of measurement of an angle.
newtype AngleMeasure = Radian {
  rad :: Double -- ^ The ratio of measurement of an angle in radians.
} deriving (Eq, Ord, Enum, Num, Fractional, Floating)

instance Show AngleMeasure where
  show = show . rad

-- | A measurement of angles.
radians :: AngleMeasure
radians = Radian 1

-- | A measurement of angles in degrees.
degrees :: AngleMeasure
degrees = radians .*. pi / 180

-- | A measurement of angular mils.
mils :: AngleMeasure
mils = radians .*. pi / 3200

-- | A measurement of arcminutes.
arcminutes :: AngleMeasure
arcminutes = degrees ./. 60

-- | A measurement of arcseconds.
arcseconds :: AngleMeasure
arcseconds = arcminutes ./. 60

-- | A measurement of grads.
grads :: AngleMeasure
grads = radians .*. pi / 200

-- | A measurement of octants.
octants :: AngleMeasure
octants = degrees .*. 45

-- | A measurement of quadrants.
quadrants :: AngleMeasure
quadrants = octants .*. 2

-- | A measurement of sextants.
sextants :: AngleMeasure
sextants = degrees .*. 60

-- | A measurement of signs.
signs :: AngleMeasure
signs = degrees .*. 30

-- | A measurement of an angle in radians.
newtype Angle = Angle {
  rad' :: Double -- ^ The measurement of an angle in radians.
} deriving (Eq, Ord, Enum, Num, Fractional, Floating)

-- | Construct an @Angle@ from a floating-point value.
angle :: Double -> Angle
angle = Angle

instance Show Angle where
  show (Angle d) = show d ++ "rad"

instance RelativeDouble AngleMeasure where
  Radian n .*. r = Radian (n * r)
  Radian n ./. r = Radian (n / r)

instance ConvertDouble AngleMeasure Angle where
  x ..> n = Angle (x * rad n)
  n <.. x = rad' n / rad x