module Quoridor.Cmdline.Options
  ( Options(..)
  , ExecMode(..)
  , getOptions
  ) where
import           Control.Monad         (unless)
import           System.Environment    (getProgName)
import           System.Exit           (exitFailure, exitSuccess)
import           System.Console.GetOpt (ArgDescr (NoArg, OptArg),
                                        ArgDescr (ReqArg),
                                        ArgOrder (RequireOrder), OptDescr,
                                        OptDescr (Option), getOpt, usageInfo)
import           Quoridor.Helpers      (andP)
data Options = Options
  { opBoardSize      :: Int
  , opNumOfPlayers   :: Int
  , opGatesPerPlayer :: Int
  , opHostListenPort :: Int
  , opHttpListenPort :: Int
  , opExecMode       :: ExecMode
  }
data ExecMode = ExLocal | ExHost | ExJoin | ExProxy
  deriving Eq
defaultOptions :: Options
defaultOptions = Options
  { opBoardSize      = 9
  , opNumOfPlayers   = 2
  , opGatesPerPlayer = 10
  , opHostListenPort = 33996
  , opHttpListenPort = 33997
  , opExecMode       = ExLocal
  }
getOptions :: [String] -> IO Options
getOptions args = do
  let (actions, _, _) = getOpt RequireOrder options args
  foldl (>>=) (return defaultOptions) actions
--helpers
isInRange :: Ord a => a -> a -> a -> Bool
isInRange a b c = ((>= b) `andP` (<= c)) a
putUsageInfoLn :: IO ()
putUsageInfoLn = do
  prg <- getProgName
  putStrLn (usageInfo prg options)
options :: [ OptDescr (Options -> IO Options) ]
options =
  [ Option "b" ["board-size"]
      (ReqArg
          (\arg opts -> do
            argNum <- rangedOption 2 9 arg
            return opts { opBoardSize = argNum })
          "INTEGER")
      "Board size (2-9 rows/columns). default 9"
  , Option "n" ["number-of-players"]
      (ReqArg
          (\arg opts -> do
            argNum <- rangedOption 2 4 arg
            return opts { opNumOfPlayers = argNum })
          "INTEGER")
      "Number of players (2-4 players). default 2"
  , Option "g" ["gates-per-player"]
      (ReqArg
          (\arg opts -> do
            argNum <- rangedOption 0 100 arg
            return opts { opGatesPerPlayer = argNum })
          "INTEGER")
      "Gates per player (1-100 gates per player). default 10"
  , Option "l" ["local"]
      (NoArg $
          \opts -> return opts { opExecMode = ExLocal })
      "Start a local game"
  , Option "h" ["host"]
      (portOptionArg ExHost)
      "Host a game server. default port 33997"
  , Option "j" ["join"]
      (portOptionArg ExJoin)
      "Join a game server"
  , Option "p" ["client-proxy"]
      (portOptionArg ExProxy)
      "Client acts as proxy for a browser (used by the http server)"
  , Option "t" ["http-port"]
      (ReqArg
          (\arg opts -> do
            argNum <- rangedOption 1025 65535 arg
            return opts { opHttpListenPort = argNum })
          "PORT")
      "A port for the http-server (port 1025-65535), relevant only if --host flag is used. default 33997"
  , Option "?" ["help"]
      (NoArg $
          \_ -> do
            putUsageInfoLn
            exitSuccess)
      "Show help"
  ]
 where portOptionArg execMode =
         OptArg
             (\arg opts -> do
                argNum <- maybe (return $ opHostListenPort opts)
                  (rangedOption 1025 65535) arg
                return opts
                  { opExecMode = execMode
                  , opHostListenPort = argNum
                  })
              "PORT"
       rangedOption x y arg = do
         let argNum = read arg
         unless (isInRange argNum x y) $ do
           putUsageInfoLn
           exitFailure
         return argNum