module DzenDhall.Extra where

import           Control.Monad
import           Control.Monad.Trans.Maybe
import           Control.Monad.Trans.Except
import qualified Data.List
import           Data.Maybe (catMaybes)
import qualified Data.Text
import           Data.Text (Text)
import           Time.Types
import           System.Directory (findExecutable)


nonNegative :: (Num a, Ord a) => a -> a
nonNegative x
  | x < 0 = 0
  | otherwise = x

positive :: Int -> Int
positive x
  | x < 1 = 1
  | otherwise = x

spaces :: Int -> Text
spaces w = Data.Text.justifyRight w ' ' ""

showPack :: Show a => a -> Text
showPack = Data.Text.pack . show

loopWhileM :: Monad m => m Bool -> m () -> m ()
loopWhileM pr act = do
    b <- pr
    when b do
      act
      loopWhileM pr act

whenJust :: (Monad m, Monoid b) => Maybe a -> (a -> m b) -> m b
whenJust = flip $ maybe (return mempty)

leftToJust :: Either a b -> Maybe a
leftToJust (Left a) = Just a
leftToJust _ = Nothing

withMaybe :: Maybe a -> b -> (a -> b) -> b
withMaybe mb b f = maybe b f mb

withEither :: Either a b -> (a -> c) -> (b -> c) -> c
withEither ei l r = either l r ei

whenLeft :: (Monad m, Monoid b) => Either a b -> (a -> m b) -> m b
whenLeft e f = whenJust (leftToJust e) f

safeHead :: [a] -> Maybe a
safeHead = fmap fst . Data.List.uncons

safeTail :: [a] -> Maybe [a]
safeTail = fmap snd . Data.List.uncons

fromLines :: [Text] -> Text
fromLines = Data.Text.intercalate "\n"

hush :: Either e a -> Maybe a
hush (Left _) = Nothing
hush (Right a) = Just a

throwMaybe :: Monad m => MaybeT m a
throwMaybe = exceptToMaybeT (throwE ())

-- This is a workaround,
-- see https://github.com/vincenthz/hs-hourglass/issues/32
addElapsedP :: ElapsedP -> ElapsedP -> ElapsedP
addElapsedP (ElapsedP e1 (NanoSeconds ns1)) (ElapsedP e2 (NanoSeconds ns2)) =
    let notNormalizedNS = ns1 + ns2
        (retainedNS, ns) = notNormalizedNS `divMod` 1000000000
    in  ElapsedP (e1 + e2 + (Elapsed $ Seconds retainedNS)) (NanoSeconds ns)

-- | Returns a list of executables that are not present in PATH.
checkExecutables :: [String] -> IO [String]
checkExecutables executables = do
  fmap catMaybes $ forM executables \executable ->
    maybe (Just executable) (const Nothing) <$> findExecutable executable

isYes :: String -> Bool
isYes response = response `elem` ["Y", "y", "Yes", "yes", ""]