{-|
Module      : Game.Werewolf.Response
Description : Response and message data structures.

Copyright   : (c) Henry J. Wylde, 2016
License     : BSD3
Maintainer  : public@hjwylde.com

A response is used as a return result of calling the @werewolf@ binary. Each response has a list of
associated messages.

@werewolf@ was designed to be ambivalent to the playing chat client. The response-message structure
reflects this by staying away from anything that could be construed as client-specific. This
includes features such as emoji support.
-}

{-# LANGUAGE CPP           #-}
{-# LANGUAGE DeriveGeneric #-}

module Game.Werewolf.Response (
    -- * Response
    Response(..),

    -- ** Common responses
    success, failure,

    -- ** Exit functions
    exitWith,

    -- * Message
    Message(..),
    publicMessage, privateMessage, groupMessages,
) where

import Control.Monad.IO.Class

import Data.Aeson
#if !MIN_VERSION_aeson(0,10,0)
import Data.Aeson.Types
#endif
import           Data.Text               (Text)
import qualified Data.Text.Lazy.Encoding as T
import qualified Data.Text.Lazy.IO       as T

import GHC.Generics

import qualified System.Exit as Exit

-- | When a user sends a command to the @werewolf@ binary, a response is always returned.
--
--   The chat interface should then relay any @messages@ from the response. Whether or not the
--   command was valid (indicated by the @ok@ flag) is often irrelevant as the returned @messages@
--   will include errors to the user.
data Response = Response
    { ok       :: Bool      -- ^ Boolean flag to indicate success.
    , messages :: [Message] -- ^ List of messages.
    } deriving (Eq, Generic, Show)

instance FromJSON Response

instance ToJSON Response where
    toJSON      = genericToJSON defaultOptions
#if MIN_VERSION_aeson(0,10,0)
    toEncoding  = genericToEncoding defaultOptions
#endif

-- | A successful, empty response.
success :: Response
success = Response True []

-- | An unsuccessful, empty response.
failure :: Response
failure = Response False []

-- | Exits fast with the given response. The response is encoded as JSON, printed to @stdout@ and
--   then the program is exited with @0@ (success).
--
--   The program always exits with success even if the response was a failure one. This is to
--   distinguish between bad calls to the binary and bad commands to the werewolf engine.
exitWith :: MonadIO m => Response -> m a
exitWith response = liftIO $ T.putStrLn (T.decodeUtf8 $ encode response) >> Exit.exitSuccess

-- | A message may be either public or private, indicated by its @to@ field.
--
--   Each message contains a single text field. This field is permitted to contain special
--   characters such as new lines and tabs.
data Message = Message
    { to      :: Maybe Text -- ^ The message recipient: 'Nothing' for a public message,
                            --   'Just' for a private message.
    , message :: Text       -- ^ The message text.
    } deriving (Eq, Generic, Show)

instance FromJSON Message

instance ToJSON Message where
    toJSON      = genericToJSON defaultOptions
#if MIN_VERSION_aeson(0,10,0)
    toEncoding  = genericToEncoding defaultOptions
#endif

-- | Creates a public message with the given text.
publicMessage :: Text -> Message
publicMessage = Message Nothing

-- | @privateMessage to message@
--
--   Creates a private message to @to@ with the given text.
privateMessage :: Text -> Text -> Message
privateMessage to = Message (Just to)

-- | @groupMessages tos message@
--
--   Creates multiple private messages (1 to each recipient) with the given text.
groupMessages :: [Text] -> Text -> [Message]
groupMessages tos message = map (`privateMessage` message) tos