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

module Data.Geo.Coordinate.Minutes(
  Minutes
, AsMinutes(..)
, remMinutes
) where

import Control.Applicative(Applicative)
import Control.Category(Category(id))
import Control.Lens(Optic', Choice, prism')
import Data.Bool((&&))
import Data.Maybe(Maybe(Just, Nothing))
import Data.Eq(Eq)
import Data.Int(Int)
import Data.Ord(Ord((>=), (<)))
import Prelude(Show, rem)

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

newtype Minutes =
  Minutes Int
  deriving (Eq, Ord, Show)

class AsMinutes p f s where
  _Minutes ::
    Optic' p f s Minutes

instance AsMinutes p f Minutes where
  _Minutes =
    id

-- | A prism on minutes to an integer between 0 and 59 inclusive.
--
-- >>> (7 :: Int) ^? _Minutes
-- Just (Minutes 7)
--
-- >>> (0 :: Int) ^? _Minutes
-- Just (Minutes 0)
--
-- >>> (59 :: Int) ^? _Minutes
-- Just (Minutes 59)
--
-- >>> (60 :: Int) ^? _Minutes
-- Nothing
--
-- prop> all (\m -> _Minutes # m == (n :: Int)) (n ^? _Minutes)
instance (Choice p, Applicative f) => AsMinutes p f Int where
  _Minutes =
    prism'
      (\(Minutes i) -> i)
      (\i -> if i >= 0 && i < 60
               then Just (Minutes i)
               else Nothing)       

-- | Setting a value `>= 60` will get that value `(`rem` 60)`.
--
-- >>> remMinutes 7
-- Minutes 7
--
-- >>> remMinutes 0
-- Minutes 0
--
-- >>> remMinutes 60
-- Minutes 0
--
-- >>> remMinutes 1
-- Minutes 1
--
-- >>> remMinutes 59
-- Minutes 59 
remMinutes ::
  Int
  -> Minutes
remMinutes x =
  Minutes (x `rem` 60)