{-# LANGUAGE RecordWildCards #-}

module Development.Rattle.Program(
    Program, newProgram, runProgram
    ) where

import Development.Rattle.Hash
import Development.Rattle.Server
import Development.Rattle.Derived
import Development.Shake.Command
import System.IO.Unsafe
import System.FilePath
import Language.Haskell.TH
import qualified Data.ByteString.Char8 as BS

-- | A program that can be run externally.
data Program a = Program
    {programDisplay :: a -> String
    ,programContents :: String
    ,programHash :: Hash
    }

-- | Create a new program which is based on a TH splice.
newProgram :: (Show a, Read a) => (a -> String) -> Q (TExp (a -> IO ())) -> Program a
newProgram display expr = Program display contents (hashString contents)
    where contents = unlines $ generate $ unsafePerformIO $ runQ expr

-- | Run a program.
runProgram :: Show a => Program a -> a -> Run ()
runProgram Program{..} x = do
    let Hash unhash = programHash
    let file = ".rattle/program/" </> BS.unpack unhash <.> "hs"
    cmdWriteFile file programContents
    cmd "runhaskell" file [show x]

generate :: TExp (a -> IO ()) -> [String]
generate expr =
    ["import System.Environment"
    ,"import System.IO"
    ,"import System.Directory"
    ,"import Development.Shake.Command"
    ,"import System.FilePath.Windows"
    ,"import Data.List as Data.OldList"
    ,"import System.IO.Extra"
    ,"import Data.Foldable"
    ,"import Data.Functor"
    ,"import System.Directory.Extra"
    ,"import GHC.List"
    ,"import Development.Shake.Command as Development.Shake.Internal.CmdOption"
    ,"import Data.List.Extra"
    ,"import GHC.Base"
    ,"import GHC.Classes"
    ,"main :: IO ()"
    ,"main = do [x] <- System.Environment.getArgs; body (read x)"
    ,"body = " ++ pprint (unType expr)
    ]