{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveGeneric, OverloadedStrings, ScopedTypeVariables #-}
module Database.PlistBuddy.Types
        ( -- * Remote Monad
          PlistBuddy(..)
        , throwPlistError
        , catchPlistError
        -- * Remote monad handle
        , Plist(..)
        -- * Other types
        , Value(..)
         -- * Exception
        , PlistBuddyException(..)
        , PlistError(..)
        -- * Audit
        , Audit(..)
        , Trail(..)
        , AuditTrail(..)
        ) where

import Control.Concurrent
import Control.Exception
import Control.Monad.Reader
import Control.Monad.Except

import Data.Text(Text)
import Data.ByteString (ByteString)
import Data.IORef

import System.Process
import System.Posix.Pty

import Data.Time

import GHC.Generics

------------------------------------------------------------------------------

-- | The Remote Monad
newtype PlistBuddy a = PlistBuddy (ExceptT PlistError (ReaderT Plist IO) a)
  deriving (Functor,Applicative, Monad, MonadError PlistError, MonadReader Plist, MonadIO)

newtype PlistError = PlistError String 
 deriving (Show, Eq)

-- | A version of `catchError` with the type specialized to PlistBuddy. Using
--   this will cause a static error if used on a non-PlistBuddy monad.

catchPlistError :: PlistBuddy a -> (PlistError -> PlistBuddy a) -> PlistBuddy a
catchPlistError = catchError

-- | Throw a `PlistError`. Uncaught `PlistError` exceptions will
--   be thrown by `send` as IO Exceptions.

throwPlistError :: PlistError -> PlistBuddy a
throwPlistError = throwError

-- | The Remote Plist 
data Plist = Plist 
  { plist_pty   :: Pty
  , plist_lock  :: MVar ()  
  , plist_proc  :: ProcessHandle
  , plist_debug :: Bool
  , plist_file  :: FilePath
  , plist_trail :: Trail -> IO ()      -- audit information
  , plist_launder :: IO ()             -- close audit without issuing an exit; for testing
  , plist_dirty  :: IORef (Maybe Bool) -- if the database has been changed; Nothing == closed
  }
        
------------------------------------------------------------------------------

data Value  = String Text
            | Array [Value]       
            | Dict [(Text,Value)] 
            | Bool Bool
            | Real Double
            | Integer Integer
            | Date UTCTime
            | Data ByteString
        deriving (Show, Read, Generic)

------------------------------------------------------------------------------

-- | 'PlistBuddyException' is for fatal things,
--  like the sub-process blocks, for some reason.
data PlistBuddyException = PlistBuddyException String
    deriving (Show, Generic)

instance Exception PlistBuddyException

------------------------------------------------------------------------------

data AuditTrail = AuditTrail ByteString [Trail] (Maybe ByteString)
   deriving (Show,Read,Generic)  
   
data Audit
 = UTCTime :! Trail 
   deriving (Show,Read,Generic)  
   
data Trail 
  = Save ByteString       -- ^ hash code of saved file
  | Revert
  | Exit
  | Clear Value
  | Set [Text] Value
  | Add [Text] Value
  | Delete [Text]
  | Start ByteString      -- ^ hash code at start of audit capture
    deriving (Show,Read,Generic)