{-# LANGUAGE LambdaCase #-}
-- | This module deals with information of a task which is dependent on the status.
module Taskwarrior.Status
  ( Status(..)
  , parseFromObject
  , toPairs
  )
where

import           Taskwarrior.Mask               ( Mask )
import qualified Taskwarrior.Time              as Time
import           Data.Aeson                     ( Object
                                                , (.:)
                                                , (.=)
                                                )
import qualified Data.Aeson                    as Aeson
import           Control.Applicative            ( (<|>) )
import           Data.Text                      ( Text )
import           Data.Time                      ( UTCTime )
import           Data.UUID                      ( UUID )
import           Data.Aeson.Types               ( Parser
                                                , typeMismatch
                                                , Pair
                                                )
-- | A task can be pending, deleted, completed, waiting or recurring.
-- If I task is a recurring child or a recurring parent depends on the existence of the corresponding fields and can not be told from the status field alone.
-- It is recommended to access the fields only by pattern matching since the getters are partial.
data Status =
  Pending |
  Deleted {  end :: UTCTime } |
  Completed {  end :: UTCTime } |
  Waiting { wait :: UTCTime } |
  RecurringParent {
    recur :: Text,
    mask :: Mask} |
  RecurringChild {
    recur :: Text,
    imask :: Integer,
    parent :: UUID }
  deriving (Eq, Show, Read, Ord)

parseFromObject, parseParentFromObject, parseChildFromObject
  :: Object -> Parser Status
-- | Takes all information that is dependent on the status from a JSON object.
parseFromObject o = (o .: "status") >>= \case
  "pending"   -> pure Pending
  "deleted"   -> Deleted <$> (o .: "end" >>= Time.parse)
  "completed" -> Completed <$> (o .: "end" >>= Time.parse)
  "waiting"   -> Waiting <$> (o .: "wait" >>= Time.parse)
  "recurring" -> parseParentFromObject o <|> parseChildFromObject o
  str         -> typeMismatch "status" (Aeson.String str)

-- | Gathers all fields for a RecurringChild status.
parseChildFromObject o =
  RecurringChild <$> o .: "recur" <*> o .: "imask" <*> o .: "parent"

-- | Gathers all fields for a RecurringParent status.
parseParentFromObject o = RecurringParent <$> o .: "recur" <*> o .: "mask"

-- | A list of Pairs can be used to construct a JSON object later. The result of Status.toPairs is supposed to be combined with the rest of the fields of a task.
toPairs :: Status -> [Pair]
toPairs = \case
  Pending        -> [statusLabel "pending"]
  Deleted {..}   -> [statusLabel "deleted", "end" .= Time.toValue end]
  Completed {..} -> [statusLabel "completed", "end" .= Time.toValue end]
  Waiting {..}   -> [statusLabel "waiting", "wait" .= Time.toValue wait]
  RecurringParent {..} ->
    [statusLabel "recurring", "recur" .= recur, "mask" .= mask]
  RecurringChild {..} ->
    [ statusLabel "recurring"
    , "recur" .= recur
    , "imask" .= imask
    , "parent" .= parent
    ]
 where
  statusLabel :: Text -> Pair
  statusLabel = ("status" .=)