-- | Options for the Posts report.
module Penny.Cabin.Posts.Options where


import Control.Monad.Exception.Synchronous (Exceptional)
import Data.Text (Text)
import qualified Data.Text as X
import Data.Time (formatTime)
import System.Locale (defaultTimeLocale)
import qualified Data.Time as Time
import qualified Text.Matchers.Text as M

import qualified Penny.Liberty as Ly
import qualified Penny.Liberty.Expressions as Ex
import qualified Penny.Lincoln as L
import qualified Penny.Lincoln.Balance as Bal
import qualified Penny.Lincoln.Bits as Bits
import qualified Penny.Lincoln.Queries as Q
import qualified Penny.Cabin.Posts.Allocate as A
import qualified Penny.Cabin.Chunk as CC
import qualified Penny.Cabin.Colors as C
import qualified Penny.Cabin.Options as O
import qualified Penny.Cabin.Posts.Fields as Fields
import qualified Penny.Cabin.Posts.Fields as F
import qualified Penny.Cabin.Posts.Meta as Meta
import qualified Penny.Cabin.Posts.Spacers as S
import qualified Penny.Cabin.Posts.Spacers as Spacers
import qualified Penny.Cabin.Colors.DarkBackground as Dark
import Penny.Copper.DateTime (DefaultTimeZone)
import Penny.Copper.Qty (RadGroup)
import qualified Penny.Shield as S


type Box = L.Box Meta.PostMeta

data T =
  T { drCrColors :: C.DrCrColors
      -- ^ Colors to use when displaying debits, credits, and
      -- when displaying balance totals

    , baseColors :: C.BaseColors 
      -- ^ Colors to use when displaying everything else

    , dateFormat :: Box -> X.Text
      -- ^ How to display dates. This function is applied to the
      -- a PostingInfo so it has lots of information, but it
      -- should return a date for use in the Date field.
      
    , qtyFormat :: Box -> X.Text
      -- ^ How to display the quantity of the posting. This
      -- function is applied to a PostingInfo so it has lots of
      -- information, but it should return a formatted string of
      -- the quantity. Allows you to format digit grouping,
      -- radix points, perform rounding, etc.
      
    , balanceFormat :: Bits.Commodity -> Bal.BottomLine -> X.Text
      -- ^ How to display balance totals. Similar to
      -- balanceFormat.
      
    , payeeAllocation :: A.Allocation
      -- ^ This and accountAllocation determine how much space
      -- payees and accounts receive. They divide up the
      -- remaining space after everything else is displayed. For
      -- instance if payeeAllocation is 60 and accountAllocation
      -- is 40, the payee takes about 60 percent of the
      -- remaining space and the account takes about 40 percent.
      
    , accountAllocation :: A.Allocation 
      -- ^ See payeeAllocation above

    , width :: ReportWidth
      -- ^ Gives the default report width. This can be
      -- overridden on the command line. You can use the
      -- information from the Runtime to make this as wide as
      -- the as the current terminal.

    , subAccountLength :: Int
      -- ^ When shortening the names of sub accounts to make
      -- them fit, they will be this long.

    , colorPref :: CC.Colors
      -- ^ How many colors you want to see, or do it
      -- automatically.

    , timeZone :: DefaultTimeZone
      -- ^ When dates and times are given on the command line
      -- and they have no time zone, they are assumed to be in
      -- this time zone. This has no bearing on how dates are
      -- formatted in the output; for that, see dateFormat
      -- above.

    , radGroup :: RadGroup
      -- ^ The characters used for the radix point for numbers
      -- given on the command line (e.g. a full stop, or a
      -- comma) and for the digit group separator for
      -- numbers parsed from the command line (e.g. a full stop,
      -- or a comma). Affects how inputs are parsed. Has no bearing
      -- on how output is formatted; for that, see qtyFormat and
      -- balanceFormat above.
      
    , sensitive :: M.CaseSensitive
      -- ^ Whether pattern matches are case sensitive by default.
      
    , factory :: M.CaseSensitive
                 -> Text -> Exceptional Text (Text -> Bool)
      -- ^ Default factory for pattern matching
      
    , tokens :: [Ex.Token (L.Box Ly.LibertyMeta -> Bool)]
      -- ^ Default list of tokens used to filter postings.
      
    , postFilter :: [Ly.PostFilterFn]
      -- ^ The entire posting list is transformed by this
      -- function after it is filtered according to the tokens.
      
    , fields :: Fields.T Bool
      -- ^ Default fields to show in the report.
      
    , spacers :: Spacers.T Int
      -- ^ Default width for spacer fields. If any of these Ints are
      -- less than or equal to zero, there will be no spacer. There is
      -- never a spacer for fields that do not appear in the report.
      
    , showZeroBalances :: O.ShowZeroBalances
    }

