{-|
Module      : GossipTypes
Description : Implements different utilitarian types used for dynamic epistemic gossip, together with some operations thereon.
Copyright   : (c) Jesper Kuiper, 2021
                  Leander van Boven, 2021
                  Ramon Meffert, 2021
License     : BSD3
-}
module GossipTypes 
  ( -- * Type definitions
    -- ** Agents
    Agent
  , AgentId
  , AgentName
    -- ** Calls 
  , Call
  , GroupCall
    -- * Call generation
  , (☎)
  , toCalls
    -- * Showables and printables
    -- ** Agents
  , showAgent
    -- ** Calls
  , printCall
  , printCalls
  , printMakeCall
    -- * Group calls
  , printGroupCall
  , printGroupCalls
  , printMakeGroupCall
    -- ** General
  , printNoCalls
  , printAllCalls
  )

where

import Control.Monad
import Data.Graph.Inductive (Gr, LEdge, LNode, prettyPrint, Graph (noNodes))
import Data.List
import System.Console.ANSI

import Util

-- | The type of an agent. Agents are always defined in terms of a character and a corresponding (unique) identifying integer.
type Agent = LNode Char

-- | The agent identifier.
type AgentId = Int

-- | The agent name. 
type AgentName = Char

-- | A call between two agents, defined as (from/caller, to/callee).
-- A call is considered to be bidirectional, i.e. information flows from the caller to the callee and vice versa. 
type Call = (Agent, Agent)

-- | A call between a group of agents. The first item in the tuple is the caller, they need to have the phone number of all callees in the list. A group call is also omnidirectional, i.e. the all agent information is pooled and distributed to all agents in the call.  
type GroupCall = (Agent, [Agent])

-- | Generates a call between two agents. 
(☎) :: Agent -> Agent -> Call
☎ :: Agent -> Agent -> Call
(☎) Agent
f Agent
t = (Agent
f,Agent
t)
infix 0 

-- | Converts a group call to a list of direct calls. This results in an identical flow of information. 
toCalls :: GroupCall -> [Call]
toCalls :: GroupCall -> [Call]
toCalls g :: GroupCall
g@(Agent
f, [Agent]
to) = [(Agent
f,Agent
t) | Agent
t <- [Agent]
to] [Call] -> [Call] -> [Call]
forall a. [a] -> [a] -> [a]
++ [(Agent
t1,Agent
t2) | Agent
t1 <- [Agent]
to, Agent
t2 <- [Agent]
to, Agent
t1 Agent -> Agent -> Bool
forall a. Eq a => a -> a -> Bool
/= Agent
t2]

-- | Converts an agent to a print-friendly string. 
showAgent :: Agent -> String
showAgent :: Agent -> String
showAgent (Node
_, Char
lab) = [Char
lab]

-- | Prints a message that no calls are available. 
printNoCalls :: IO ()
printNoCalls :: IO ()
printNoCalls = do
  String -> IO ()
putStr String
"["
  Color -> String -> IO ()
putStrFgc Color
Red String
"No calls to display"
  String -> IO ()
putStrLn String
"]"

-- | Prints a call. 
printCall :: Call -> IO ()
printCall :: Call -> IO ()
printCall ((Node
_, Char
i), (Node
_, Char
j)) = do
    String -> IO ()
putStr (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
" (" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Char -> String
forall a. Show a => a -> String
show Char
i String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"," String -> String -> String
forall a. [a] -> [a] -> [a]
++ Char -> String
forall a. Show a => a -> String
show Char
j String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
") "

-- | Prints a message that a call is made between two agents. 
printMakeCall :: Call -> IO ()
printMakeCall :: Call -> IO ()
printMakeCall ((Node
_,Char
m), (Node
_,Char
n)) = do
    String -> IO ()
putStr String
"\nMaking call between "
    Color -> String -> IO ()
putStrFgc Color
Green (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ Char -> String
forall a. Show a => a -> String
show Char
m
    String -> IO ()
putStr String
" and "
    Color -> String -> IO ()
putStrLnFgc Color
Green (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ Char -> String
forall a. Show a => a -> String
show Char
n

-- | Prints a list of calls. 
printCalls :: [Call] -> IO ()
printCalls :: [Call] -> IO ()
printCalls [] = IO ()
printNoCalls
printCalls [Call]
c = (Call -> IO ()) -> [Call] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Call -> IO ()
printCall [Call]
c

-- | Prints a group call. 
printGroupCall :: GroupCall -> IO ()
printGroupCall :: GroupCall -> IO ()
printGroupCall GroupCall
g = do
  Color -> String -> IO ()
putStrFgc Color
Green String
"  Groupcall with calls: "
  (Call -> IO ()) -> [Call] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Call -> IO ()
printCall (GroupCall -> [Call]
toCalls GroupCall
g)
  String -> IO ()
putStr String
"\n"

-- | Prints a message that a group call is made. 
printMakeGroupCall :: GroupCall -> IO ()
printMakeGroupCall :: GroupCall -> IO ()
printMakeGroupCall ((Node
_,Char
f), [Agent]
to) = do
  String -> IO ()
putStr String
"\nMaking group call initialised by "
  Color -> String -> IO ()
putStrFgc Color
Green [Char
f]
  String -> IO ()
putStr String
" to: "
  Color -> String -> IO ()
putStrLnFgc Color
Green (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ Char -> String -> String
forall a. a -> [a] -> [a]
intersperse Char
' ' (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ (Agent -> Char) -> [Agent] -> String
forall a b. (a -> b) -> [a] -> [b]
map Agent -> Char
forall a b. (a, b) -> b
snd [Agent]
to

-- | Prints a list of group calls. 
printGroupCalls :: [GroupCall] -> IO ()
printGroupCalls :: [GroupCall] -> IO ()
printGroupCalls [] = IO ()
printNoCalls
printGroupCalls [GroupCall]
g = (GroupCall -> IO ()) -> [GroupCall] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ GroupCall -> IO ()
printGroupCall [GroupCall]
g

-- | Prints all calls, given a list of direct calls and group calls. 
printAllCalls :: ([Call], [GroupCall]) -> IO ()
printAllCalls :: ([Call], [GroupCall]) -> IO ()
printAllCalls ([Call]
c, [GroupCall]
g) = do
  Color -> String -> IO ()
putStrLnFgc Color
Yellow String
"Direct calls:"
  [Call] -> IO ()
printCalls [Call]
c
  String -> IO ()
putStr String
"\n"
  Color -> String -> IO ()
putStrLnFgc Color
Yellow String
"Group calls:"
  [GroupCall] -> IO ()
printGroupCalls [GroupCall]
g