-- ----------------------------------------------------------------------------
{- |
  Module     : Holumbus.MapReduce.DaemonInterface
  Copyright  : Copyright (C) 2009 Sebastian Reese
  License    : MIT

  Maintainer : Sebastian Reese (str@holumbus.org)
  Stability  : experimental
  Portability: portable
  Version    : 0.1


-}
-- ----------------------------------------------------------------------------

module Holumbus.MapReduce.DaemonInterfaceWithFS
    (
      -- * operations
      runDaemon
    )
where

import           Data.Maybe
import Data.Binary
import Data.List
import           Holumbus.Common.Utils                         ( handleAll )
import           Holumbus.Common.FileHandling
import qualified Holumbus.Common.Debug                          as DEBUG
import qualified Holumbus.Console.ServerConsole                       as Console

import qualified Holumbus.MapReduce.MapReduce                   as MR
import qualified Holumbus.MapReduce.Types                       as T
import qualified Holumbus.FileSystem.FileSystem                 as FS
import qualified Holumbus.FileSystem.Storage                    as S



-- ----------------------------------------------------------------------------
-- Operations
-- ----------------------------------------------------------------------------

-- | runs the user interface... just add an fileSystem an a fancy version-number
runDaemon :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> String -> Int -> String -> IO ()
runDaemon mrfs version port prompt
  = do
    -- starts the console with the specified commands
    Console.startServerConsole (createConsole version) mrfs port prompt



-- ----------------------------------------------------------------------------
-- private functions
-- ----------------------------------------------------------------------------


createConsole :: (MR.MapReduce mr) => String -> Console.ConsoleData (mr,FS.FileSystem)
createConsole version =
  Console.addConsoleCommand "site"      getMySiteId "" $
  Console.addConsoleCommand "mrtype"    getMapReduceType "" $
  Console.addConsoleCommand "startC"    startControlling "" $
  Console.addConsoleCommand "stopC"     stopControlling "" $
  Console.addConsoleCommand "isC"       isControlling "" $
  Console.addConsoleCommand "step"      doSingleStep "" $
  Console.addConsoleCommand "mrJob"     doMapReduceJob "" $
  Console.addConsoleCommand "parse"     parseJob "" $
  Console.addConsoleCommand "debug"     printDebug "" $ 
  Console.addConsoleCommand "version"   (printVersion version) "prints the version" $ 
  Console.addConsoleCommand "fsid"        getMyFSSiteId     "get my siteId" $
  Console.addConsoleCommand "sites"     getFileSites    "get all sites with the given file name" $
  Console.addConsoleCommand "with"      getNearestNodePortWithFile      "gets the nearest node-port with a file (DEBUG)" $
  Console.addConsoleCommand "for"       getNearestNodePortForFile       "gets the nearest node-port for a (new) file (DEBUG)" $  
  Console.addConsoleCommand "contains"  containsFile    "is file in filesystem or not" $
  Console.addConsoleCommand "create"    createFile      "adds a file" $
  Console.addConsoleCommand "createS"   createFiles     "adds a list of files [(Filename,FileContent)]" $
  Console.addConsoleCommand "append"    append          "appends to a file" $
  Console.addConsoleCommand "delete"    deleteFile      "deletes a file" $
  Console.addConsoleCommand "content"   getFileContent  "gets the content of a file" $
  Console.addConsoleCommand "data"      getFileData     "gets the metadata of a file" $
  Console.addConsoleCommand "local"     isFileLocal     "test, if the file is on the local node" $
  Console.addConsoleCommand "fsdebug"   printFSDebug    "prints internal state of the filesystem (DEBUG)" $ 
  Console.initializeConsole

 
getMySiteId :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
getMySiteId (mr,_) _
  = handleAll (\e -> return . show $ e) $
      do
      s <- MR.getMySiteId mr
      return . show $ s


getMapReduceType :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
getMapReduceType (mr,_) _
  = handleAll (\e -> return . show $ e) $
      do
      t <- MR.getMapReduceType mr
      return . show $ t

  
startControlling :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
startControlling (mr,_) _
  = handleAll (\e -> return . show $ e) $
      do
      MR.startControlling mr
      return "OK"


stopControlling :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
stopControlling (mr,_) _
  = handleAll (\e -> return . show $ e) $
      do
      MR.stopControlling mr
      return "OK"


isControlling :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
isControlling (mr,_) _
  = handleAll (\e -> return . show $ e) $
      do
      b <- MR.isControlling mr
      return . show $ b


  
