{-|
Module      : Text.Nmis
Description : Main module for nmis parsing
Copyright   : (c) Sasa Bogicevic, 2017
License     : GPL-3
Maintainer  : brutallesale@gmail.com
Stability   : experimental

-}

--   NMIS stands for Network Management Information System
--
--   This parser parses the nmis format files to Nmis record type
--
--  Example usage :
--
--  module Main where
--
--  import System.Environment (getArgs)
--
--  import System.IO
--
--  import Text.Megaparsec
--
--  import Text.Nmis
--
--  main :: IO ()
--
--  main = do
--
--    args <- getArgs
--
--    case args of
--
--      [] -> putStrLn "error: you need to pass in file path"
--
--      [path] -> do
--
--              contents <- readFile path
--
--              let result = parse parseNmis "" contents
--
--              case result of
--
--                  Left nodes -> print $ parseErrorPretty nodes
--
--                  Right nodes -> do
--
--                      print nodes
--
--      _ -> putStrLn "error: you need to pass in only one file path"
--

{-# LANGUAGE OverloadedStrings #-}

module Text.Nmis where


import Control.Monad (join)
import qualified Data.Map.Strict as M
import Prelude hiding (until)
import Text.Megaparsec
import Text.Megaparsec.String
import Text.Read (readMaybe)
import Data.Maybe (fromMaybe)
import Text.Internal.NmisTypes
import Text.Internal.Helper as H

-- |  Parse nmis file to [Nmis]
--
--    __This is a single function you will need to parse the file__
parseNmis :: Parser [Nmis]
parseNmis =
  lexeme $ do
    _ <- optional $ symbol "#"
    _ <- phash
    _ <- space >> string "("
    parseToList


-- | Parses single record
parseSingle :: Parser Nmis
parseSingle = do
  _ <- optional H.pQuotedStr
  _ <- until "=>" >> space >> string "{" >> newline
  node <- manyTill pOpt (try $ lookAhead $ optional space *> string "}")
  _ <- optional space >> string "}"
  _ <- optional $ string ","
  let rmap = M.fromList node
  return
    Nmis
    { customer = M.lookup "customer" rmap
    , groups = M.lookup "groups" rmap
    , active = M.lookup "active" rmap
    , businessService = M.lookup "businessService" rmap
    , advancedOptions = M.lookup "advancedOptions" rmap
    , authKey = M.lookup "authKey" rmap
    , authPassword = M.lookup "authPassword" rmap
    , authProtocol = M.lookup "authProtocol" rmap
    , calls = M.lookup "calls" rmap
    , cbqos = M.lookup "cbqos" rmap
    , collect = M.lookup "collect" rmap
    , community = M.lookup "community" rmap
    , depend = M.lookup "depend" rmap
    , display_name = M.lookup "display_name" rmap
    , group = M.lookup "group" rmap
    , host = M.lookup "host" rmap
    , location = M.lookup "location" rmap
    , model = M.lookup "model" rmap
    , name = M.lookup "name" rmap
    , netType = M.lookup "netType" rmap
    , ping = M.lookup "ping" rmap
    , port = M.lookup "port" rmap
    , rancid = M.lookup "rancid" rmap
    , roleType = M.lookup "roleType" rmap
    , serviceStatus = M.lookup "serviceStatus" rmap
    , services = M.lookup "services" rmap
    , threshold = M.lookup "threshold" rmap
    , timezone = join $ readMaybe <$> M.lookup "timezone" rmap
    , uuid = M.lookup "uuid" rmap
    , version = M.lookup "version" rmap
    , webserver = M.lookup "webserver" rmap
    }


-- |
-- This function is used to match records by group/groups field.
--
-- You have two lists with Nmis types [Nmis] [Nmis]
--
-- and you want to match group field from one to the groups field from the other,
--
-- grab a customer field from first list and attach it to the second list which you can save later.
--
-- This is something of practical use which I needed in the real world.
lookupInList :: [Nmis] ->  Nmis -> Nmis
lookupInList cust n = do
   let fcusts =  filterByGroup (group n) cust
   if length fcusts > 0 then
       n { customer = customer (head fcusts) }
   else n

-- | Filters Nmis by Nodes group
filterByGroup :: Maybe String -> [Nmis]  -> [Nmis]
filterByGroup s cl = filter (\c -> groups c == s) cl

-- | parses many single values until ');'
parseToList :: Parser [Nmis]
parseToList = do
  result <-
    manyTill parseSingle (try $ lookAhead $ optional space >> string ")")
  _ <- optional newline
  _ <- string ")"
  _ <- optional $ string ";"
  return result

-- | Show maybe integer for timezone field
showMaybeInt :: Maybe Integer -> String
showMaybeInt i = case i of
  Nothing -> ""
  Just x -> show x

-- | Show Nmis list
--
-- Use for printing the [Nmis] list
showNmis :: [Nmis] -> String
showNmis [] =  ""
showNmis (c:cx) =
  "\n  '" ++ fromMaybe "" (name c) ++ "' => {\n"
  ++ "    'customer' => '" ++ fromMaybe ""  (customer c) ++ "',\n"
  ++ "    'active' => '" ++ fromMaybe ""  (active c) ++ "',\n"
  ++ "    'authkey' => '" ++ fromMaybe ""  (authKey c) ++ "',\n"
  ++ "    'authpassword' => '" ++ fromMaybe ""  (authPassword c) ++ "',\n"
  ++ "    'authprotocol' => '" ++ fromMaybe ""  (authProtocol c) ++ "',\n"
  ++ "    'businesService' => '" ++ fromMaybe "" (businessService c) ++ "',\n"
  ++ "    'calls' => '" ++ fromMaybe ""  (calls c) ++ "',\n"
  ++ "    'cbqos' => '" ++ fromMaybe ""  (cbqos c) ++ "',\n"
  ++ "    'collect' => '" ++ fromMaybe ""  (collect c) ++ "',\n"
  ++ "    'community' => '" ++ fromMaybe ""  (community c) ++ "',\n"
  ++ "    'depend' => '" ++ fromMaybe ""  (depend c) ++ "',\n"
  ++ "    'display_name' => '" ++ fromMaybe "" (display_name c) ++ "',\n"
  ++ "    'group' => '" ++ fromMaybe ""  (group c) ++ "',\n"
  ++ "    'host' => '" ++ fromMaybe ""  (host c) ++ "',\n"
  ++ "    'location' => '" ++ fromMaybe ""  (location c) ++ "',\n"
  ++ "    'model' => '" ++ fromMaybe ""  (model c) ++ "',\n"
  ++ "    'name' => '" ++ fromMaybe ""  (name c) ++ "',\n"
  ++ "    'netType' => '" ++ fromMaybe ""  (netType c) ++ "',\n"
  ++ "    'ping' => '" ++ fromMaybe ""  (ping c) ++ "',\n"
  ++ "    'port' => '" ++ fromMaybe ""  (port c) ++ "',\n"
  ++ "    'rancid' => '" ++ fromMaybe ""  (rancid c) ++ "',\n"
  ++ "    'roleType' => '" ++ fromMaybe ""  (roleType c) ++ "',\n"
  ++ "    'serviceStatus' => '" ++ fromMaybe ""  (serviceStatus c) ++ "',\n"
  ++ "    'services' => '" ++ fromMaybe ""  (services c) ++ "',\n"
  ++ "    'threshold' => '" ++ fromMaybe ""  (threshold c) ++ "',\n"
  ++ "    'timezone' => '" ++ showMaybeInt (timezone c) ++ "',\n"
  ++ "    'uuid' => '" ++ fromMaybe ""  (uuid c) ++ "',\n"
  ++ "    'version' => '" ++ fromMaybe ""  (version c) ++ "',\n"
  ++ "    'webserver' => '" ++ fromMaybe ""  (webserver c) ++ "',\n"
  ++ "},"
  ++ showNmis cx