{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveFunctor #-}
{-
Bustle.Types: defines types used by Bustle
Copyright (C) 2008 Collabora Ltd.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-}
module Bustle.Types
  ( ObjectPath
  , formatObjectPath

  , InterfaceName
  , formatInterfaceName

  , MemberName
  , formatMemberName

  , Serial

  , UniqueName(..)
  , OtherName(..)
  , TaggedBusName(..)
  , isUnique
  , isOther
  , unUniqueName
  , unOtherName
  , unBusName

  , dbusName
  , dbusInterface

  , fakeUniqueName

  , Microseconds(..)
  , µsFromPair
  , µsToMs

  , Member(..)
  , Message(..)
  , NOC(..)
  , Event(..)
  , Detailed(..)
  , DetailedEvent
  , Change(..)
  , partitionDetaileds
  , mentionedNames
  , Log
  )
where

import Data.Word (Word32)
import DBus ( ObjectPath, formatObjectPath
            , InterfaceName, formatInterfaceName, interfaceName_
            , MemberName, formatMemberName
            , BusName, formatBusName, busName_
            , ReceivedMessage
            )
import Data.Maybe (maybeToList)
import Data.Either (partitionEithers)

type Serial = Word32

newtype UniqueName = UniqueName BusName
  deriving (Ord, Show, Eq)
newtype OtherName = OtherName BusName
  deriving (Ord, Show, Eq)
data TaggedBusName =
    U UniqueName
  | O OtherName
  deriving (Ord, Show, Eq)

isUnique, isOther :: TaggedBusName -> Bool
isUnique (U _) = True
isUnique (O _) = False
isOther = not . isUnique

unUniqueName :: UniqueName -> String
unUniqueName (UniqueName x) = formatBusName x

unOtherName :: OtherName -> String
unOtherName (OtherName x) = formatBusName x

unBusName :: TaggedBusName -> String
unBusName (U (UniqueName x)) = formatBusName x
unBusName (O (OtherName  x)) = formatBusName x

-- These useful constants disappeared from dbus in the grand removing of the
-- -core suffix.
dbusName :: BusName
dbusName = busName_ "org.freedesktop.DBus"

dbusInterface :: InterfaceName
dbusInterface = interfaceName_ "org.freedesktop.DBus"

-- FIXME: nothing stops someone passing in garbage
-- http://www.youtube.com/watch?v=WorPANO_ANU
fakeUniqueName :: String
               -> UniqueName
fakeUniqueName = UniqueName . busName_ . (":fake." ++)

newtype Microseconds = Microseconds Integer
  deriving (Show, Ord, Eq, Num, Real, Enum, Integral)

µsFromPair :: Integer
           -> Integer
           -> Microseconds
µsFromPair s µs = Microseconds $ (s * 1000000) + µs

µsToMs :: Microseconds
       -> Integer
µsToMs (Microseconds µs) = µs `div` 1000

data Member = Member { path :: ObjectPath
                     , iface :: Maybe InterfaceName
                     , membername :: MemberName
                     }
  deriving (Ord, Show, Eq)

data Event = MessageEvent Message
           | NOCEvent NOC
  deriving (Show, Eq, Ord)

data Message = MethodCall { serial :: Serial
                          , sender :: TaggedBusName
                          , destination :: TaggedBusName
                          , member :: Member
                          }
             | MethodReturn { inReplyTo :: Maybe (Detailed Message)
                            , sender :: TaggedBusName
                            , destination :: TaggedBusName
                            }
             | Signal { sender :: TaggedBusName
                      , signalDestination :: Maybe TaggedBusName
                      , member :: Member
                      }
             | Error { inReplyTo :: Maybe (Detailed Message)
                     , sender :: TaggedBusName
                     , destination :: TaggedBusName
                     }
  deriving (Show, Eq, Ord)

data NOC = Connected { actor :: UniqueName
                     }
         | Disconnected { actor :: UniqueName
                        }
         | NameChanged { changedName :: OtherName
                       , change :: Change
                       }
  deriving (Show, Eq, Ord)

type MessageSize = Int

data Detailed e =
    Detailed { deTimestamp :: Microseconds
             , deEvent :: e
             , deDetails :: Maybe (MessageSize, ReceivedMessage)
             }
  deriving (Show, Eq, Functor)

type DetailedEvent = Detailed Event

instance Ord e => Ord (Detailed e) where
    compare (Detailed µs x _) (Detailed µs' y _)
        = compare (µs, x) (µs', y)

data Change = Claimed UniqueName
            | Stolen UniqueName UniqueName
            | Released UniqueName
  deriving (Show, Eq, Ord)

partitionDetaileds :: [DetailedEvent]
                   -> ([Detailed NOC], [Detailed Message])
partitionDetaileds = partitionEithers . map f
  where
    f (Detailed µs e details) =
        case e of
            NOCEvent n -> Left $ Detailed µs n details
            MessageEvent m -> Right $ Detailed µs m details

mentionedNames :: Message -> [TaggedBusName]
mentionedNames m = sender m:dest
  where
    dest = case m of
        Signal {} -> maybeToList $ signalDestination m
        _         -> [destination m]

type Log = [DetailedEvent]

-- vim: sw=2 sts=2
