module Air.Lab1 where

import Air.Data.Default
import Air.Env hiding (mod, read, length, drop, at)
import Air.Extra
import Air.Extra (now)
import Control.Concurrent
import Control.Concurrent.STM
import Data.ByteString.Lazy.Char8 (ByteString, pack, unpack)
import Data.IORef
import Data.List ( genericDrop, genericLength )
import Data.Maybe
import Data.StateVar
import Data.Time.Clock.POSIX
import Prelude ()
import System.Exit
import System.Exit ( exitWith, ExitCode(ExitSuccess) )
import System.IO (hGetContents)
import System.Process
import Text.JSON.Generic
import Text.JSON.String
import qualified Control.Monad as Monad
import qualified Data.StateVar as StateVar
import qualified Prelude as P


atom :: STM a -> IO a
atom = atomically


want :: (Monad m, Default b) => (Maybe a) -> (a -> m b) -> m b
want = flip - maybe (return def)

wantM :: (Monad m, Default b) => m (Maybe a) -> (a -> m b) -> m b
wantM x f = do
  x' <- x
  x'.want - f

append_maybe :: a -> Maybe [a] -> Maybe [a]
append_maybe x Nothing = Just [x]
append_maybe x (Just xs) = Just - x:xs

  
maybe_first :: [a] -> Maybe a
maybe_first xs = case xs of
  [] -> Nothing
  (y:_) -> Just y
  
    
update_list :: (a -> Bool) -> (a -> a) -> [a] -> [a]
update_list p f xs = do
  x <- xs
  let y = if p x then f x else x
  return y


decode_json :: (Data a) => String -> Either String a
decode_json s =
    case runGetJSON readJSValue s of
    Left msg -> Left msg
    Right j ->
        case fromJSON j of
        Error msg -> Left msg
        Ok x -> Right x



mapT :: (HasGetter g, HasSetter g) => (a -> a) -> [g a] -> IO [g a]
mapT f xs = xs.mapM (\o -> do
  o $~ f
  return o
  )

filterT :: (HasGetter g) => (a -> Bool) -> [g a] -> IO [g a]
filterT f xs = xs.mapM (\o -> do
  object <- get o
  if f object
    then return - Just o
    else return - Nothing
  )
  .fmap catMaybes

findT :: (HasGetter g) => (a -> Bool) -> [g a] -> IO (Maybe (g a))
findT f xs = filterT f xs ^ listToMaybe

find_or_fail_with_message_T :: (HasGetter g) => String -> (a -> Bool) -> [g a] -> IO (g a)
find_or_fail_with_message_T msg f xs = do
  r <- findT f xs
  case r of
    Nothing -> error msg
    Just _r -> return _r



now_in_micro_seconds :: IO Integer
now_in_micro_seconds = 
  now ^ (utcTimeToPOSIXSeconds > (* 1000000) > floor)

now_in_milli_seconds :: IO Integer
now_in_milli_seconds = 
  now ^ (utcTimeToPOSIXSeconds > (* 1000) > floor)

run_within :: (RealFrac a) => a -> String -> [String] -> IO (Maybe ExitCode)
run_within secs cmd args = do
  (_, hOut, hError, pid) <- runInteractiveProcess cmd args Nothing Nothing
  
  
  bomb_time <- now ^ t2f ^ (P.+ secs)
  
  let { wait_loop = do
      exit_code <- getProcessExitCode pid
      
      case exit_code of
        Just code -> do
          hGetContents hOut >>= putStrLn
          hGetContents hError >>= putStrLn
          return - Just code
        Nothing -> do
          time_stamp <- now ^ t2f
          
          if time_stamp >= bomb_time
            then do
              terminateProcess pid
              return - Nothing
            else do
              -- putStrLn "sleeping ..."
              sleep 0.1
              wait_loop
    }

  wait_loop
  

as_string :: (String -> String) -> ByteString -> ByteString
as_string f x = x.unpack.f.pack