{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Data.Text.Format.Numbers
    ( PrettyCfg(..)
    , prettyF
    , prettyI
    )
where

import Data.Monoid
import qualified Data.Text as T

data PrettyCfg
    = PrettyCfg
    { PrettyCfg -> Int
pc_decimals :: !Int
      -- ^ Decimals to display
    , PrettyCfg -> Maybe Char
pc_thousandsSep :: !(Maybe Char)
      -- ^ Optional thousands separator
    , PrettyCfg -> Char
pc_decimalSep :: !Char
      -- ^ Decimal separator
    }

-- | Pretty print any number given a configuration
prettyF :: RealFrac i => PrettyCfg -> i -> T.Text
prettyF :: PrettyCfg -> i -> Text
prettyF PrettyCfg{Char
Int
Maybe Char
pc_decimalSep :: Char
pc_thousandsSep :: Maybe Char
pc_decimals :: Int
pc_decimalSep :: PrettyCfg -> Char
pc_thousandsSep :: PrettyCfg -> Maybe Char
pc_decimals :: PrettyCfg -> Int
..} i
n =
  let tpow :: Int
tpow = Int
10 Int -> Int -> Int
forall a b. (Num a, Integral b) => a -> b -> a
^ Int
pc_decimals
      lshift :: i
lshift = i
n i -> i -> i
forall a. Num a => a -> a -> a
* Int -> i
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
tpow
      lshiftr :: Int
lshiftr = i -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round i
lshift
      lshifti' :: Int
lshifti' = Int -> Int
forall a. Num a => a -> a
abs Int
lshiftr
      intPart :: Int
intPart = Int
lshifti' Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
tpow
      decPart :: Int
decPart = Int
lshifti' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
intPart Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
tpow
      preDecimal :: Text
preDecimal =
          if Int
lshiftr Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0
          then Maybe Char -> Int -> Text
prettyI Maybe Char
pc_thousandsSep (Int
intPart Int -> Int -> Int
forall a. Num a => a -> a -> a
* (-Int
1))
          else Maybe Char -> Int -> Text
prettyI Maybe Char
pc_thousandsSep Int
intPart
      postDecimal :: Text
postDecimal =
          if Int
pc_decimals Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0
          then Char -> Text -> Text
T.cons Char
pc_decimalSep (Int -> Char -> Text -> Text
T.justifyRight Int
pc_decimals Char
'0' (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Int -> String
forall a. Show a => a -> String
show Int
decPart)
          else Text
""
  in Text
preDecimal Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
postDecimal

-- | Pretty print an 'Int' given an optional thousands separator
prettyI :: Maybe Char -> Int -> T.Text
prettyI :: Maybe Char -> Int -> Text
prettyI Maybe Char
tsep Int
n =
  let ni :: Text
ni = String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Int -> String
forall a. Show a => a -> String
show (Int -> String) -> Int -> String
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a. Num a => a -> a
abs Int
n
      nis :: Text
nis =
          case Maybe Char
tsep of
            Just Char
s ->
                Text -> [Text] -> Text
T.intercalate (Char -> Text
T.singleton Char
s) ([Text] -> Text) -> [Text] -> Text
forall a b. (a -> b) -> a -> b
$
                [Text] -> [Text]
forall a. [a] -> [a]
reverse ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ (Text -> Text) -> [Text] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Text -> Text
T.reverse ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ Int -> Text -> [Text]
T.chunksOf Int
3 (Text -> [Text]) -> Text -> [Text]
forall a b. (a -> b) -> a -> b
$ Text -> Text
T.reverse Text
ni
            Maybe Char
Nothing ->
                Text
ni
  in if Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 then Text
"-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
nis else Text
nis