{-# LANGUAGE OverloadedStrings #-}

module SimpleLocalnetHelper
       (getSlaveBackend,
        getSlaveConfBackend,
        getMasterBackend,
        getMasterConfBackend,
        startMasterProcess) where

import System.IO
import qualified Control.Distributed.Process as DP
import qualified Control.Distributed.Process.Node as Node
import Control.Distributed.Process.Backend.SimpleLocalnet
import qualified Data.ByteString.Char8 as BS
import Network.Transport (EndPointAddress(..))

-- | Make a NodeId from "host:port" string.
makeNodeId :: String -> DP.NodeId
makeNodeId addr = DP.NodeId . EndPointAddress . BS.concat $ [BS.pack addr, ":0"]

splitAddr :: String -> (String, String)
splitAddr addr = (host, port)
  where
    (host, rest) = break (== ':') addr
    port = tail rest

deleteAt :: Int -> [String] -> [String]
deleteAt i xs = xs1 ++ xs2
  where (xs1, rest) = splitAt i xs
        xs2 = tail rest

getSlaveBackend :: Int -> [String] -> DP.RemoteTable -> IO Backend
getSlaveBackend i addrs rtable =
  do let addr = addrs !! i
         (host, port) = splitAddr addr
     backend <- initializeBackend host port rtable
     return backend

getMasterBackend :: Int -> [String] -> DP.RemoteTable -> IO (Backend, [DP.NodeId])
getMasterBackend i addrs rtable =
  do let addr = addrs !! i
         (host, port) = splitAddr addr
         slaves = map makeNodeId $ deleteAt i addrs 
     backend <- initializeBackend host port rtable
     return (backend, slaves)

getClusterConf :: IO [String]
getClusterConf = fmap addresses $ readFile "cluster.conf"

addresses :: String -> [String]
addresses =
  filter (\x -> head x /= '#') .
  filter (\x -> not $ null $ filter (\y -> y /= ' ') x) .
  lines

getSlaveConfBackend :: Int -> DP.RemoteTable -> IO Backend
getSlaveConfBackend i rtable =
  do addrs <- getClusterConf
     getSlaveBackend i addrs rtable

getMasterConfBackend :: Int -> DP.RemoteTable -> IO (Backend, [DP.NodeId])
getMasterConfBackend i rtable =
  do addrs <- getClusterConf
     getMasterBackend i addrs rtable

startMasterProcess :: Backend -> [DP.NodeId] -> (Backend -> [DP.NodeId] -> DP.Process ()) -> IO ()
startMasterProcess backend slaves process =
  do node <- newLocalNode backend
     Node.runProcess node (process backend slaves)