newtype ReportWidth = ReportWidth { unReportWidth :: Int }
                      deriving (Eq, Show, Ord)

ymd :: Box -> X.Text
ymd p = X.pack (formatTime defaultTimeLocale fmt d) where
  d = Time.localDay
      . Bits.localTime
      . Q.dateTime
      . L.boxPostFam
      $ p
  fmt = "%Y-%m-%d"

qtyAsIs :: Box -> X.Text
qtyAsIs p = X.pack . show . Bits.unQty . Q.qty . L.boxPostFam $ p

balanceAsIs :: Bits.Commodity -> Bal.BottomLine -> X.Text
balanceAsIs _ n = case n of
  Bal.Zero -> X.pack "--"
  Bal.NonZero c -> X.pack . show . Bits.unQty . Bal.qty $ c

defaultWidth :: ReportWidth
defaultWidth = ReportWidth 80

columnsVarToWidth :: Maybe String -> ReportWidth
columnsVarToWidth ms = case ms of
  Nothing -> defaultWidth
  Just str -> case reads str of
    [] -> defaultWidth
    (i, []):[] -> if i > 0 then ReportWidth i else defaultWidth
    _ -> defaultWidth

defaultOptions ::
  DefaultTimeZone
  -> RadGroup
  -> S.Runtime
  -> T
defaultOptions dtz rg rt =
  T { drCrColors = Dark.drCrColors
    , baseColors = Dark.baseColors
    , dateFormat = ymd
    , qtyFormat = qtyAsIs
    , balanceFormat = balanceAsIs
    , payeeAllocation = A.allocation 40
    , accountAllocation = A.allocation 60
    , width = widthFromRuntime rt
    , subAccountLength = 2
    , colorPref = O.autoColors O.PrefAuto rt
    , timeZone = dtz
    , radGroup = rg
    , sensitive = M.Insensitive
    , factory = \s t -> (return (M.within s t))
    , tokens = []
    , postFilter = []
    , fields = defaultFields
    , spacers = defaultSpacerWidth
    , showZeroBalances = O.ShowZeroBalances False }

widthFromRuntime :: S.Runtime -> ReportWidth
widthFromRuntime rt = case S.screenWidth rt of
  Nothing -> defaultWidth
  Just w -> ReportWidth . S.unScreenWidth $ w

defaultFields :: Fields.T Bool
defaultFields =
  Fields.T { F.globalTransaction    = False
           , F.revGlobalTransaction = False
           , F.globalPosting        = False
           , F.revGlobalPosting     = False
           , F.fileTransaction      = False
           , F.revFileTransaction   = False
           , F.filePosting          = False
           , F.revFilePosting       = False
           , F.filtered             = False
           , F.revFiltered          = False
           , F.sorted               = False
           , F.revSorted            = False
           , F.visible              = False
           , F.revVisible           = False
           , F.lineNum              = False
           , F.date                 = True
           , F.flag                 = False
           , F.number               = False
           , F.payee                = True
           , F.account              = True
           , F.postingDrCr          = True
           , F.postingCmdty         = True
           , F.postingQty           = True
           , F.totalDrCr            = True
           , F.totalCmdty           = True
           , F.totalQty             = True
           , F.tags                 = False
           , F.memo                 = False
           , F.filename             = False }

defaultSpacerWidth :: Spacers.T Int
defaultSpacerWidth =
  Spacers.T { S.globalTransaction    = 1
            , S.revGlobalTransaction = 1
            , S.globalPosting        = 1
            , S.revGlobalPosting     = 1
            , S.fileTransaction      = 1
            , S.revFileTransaction   = 1
            , S.filePosting          = 1
            , S.revFilePosting       = 1
            , S.filtered             = 1
            , S.revFiltered          = 1
            , S.sorted               = 1
            , S.revSorted            = 1
            , S.visible              = 1
            , S.revVisible           = 1
            , S.lineNum              = 1
            , S.date                 = 1
            , S.flag                 = 1
            , S.number               = 1
            , S.payee                = 4
            , S.account              = 1
            , S.postingDrCr          = 1
            , S.postingCmdty         = 1
            , S.postingQty           = 1
            , S.totalDrCr            = 1
            , S.totalCmdty           = 1 }