{-# LANGUAGE DeriveGeneric #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Network.Transport
-- Copyright   :  (c) Phil Hargett 2013
-- License     :  MIT (see LICENSE file)
-- 
-- Maintainer  :  phil@haphazardhouse.net
-- Stability   :  experimental
-- Portability :  non-portable (requires STM)
--
-- A 'Transport' abstracts the details of message delivery, and defines the interfaces
-- that specific 'Transport' implementations should provide in order to deliver messages
-- for 'Endpoint's.
--
-----------------------------------------------------------------------------

module Network.Transport (
  Address,
  Binding(..),
  Envelope(..),
  Message,
  Name,
  Resolver,
  resolve,
  resolverFromList,
  Scheme,
  Transport(..),

  module Control.Concurrent.Mailbox
  
  ) where

-- local imports
import Control.Concurrent.Mailbox

-- external imports

import Data.ByteString as B
import Data.Serialize

import GHC.Generics

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

{-|
Messages are containers for arbitrary data that may be sent to other 'Network.Endpoints.Endpoint's.
-}
type Message = B.ByteString

{-|
Name for uniquely identifying an 'Endpoint'; suitable for identifying
the target destination for a 'Message'.

-}
type Name = String

{-|
An 'Envelope' is a container for a 'Message' with the 'Address' of the 'Message''s destination.

-}
data Envelope = Envelope {
  envelopeDestination :: Name,
  envelopeContents :: Message
  } deriving (Eq,Show,Generic)

instance Serialize Envelope

{-|
A 'Mailbox' is a place where transports can put messages for 'Network.Endpoint.Endpoint's
to receive.  Typically 'Network.Endpoint.Endpoint's will use the same 'Mailbox' when
binding or connecting with a 'Transport'.
-}

{-|
An address is a logical identifier suitable for establishing a connection to
another 'Endpoint' over a 'Transport'. It's use (if at all) is specific to the 'Transport'
in question.
-}
type Address = String

{-|
A scheme is an identifier for a discrete type of transport.
-}
type Scheme = String

{-|
Bindings are a site for receiving messages on a particular 'Address'
through a 'Transport'.
-}
data Binding = Binding {
  bindingName :: Name,
  unbind :: IO ()
  }

{-|
A 'Transport' defines a specific method for establishing connections
between 'Endpoint's.
-}
data Transport = Transport {
  scheme :: String,
  handles :: Name -> IO Bool,
  bind :: Mailbox Message -> Name -> IO (Either String Binding),
  sendTo :: Name -> Message -> IO (),
  shutdown :: IO ()
  }

{-|
A 'Resolver' translates a name into an 'Address', if possible. 
'Transport's may find resolvers useful for determing
where to reach a specific 'Endpoint', given it''s 'Name'.
-}
newtype Resolver = Resolver (Name -> IO (Maybe Address))

{-|
Ask the 'Resolver' to find one or more 'Address'es for the provided
'Name', if any are available from this resolver.

-}
resolve :: Resolver -> Name -> IO (Maybe Address)
resolve (Resolver resolver) name = resolver name

{-|
A simple 'Resolver' that accepts an association list of 'Name's to 'Address'es
and returns the addresses associated with a given name in the list.
-}
resolverFromList :: [(Name,Address)] -> Resolver
resolverFromList addresses = Resolver (\name -> return $ lookup name addresses)