{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE 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 (fromUnicodeChar, isInCharRange, toUnicodeChar), 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
  { -- | 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 -> Int
hours :: Int,
    -- | Is 'True' if it is half past the given hour on the 'Clock'.
    Clock -> Bool
minutes30 :: Bool
  }
  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 ::
  -- | The number of hours.
  Int ->
  -- | The number of minutes, must be between 0 and 60.
  Int ->
  -- | The clock object that is the closest to the given hours and minutes.
  Clock
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
h forall 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 ::
  -- | The given hour of the clock, can be any value, but will be set between 1 and 12.
  Int ->
  -- | A 'Bool'ean that indicates if it is half past that hour, so 'True' means we add 30 minutes.
  Bool ->
  -- | A clock object that represents the time that is passed through an hour and .
  Clock
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
h forall 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
h forall 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