doSingleStep :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
doSingleStep (mr,_) _
  = handleAll (\e -> return . show $ e) $
      do
      MR.doSingleStep mr
      return "OK"


  
doMapReduceJob :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
doMapReduceJob (mr,_) opts
  = do
    handleAll (\e -> return . show $ e) $
      do
      (mbName,_) <- Console.nextOption opts
      jobInfo <- (loadFromXmlFile (fromJust mbName))::IO T.JobInfo
      r <- MR.doMapReduceJob jobInfo mr
      return . (++"RESULT:\n") . show $  r


parseJob :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
parseJob _ opts
  = do
    handleAll (\e -> return . show $ e) $
      do
      (mbName,_) <- Console.nextOption opts
      jobInfo <- (loadFromXmlFile (fromJust mbName))::IO T.JobInfo
      return . show $ jobInfo


printDebug :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
printDebug (mr,_) _
  = do
    handleAll (\e -> return . show $ e) $ DEBUG.getDebug  mr

  
printVersion :: (MR.MapReduce mr) => String -> (mr,FS.FileSystem) -> [String] -> IO String
printVersion version _ _
  = return version

getFileNameAndContent :: [String] -> (S.FileId, S.FileContent)
getFileNameAndContent []   = error "no filename given"
getFileNameAndContent (x:xs) = (x, encode $ intercalate " " xs)
-- getFileNameAndContent (x:xs) = (x, S.TextFile $ intercalate " " xs)

getFileNamesAndContent :: [String] -> [(S.FileId, S.FileContent)]
getFileNamesAndContent [] = []
getFileNamesAndContent (_:[]) = error "no content given"
getFileNamesAndContent (fid:c:xs) = ((fid, encode c):getFileNamesAndContent xs)


getFileNameAndContentSize :: [String] -> (S.FileId, Integer)
getFileNameAndContentSize []   = error "no filename given"
getFileNameAndContentSize (x1:[]) = (x1, 0)
getFileNameAndContentSize (x1:x2:_) = (x1, read x2)


getMyFSSiteId :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
getMyFSSiteId (_,fs) _
  = do
    handleAll (\e -> return $ show e) $
      do
      i <- FS.getMySiteId fs
      return $ show i
     

getFileSites :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
getFileSites (_,fs) opts
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, _) = getFileNameAndContent opts
      s <- FS.getFileSites n fs
      return $ show s


getNearestNodePortWithFile :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
getNearestNodePortWithFile (_,fs) opts
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, _) = getFileNameAndContent opts
      p <- FS.getNearestNodePortWithFile n fs
      return $ show p


getNearestNodePortForFile :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
getNearestNodePortForFile (_,fs) opts
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, s) = getFileNameAndContentSize opts
      p <- FS.getNearestNodePortForFile n s fs
      return $ show p


containsFile :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
containsFile (_,fs) opts 
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, _) = getFileNameAndContent opts
      b <- FS.containsFile n fs
      return $ show b


createFile :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
createFile (_,fs) opts 
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, c) = getFileNameAndContent opts
      FS.createFile n c fs 
      return ""

createFiles :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
createFiles (_,fs) opts
  = do
    handleAll (\e -> return $ show e) $
      do
      let l = getFileNamesAndContent opts
      FS.createFiles l fs
      return ""

append :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
append (_,fs) opts
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, c) = getFileNameAndContent opts
      FS.appendFile n c fs
      return ""
  

deleteFile :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
deleteFile (_,fs) opts
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, _) = getFileNameAndContent opts
      FS.deleteFile n fs
      return ""


getFileContent :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
getFileContent (_,fs) opts
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, _) = getFileNameAndContent opts
      c <- FS.getFileContent n fs
      return $ show c


getFileData :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
getFileData (_,fs) opts
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, _) = getFileNameAndContent opts
      d <- FS.getFileData n fs
      return $ show d
      

isFileLocal :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
isFileLocal (_,fs) opts 
  = do
    handleAll (\e -> return $ show e) $
      do
      let (n, _) = getFileNameAndContent opts
      b <- FS.isFileLocal n fs
      return $ show b

printFSDebug :: (MR.MapReduce mr) => (mr,FS.FileSystem) -> [String] -> IO String
printFSDebug (_,fs) _
  = do
    handleAll (\e -> return . show $ e) $ DEBUG.getDebug  fs