{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Hcompta.CLI.Command.Print where

import           Prelude hiding (foldr)
-- import           Control.Arrow (first)
-- import           Control.Applicative ((<$>))
-- import           Control.Monad ((>=>))
import           Control.Monad.IO.Class (liftIO)
import           Control.Monad.Trans.Except (runExceptT)
import qualified Data.Either
import           Data.Foldable (foldr)
import           System.Console.GetOpt
                 ( ArgDescr(..)
                 , OptDescr(..)
                 , usageInfo )
import           System.Environment as Env (getProgName)
import           System.Exit (exitWith, ExitCode(..))
import qualified System.IO as IO

import qualified Hcompta.CLI.Args as Args
import qualified Hcompta.CLI.Context as Context
import qualified Hcompta.CLI.Format.Ledger as CLI.Ledger
import qualified Hcompta.CLI.Write as Write
import qualified Hcompta.Format.Ledger as Ledger
import qualified Hcompta.Format.Ledger.Journal as Ledger.Journal
import qualified Hcompta.Format.Ledger.Read as Ledger.Read
import qualified Hcompta.Format.Ledger.Write as Ledger.Write
import qualified Hcompta.Lib.Leijen as W
import qualified Hcompta.Filter as Filter
import qualified Hcompta.Filter.Read as Filter.Read

data Ctx
 =   Ctx
 { ctx_input :: [FilePath]
 , ctx_align :: Bool
 } deriving (Eq, Show)

nil :: Ctx
nil =
	Ctx
	 { ctx_input = []
	 , ctx_align = True
	 }

usage :: IO String
usage = do
	bin <- Env.getProgName
	return $unlines $
		[ "SYNTAX "
		, "  "++bin++" print [option..]"
		, ""
		, usageInfo "OPTIONS" options
		]

options :: Args.Options Ctx
options =
	[ Option "h" ["help"]
	 (NoArg (\_context _ctx -> do
		usage >>= IO.hPutStr IO.stderr
		exitWith ExitSuccess))
	 "show this help"
	, Option "i" ["input"]
	 (ReqArg (\s _context ctx -> do
		return $ ctx{ctx_input=s:ctx_input ctx}) "FILE")
	 "read data from given file, can be use multiple times"
	, Option "" ["align"]
	 (OptArg (\arg context ctx -> do
		ctx_align <- case arg of
		 Nothing    -> return $ True
		 Just "yes" -> return $ True
		 Just "no"  -> return $ False
		 Just _     -> Write.fatal context $
			W.text "--align option expects \"yes\", or \"no\" as value"
		return $ ctx{ctx_align})
	  "[yes|no]")
	 "align output"
	]

run :: Context.Context -> [String] -> IO ()
run context args = do
	(ctx, text_filters) <- Args.parse context usage options (nil, args)
	read_journals <- do
		CLI.Ledger.paths context $ ctx_input ctx
		>>= do
			mapM $ \path -> do
				liftIO $ runExceptT $ Ledger.Read.file path
				>>= \x -> case x of
				 Left  ko -> return $ Left (path, ko)
				 Right ok -> return $ Right ok
		>>= return . Data.Either.partitionEithers
	case read_journals of
	 (errs@(_:_), _journals) ->
		(flip mapM_) errs $ \(_path, err) -> do
			Write.fatal context $ err
	 ([], journals) -> do
		(filters::[Filter.Test_Bool (Filter.Test_Transaction Ledger.Transaction)]) <-
			(flip mapM) text_filters $ \s ->
				liftIO $ Filter.Read.read Filter.Read.test_transaction s
				>>= \f -> case f of
				 Left  ko -> Write.fatal context $ ko
				 Right ok -> return ok
		Write.debug context $ "transaction_filter: " ++ show filters
		style_color <- Write.with_color context IO.stdout
		let sty = Ledger.Write.Style
			 { Ledger.Write.style_align = ctx_align ctx
			 , Ledger.Write.style_color
			 }
		let transactions =
			foldr
			 (Ledger.Journal.fold
				 (flip (foldr
					 (flip (foldr
						 (\tr ->
							case Filter.test
							 (foldr Filter.And Filter.Any filters) tr of
							 False -> id
							 True  -> (:) tr
						 ))))
				 . Ledger.journal_transactions))
			 []
			 journals
		Ledger.Write.put sty IO.stdout $ do
		Ledger.Write.transactions transactions