module Balances where 

import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.List
import LedgerT
import Valuation

type AccountNameC = [String]
type Balances = Map.Map AccountNameC MixedAmount

nBalances :: RawLedger -> [Date] -> [Balances]
nBalances rl ts = accumBalances $ map netEntries $ partitionByDates ts $ (entries rl)
  where
    partitionByDates :: [Date] -> [Entry] -> [[Entry]]
    partitionByDates [] _ = []
    partitionByDates (t:ts) es = est : partitionByDates ts es'
      where
        (est,es') = span (\e -> edate e <= t) es

    accumBalances :: [Balances] -> [Balances]
    accumBalances = scanl1 (Map.unionWith (+))


netEntries :: [Entry] -> Balances
netEntries es = foldr addt Map.empty (concatMap etransactions es)
  where
    addt rtran bal = Map.insertWith (+) (accountNameComponents $ taccount rtran) (tamount rtran) bal


aggBalances :: Balances -> Balances
aggBalances bal = foldr aggf Map.empty (Map.toList bal)
  where
    aggf :: (AccountNameC,MixedAmount) -> Balances -> Balances
    aggf (aname,amount) bal = foldr agga bal (inits aname)
      where
        agga aname bal = Map.insertWith (+) aname amount bal


foldBalances :: (AccountNameC -> Amount -> b -> b) -> b -> Balances -> b
foldBalances  f b bals = foldr
    (\(an,ma) b -> foldr
        (\a b -> f an a b)
        b (amounts ma)
    )
    b (Map.toList bals)


commoditySymbols :: Balances -> [CommoditySymbol]
commoditySymbols bals = nub $ sort $ foldBalances (\an ma cs -> symbol (commodity ma) : cs) [] bals
