{- | Penny - double-entry accounting system

Penny is organized into a tree of modules, each with a name. Check out
the links for details on each component of Penny.

"Penny.Cabin" - Penny reports. Depends on Lincoln and Liberty.

"Penny.Copper" - the Penny parser. Depends on Lincoln.

"Penny.Liberty" - Penny command line parser helpers. Depends on
Lincoln and Copper.

"Penny.Lincoln" - the Penny core. Depends on no other Penny components.

"Penny.Shield" the Penny runtime environment

"Penny.Zinc" - the Penny command-line interface. Depends on Cabin,
Copper, Liberty, and Lincoln.

The dependencies are represented as a dot file in doc/dependencies.dot
in the Penny git repository.

This module exports a few functions that are useful for building a
simple command line program with default options. To make anything
more complicated you may need to import modules from deeper down
within the hierarchy, but for a program based on the defaults only you
should be able to import this module only (and nothing else).

-}
module Penny (
  -- * Default time zone
  Copper.DefaultTimeZone (DefaultTimeZone)
  , Copper.utcDefault
  , minsToDefaultTimeZone
    
    -- * Radix and grouping characters
  , Copper.RadGroup
  , Copper.periodComma
  , Copper.periodSpace
  , Copper.commaPeriod
  , Copper.commaSpace
    
    -- * Reports
  , Cabin.allReportsWithDefaults
    
    -- * Parser defaults
  , Z.T(..)
  , Z.defaultFromRuntime
    
    -- * Main function
  , defaultPenny
  , customPenny
    
    -- * Other useful stuff - for custom reports
  ) where

import qualified Penny.Cabin as Cabin
import qualified Penny.Copper as Copper
import qualified Penny.Lincoln as L
import qualified Penny.Shield as S
import qualified Penny.Zinc as Z

-- | Use to make a DefaultTimeZone based on the number of minutes the
-- time zone is offset from UTC. Make sure the argument you supply is
-- between (-840) and 840; otherwise your program will crash at
-- runtime.
minsToDefaultTimeZone :: Int -> Copper.DefaultTimeZone
minsToDefaultTimeZone i = case L.minsToOffset i of
  Nothing -> error $ "penny: error: minutes out of range of "
             ++ "allowed values for time zone"
  Just tzo -> Copper.DefaultTimeZone tzo

defaultPenny :: Copper.DefaultTimeZone -> Copper.RadGroup -> IO ()
defaultPenny dtz rg = do
  rt <- S.runtime
  let df = Z.defaultFromRuntime dtz rg
      rs = Cabin.allReportsWithDefaults dtz rg
  Z.runPenny rt dtz rg df rs

customPenny ::
  Copper.DefaultTimeZone
  -> Copper.RadGroup
  -> (S.Runtime -> Z.T)
  -> [Cabin.Report]
  -> IO ()
customPenny dtz rg gd rs = do
  rt <- S.runtime
  Z.runPenny rt dtz rg gd rs