{-# LANGUAGE DeriveDataTypeable, DeriveGeneric, OverloadedStrings, Safe, TypeApplications #-}

{-|
Module      : Data.Char.Emoji.Clock
Description : A module that defines emoji that display a clock.
Maintainer  : hapytexeu+gh@gmail.com
Stability   : experimental
Portability : POSIX

The emoji have 24 clock emoji, each time with 30 minutes difference.
-}

module Data.Char.Emoji.Clock (
    -- * Clock emoji
    Clock, hours, minutes30, clock, closestClock
  ) where

import Control.DeepSeq(NFData)

import Data.Bits((.|.), shiftL, shiftR)
import Data.Bool(bool)
import Data.Char(chr, ord)
import Data.Char.Core(UnicodeCharacter(toUnicodeChar, fromUnicodeChar, isInCharRange), UnicodeText(isInTextRange), generateIsInTextRange')
import Data.Data(Data)
import Data.Hashable(Hashable)

import GHC.Enum(toEnumError)
import GHC.Generics(Generic)

import Test.QuickCheck.Arbitrary(Arbitrary(arbitrary), arbitraryBoundedEnum)

-- | A 'Clock' object that can be converted to a unicode character that displays
-- a clock with the given time. The 'Clock' has an 'hours' field that contains
-- the given hours between 0 and 12, and a 'minutes30' field that if 'True',
-- means that the clock is half past that hour.
data Clock = Clock {
    Clock -> Int
hours :: Int  -- ^ The number of hours on the given clock. Is between 0 and 12. For 0, the 'minutes30' is 'True'; and for 12, the 'minutes30' is 'False'.
  , Clock -> Bool
minutes30 :: Bool  -- ^ Is 'True' if it is half past the given hour on the 'Clock'.
  } deriving (Typeable Clock
Clock -> DataType
Clock -> Constr
(forall b. Data b => b -> b) -> Clock -> Clock
forall a.
Typeable a
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> Clock -> u
forall u. (forall d. Data d => d -> u) -> Clock -> [u]
forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Clock -> r
forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Clock -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Clock -> m Clock
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Clock -> m Clock
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Clock
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Clock -> c Clock
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Clock)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Clock)
gmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Clock -> m Clock
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Clock -> m Clock
gmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Clock -> m Clock
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Clock -> m Clock
gmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Clock -> m Clock
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Clock -> m Clock
gmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Clock -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Clock -> u
gmapQ :: forall u. (forall d. Data d => d -> u) -> Clock -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> Clock -> [u]
gmapQr :: forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Clock -> r
$cgmapQr :: forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Clock -> r
gmapQl :: forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Clock -> r
$cgmapQl :: forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Clock -> r
gmapT :: (forall b. Data b => b -> b) -> Clock -> Clock
$cgmapT :: (forall b. Data b => b -> b) -> Clock -> Clock
dataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Clock)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Clock)
dataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Clock)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Clock)
dataTypeOf :: Clock -> DataType
$cdataTypeOf :: Clock -> DataType
toConstr :: Clock -> Constr
$ctoConstr :: Clock -> Constr
gunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Clock
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Clock
gfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Clock -> c Clock
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Clock -> c Clock
Data, Clock -> Clock -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Clock -> Clock -> Bool
$c/= :: Clock -> Clock -> Bool
== :: Clock -> Clock -> Bool
$c== :: Clock -> Clock -> Bool
Eq, forall x. Rep Clock x -> Clock
forall x. Clock -> Rep Clock x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Clock x -> Clock
$cfrom :: forall x. Clock -> Rep Clock x
Generic, Eq Clock
Clock -> Clock -> Bool
Clock -> Clock -> Ordering
Clock -> Clock -> Clock
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Clock -> Clock -> Clock
$cmin :: Clock -> Clock -> Clock
max :: Clock -> Clock -> Clock
$cmax :: Clock -> Clock -> Clock
>= :: Clock -> Clock -> Bool
$c>= :: Clock -> Clock -> Bool
> :: Clock -> Clock -> Bool
$c> :: Clock -> Clock -> Bool
<= :: Clock -> Clock -> Bool
$c<= :: Clock -> Clock -> Bool
< :: Clock -> Clock -> Bool
$c< :: Clock -> Clock -> Bool
compare :: Clock -> Clock -> Ordering
$ccompare :: Clock -> Clock -> Ordering
Ord, ReadPrec [Clock]
ReadPrec Clock
Int -> ReadS Clock
ReadS [Clock]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Clock]
$creadListPrec :: ReadPrec [Clock]
readPrec :: ReadPrec Clock
$creadPrec :: ReadPrec Clock
readList :: ReadS [Clock]
$creadList :: ReadS [Clock]
readsPrec :: Int -> ReadS Clock
$creadsPrec :: Int -> ReadS Clock
Read, Int -> Clock -> ShowS
[Clock] -> ShowS
Clock -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Clock] -> ShowS
$cshowList :: [Clock] -> ShowS
show :: Clock -> String
$cshow :: Clock -> String
showsPrec :: Int -> Clock -> ShowS
$cshowsPrec :: Int -> Clock -> ShowS
Show)

instance Hashable Clock

instance NFData Clock

instance Bounded Clock where
    minBound :: Clock
minBound = Int -> Bool -> Clock
Clock Int
0 Bool
True
    maxBound :: Clock
maxBound = Int -> Bool -> Clock
Clock Int
12 Bool
False

instance Enum Clock where
    fromEnum :: Clock -> Int
