-- | Like 'Penny.Lincoln.Queries' but instead of querying the main
-- posting of the PostFam, queries the siblings. Therefore, these
-- functions return a list, with each entry in the list containing the
-- best answer for each sibling. There is one item in the list for
-- each sibling, even if all these items contain the same data (for
-- instance, a posting might have five siblings, but all five siblings
-- might have the same payee. Nonetheless the 'payee' function will
-- return a list of five items.)
module Penny.Lincoln.Queries.Siblings where

import Control.Arrow (second, first)
import qualified Penny.Lincoln.Bits as B
import qualified Penny.Lincoln.Ents as E
import Penny.Lincoln.Balance (Balance, entryToBalance)

-- | For all siblings, uses information from the Posting if it is set;
-- otherwise, uses data from the TopLine.
bestSibs
  :: (B.PostingCore -> Maybe a)
  -> (B.TopLineCore -> Maybe a)
  -> E.Posting
  -> [Maybe a]
bestSibs fp ft =
  map f
  . map (second (B.pdCore . E.meta))
  . E.unrollSnd
  . second (\(x, xs) -> (x:xs))
  . second E.tailEnts
  . first B.tlCore
  . E.unPosting
  where
    f (tl, vw) = maybe (ft tl) Just (fp vw)


-- | For all siblings, get the information from the Posting if it
-- exists; otherwise Nothing.
sibs
  :: (E.Ent B.PostingData -> a)
  -> E.Posting
  -> [a]
sibs fp = map fp . snd . fmap ((\(x, xs) -> (x:xs)) . E.tailEnts)
          . E.unPosting

payee :: E.Posting -> [Maybe B.Payee]
payee = bestSibs B.pPayee B.tPayee

number :: E.Posting -> [Maybe B.Number]
number = bestSibs B.pNumber B.tNumber

flag :: E.Posting -> [Maybe B.Flag]
flag = bestSibs B.pFlag B.tFlag

postingMemo :: E.Posting -> [Maybe B.Memo]
postingMemo = sibs (B.pMemo . B.pdCore . E.meta)

account :: E.Posting -> [B.Account]
account = sibs (B.pAccount . B.pdCore . E.meta)

tags :: E.Posting -> [B.Tags]
tags = sibs (B.pTags . B.pdCore . E.meta)

entry :: E.Posting -> [Either (B.Entry B.QtyRep) (B.Entry B.Qty)]
entry = sibs E.entry

balance :: E.Posting -> [Balance]
balance = map (either entryToBalance entryToBalance) . entry

drCr :: E.Posting -> [B.DrCr]
drCr = map (either B.drCr B.drCr) . entry

amount :: E.Posting -> [Either (B.Amount B.QtyRep) (B.Amount B.Qty)]
amount = map (either (Left . B.amount) (Right . B.amount)) . entry

qty :: E.Posting -> [B.Qty]
qty = map (either (B.toQty . B.qty) (B.toQty . B.qty)) . amount

commodity :: E.Posting -> [B.Commodity]
commodity = map (either B.commodity B.commodity) . amount

postingLine :: E.Posting -> [Maybe B.PostingLine]
postingLine = sibs (fmap B.pPostingLine . B.pdFileMeta . E.meta)

side :: E.Posting -> [Maybe B.Side]
side = sibs (B.pSide . B.pdCore . E.meta)

spaceBetween :: E.Posting -> [Maybe B.SpaceBetween]
spaceBetween = sibs (B.pSpaceBetween . B.pdCore . E.meta)

globalPosting :: E.Posting -> [Maybe B.GlobalPosting]
globalPosting = sibs (B.pdGlobal . E.meta)

filePosting :: E.Posting -> [Maybe B.FilePosting]
filePosting = sibs (fmap B.pFilePosting . B.pdFileMeta . E.meta)

globalTransaction :: E.Posting -> [Maybe B.GlobalTransaction]
globalTransaction =
  map B.tlGlobal
  . map fst
  . E.unrollSnd
  . second (\(x, xs) -> (x:xs))
  . second E.tailEnts
  . E.unPosting