module Penny.Copper.Transaction (transaction, render) where

import Control.Applicative ((<$>), (<*>))
import qualified Control.Monad.Exception.Synchronous as Ex
import Data.Foldable (toList)
import qualified Data.Traversable as Tr
import qualified Data.Text as X
import Text.Parsec (many)
import Text.Parsec.Text ( Parser )

import qualified Penny.Copper.DateTime as DT
import qualified Penny.Copper.TopLine as TL
import Penny.Copper.TopLine ( topLine )
import qualified Penny.Copper.Posting as Po
import qualified Penny.Copper.Qty as Qt
import qualified Penny.Lincoln as L
import Penny.Lincoln.Family (orphans)
import qualified Penny.Lincoln.Family.Family as F
import Penny.Lincoln.Family.Family ( Family ( Family ) )
import qualified Penny.Lincoln.Transaction as T
import qualified Penny.Lincoln.Transaction.Unverified as U

errorStr :: T.Error -> String
errorStr e = case e of
  T.UnbalancedError -> "postings are not balanced"
  T.CouldNotInferError -> "could not infer entry for posting"

mkTransaction ::
  U.TopLine
  -> U.Posting
  -> U.Posting
  -> [U.Posting]
  -> Ex.Exceptional String L.Transaction
mkTransaction top p1 p2 ps = let
  famTrans = Family top p1 p2 ps
  errXact = T.transaction famTrans
  in case errXact of
    Ex.Exception err -> Ex.Exception . errorStr $ err
    Ex.Success x -> return x

maybeTransaction ::
  DT.DefaultTimeZone
  -> Qt.RadGroup
  -> Parser (Ex.Exceptional String L.Transaction)
maybeTransaction dtz rg =
  mkTransaction
  <$> topLine dtz
  <*> Po.posting rg
  <*> Po.posting rg
  <*> many (Po.posting rg)

transaction ::
  DT.DefaultTimeZone
  -> Qt.RadGroup
  -> Parser L.Transaction
transaction dtz rg = do
  ex <- maybeTransaction dtz rg
  case ex of
    Ex.Exception s -> fail s
    Ex.Success b -> return b

render ::
  DT.DefaultTimeZone
  -> (Qt.GroupingSpec, Qt.GroupingSpec)
  -> Qt.RadGroup
  -> T.Transaction
  -> Maybe X.Text
render dtz gs rg txn = do
  let txnFam = T.unTransaction txn
  tlX <- TL.render dtz (F.parent txnFam)
  pstgsX <- Tr.traverse (Po.render gs rg) (orphans txnFam)
  return $ tlX `X.append` (X.concat (toList pstgsX))