fromEnum (Clock Int
h Bool
m30) = forall a. Enum a => a -> a
pred (forall a. Bits a => a -> Int -> a
shiftL Int
h Int
1 forall a. Bits a => a -> a -> a
.|. forall a. a -> a -> Bool -> a
bool Int
0 Int
1 Bool
m30)
    toEnum :: Int -> Clock
toEnum Int
hm30
        | Int
hm30 forall a. Ord a => a -> a -> Bool
< Int
0 Bool -> Bool -> Bool
|| Int
hm30 forall a. Ord a => a -> a -> Bool
> Int
23 = forall a b. Show a => String -> Int -> (a, a) -> b
toEnumError String
"Clock" Int
hm30 (forall a. Bounded a => a
minBound :: Clock, forall a. Bounded a => a
maxBound)
        | Bool
otherwise = Int -> Bool -> Clock
Clock (forall a. Bits a => a -> Int -> a
shiftR Int
hm30' Int
1) (forall a. Integral a => a -> Bool
odd Int
hm30')
        where hm30' :: Int
hm30' = forall a. Enum a => a -> a
succ Int
hm30
    enumFrom :: Clock -> [Clock]
enumFrom = (forall a. Enum a => a -> a -> [a]
`enumFromTo` forall a. Bounded a => a
maxBound)
    enumFromThen :: Clock -> Clock -> [Clock]
enumFromThen Clock
x Clock
y = forall a. Enum a => a -> a -> a -> [a]
enumFromThenTo Clock
x Clock
y forall a. Bounded a => a
maxBound

-- | Generate the 'Clock' object that is the closest to the given hours and
-- minutes.
closestClock
  :: Int  -- ^ The number of hours.
  -> Int  -- ^ The number of minutes, must be between 0 and 60.
  -> Clock  -- ^ The clock object that is the closest to the given hours and minutes.
closestClock :: Int -> Int -> Clock
closestClock Int
h Int
m
    | Int
m forall a. Ord a => a -> a -> Bool
< Int
15 = Int -> Bool -> Clock
clock Int
h Bool
False
    | Int
m forall a. Ord a => a -> a -> Bool
< Int
45 = Int -> Bool -> Clock
clock Int
h Bool
True
    | Bool
otherwise = Int -> Bool -> Clock
clock (Int
hforall a. Num a => a -> a -> a
+Int
1) Bool
False

-- | Construct a 'Clock' object with the given number of hours, and a 'Bool'ean
-- that indicates if it is half past that hour.
-- The function will ensure that the hours are between 0 and 12 (both inclusive).
-- For half past 12, we use half past 0, for 12 hours, we use simply 12.
clock
  :: Int  -- ^ The given hour of the clock, can be any value, but will be set between 1 and 12.
  -> Bool  -- ^ A 'Bool'ean that indicates if it is half past that hour, so 'True' means we add 30 minutes.
  -> Clock  -- ^ A clock object that represents the time that is passed through an hour and .
clock :: Int -> Bool -> Clock
clock Int
h Bool
b
    | Bool
b Bool -> Bool -> Bool
&& Int
h' forall a. Eq a => a -> a -> Bool
== Int
12 = Int -> Bool -> Clock
Clock Int
0 Bool
True
    | Bool
otherwise = Int -> Bool -> Clock
Clock Int
h' Bool
b
    where h' :: Int
h' = forall a. Integral a => a -> a -> a
mod (Int
hforall a. Num a => a -> a -> a
-Int
1) Int
12 forall a. Num a => a -> a -> a
+ Int
1

instance Arbitrary Clock where
    arbitrary :: Gen Clock
arbitrary = forall a. (Bounded a, Enum a) => Gen a
arbitraryBoundedEnum

instance UnicodeCharacter Clock where
    toUnicodeChar :: Clock -> Char
toUnicodeChar (Clock Int
h Bool
False) = Int -> Char
chr (Int
0x1f54f forall a. Num a => a -> a -> a
+ Int
h)
    toUnicodeChar (Clock Int
h Bool
True) = Int -> Char
chr (Int
0x1f55c forall a. Num a => a -> a -> a
+ forall a. Integral a => a -> a -> a
mod (Int
hforall a. Num a => a -> a -> a
-Int
1) Int
12)
    fromUnicodeChar :: Char -> Maybe Clock
fromUnicodeChar Char
c
        | Char
c forall a. Ord a => a -> a -> Bool
< Char
'\x1f550' = forall a. Maybe a
Nothing
        | Char
c forall a. Ord a => a -> a -> Bool
< Char
'\x1f55c' = forall a. a -> Maybe a
Just (Int -> Bool -> Clock
Clock (Char -> Int
ord Char
c forall a. Num a => a -> a -> a
- Int
0x1f54f) Bool
False)
        | Char
c forall a. Ord a => a -> a -> Bool
< Char
'\x1f568' = forall a. a -> Maybe a
Just (Int -> Bool -> Clock
Clock (forall a. Integral a => a -> a -> a
mod (Char -> Int
ord Char
c forall a. Num a => a -> a -> a
- Int
0x1f55b) Int
12) Bool
True)
        | Bool
otherwise = forall a. Maybe a
Nothing
    isInCharRange :: Char -> Bool
isInCharRange Char
c = Char
'\x1f550' forall a. Ord a => a -> a -> Bool
<= Char
c Bool -> Bool -> Bool
&& Char
c forall a. Ord a => a -> a -> Bool
<= Char
'\x1f567'

instance UnicodeText Clock where
  isInTextRange :: Text -> Bool
isInTextRange = forall a. UnicodeCharacter a => Text -> Bool
generateIsInTextRange' @Clock