{-|
  Module     : Client
  Copyright  : Copyright (C) 2010 https://github.com/berufsnerd
  License    : GPLv3

  Portability: non-portable

  Client handling code
-}
module Client(
  serveClient
, loopUntil
) where

import Data.Map
import IO
import System.IO
import System.Log.Logger

import Data
import Protocol
import Types
import State

{-|
  Parse some input, apply a function to it, test a predicate
  and maybe do it again (aka loop).
  During the execution, a state gets modified.
-}
loopUntil
  :: (Monad m, Show c)
  => b                     -- ^ the state to work with
  -> m a                   -- ^ parse an input
  -> (a -> Bool)           -- ^ a predicate to determine wheter the parsed input
                           --   should be processed or the execution should quit
  -> (b -> a -> m (b, c))  -- ^ evaluate the parsed input
  -> (b -> c -> m ())      -- ^ show the result of the evaluation
  -> (b -> m ())           -- ^ code the execute, when the predicate fails
  -> m ()
loopUntil state parseInput pred evalAction showAction endAction = do
  result <- parseInput
  if pred result
    then do
      endAction state
      return ()
    else do
      evalResult <- evalAction state result
      showAction state (snd evalResult)
      loopUntil (fst evalResult) parseInput pred evalAction showAction endAction

{-|
  Serve the request of a client
-}
serveClient
  :: (Show a)
  => Handle  -- ^ A handle to read from
  -> Handle  -- ^ A handle to write to
  -> a       -- ^ Some id information for the current client
  -> IO ()
serveClient readHandle writeHandle id =
  action `catch`
    (\ _ -> return ())
  where
    action = loopUntil
      initState
      readQuery
      (== Quit)
      execQuery
      showAction
      endAction

    readQuery = do
      x <- hGetLine readHandle
      return (read x :: Query)

    showAction state res = do
      hPutStr writeHandle $ show res ++ eol
      hFlush writeHandle
      -- Logging
      case res of
        Result r -> debugM log $ show id ++
                      ": calculated formula. Result: " ++ show r
        _        -> return ()

    endAction state = do
      debugM log $ show id ++ ": client closed the connection. " ++
        show (getCnt state) ++ " formulas calculated."
      hClose readHandle

    log = "Client"

    eol = "\LF"
