{-# LANGUAGE NoImplicitPrelude
           , OverloadedStrings
           , PackageImports
           , UnicodeSyntax
  #-}

module Text.Numeral.BigNum
  ( cardinal
  , rule
  , cardinalRepr
  , symMap
  , forms

  , PostfixRepr
  , scaleRepr
  , pelletierRepr

  , quantityName
  , ordQuantityName
  ) where


-------------------------------------------------------------------------------
-- Imports
-------------------------------------------------------------------------------

import "base" Data.Bool     ( otherwise )
import "base" Data.Function ( ($), const, fix )
import "base" Data.Functor  ( (<$>) )
import "base" Data.Maybe    ( Maybe(Nothing, Just) )
import "base" Prelude       ( Integral, (-) )
import "base-unicode-symbols"       Data.Eq.Unicode       ( () )
import "base-unicode-symbols"       Data.Function.Unicode ( () )
import "base-unicode-symbols"       Data.List.Unicode     ( () )
import "base-unicode-symbols"       Data.Monoid.Unicode   ( () )
import "base-unicode-symbols"       Prelude.Unicode       (  )
import "containers-unicode-symbols" Data.Map.Unicode      ( () )
import "this"                       Text.Numeral
import "this"                       Text.Numeral.Misc     ( dec )
import qualified "containers" Data.Map as M ( Map, fromList, lookup )
import qualified "this"       Text.Numeral.Exp as E
import "text" Data.Text ( Text )


--------------------------------------------------------------------------------
-- Language of Big Numbers
--------------------------------------------------------------------------------

cardinal  (Integral α)  inf  α  Maybe Text
cardinal inf = render cardinalRepr inf  (pos $ fix rule)

rule  (Integral α, E.Unknown β, E.Lit β, E.Add β, E.Mul β)  Rule α β
rule = findRule (   1, lit         )
              [ (  11, add   10 L  )
              , (  20, mul   10 L L)
              , ( 100, lit         )
              , ( 101, add  100 L  )
              , ( 200, mul  100 R L)
              , (1000, lit         )
              , (1001, add 1000 L  )
              , (2000, mul 1000 R L)
              ]
                (dec 4 - 1)

cardinalRepr  Repr inf
cardinalRepr =
    defaultRepr { reprValue = \_ n  M.lookup n symMap
                , reprAdd   = Just $ \_ _ _  ""
                , reprMul   = Just $ \_ _ _  ""
                }

symMap  (Integral α)  M.Map α (Ctx (Exp i)  Text)
symMap = M.fromList
         [ (1, forms "m"     "un"       "un"       ""        "")
         , (2, forms "b"     "duo"      "duo"      "vi"      "du")
         , (3, forms "tr"    "tre"      "tres"     "tri"     "tre")
         , (4, forms "quadr" "quattuor" "quattuor" "quadra"  "quadri")
         , (5, forms "quint" "quin"     "quinqua"  "quinqua" "quin")
         , (6, forms "sext"  "sex"      "ses"      "sexa"    "ses")
         , (7, forms "sept"  "septen"   "septem"   "septua"  "septin")
         , (8, forms "oct"   "octo"     "octo"     "octo"    "octin")
         , (9, forms "non"   "novem"    "novem"    "nona"    "non")
         , (10, \c  case c of
                       CtxAdd _ (Lit 100) _               "deci"
                       CtxMul _ _ (CtxAdd L (Lit 100) _)  "ginta"
                       CtxMul {}                          "gint"
                       _                                  "dec"
           )
         , (100, \c  case c of
                        CtxMul _ (Lit n) _
                            | n  [2,3,6]  "cent"
                            | otherwise    "gent"
                        _                  "cent"
           )
         , (1000, const "millin")
         ]

forms  Text  Text  Text  Text  Text  Ctx (Exp i)  Text
forms t a1 a2 m1 m2 ctx =
    case ctx of
      CtxAdd _ (Lit 10)  _  a1
      CtxAdd {}             a2
      CtxMul _ (Lit 10)  _  m1
      CtxMul {}             m2
      _                     t


--------------------------------------------------------------------------------
-- Representations of scales
--------------------------------------------------------------------------------

-- | Function that renders the postfix part of a large number name. Or
-- more simply put, this calculates the \"illion\" part of
-- \"million\", \"billion\", etc.
type PostfixRepr i = i           -- ^ Current inflection.
                    Ctx (Exp i) -- ^ Context.
                    Text        -- ^ Postfix representation.

scaleRepr  PostfixRepr i
           [(, Ctx (Exp i)  Text)] -- ^ Additional symbol map entries.
           ScaleRepr i
scaleRepr pf syms inf _ _ e ctx = ( pf inf ctx) <$> render repr inf e
    where
      repr = cardinalRepr { reprValue = \_ n  M.lookup n syms' }
      syms' = M.fromList syms  symMap

pelletierRepr  PostfixRepr i -- ^Postfix for offset 0 names.
               PostfixRepr i -- ^Postfix for offset 3 names.
               [(, Ctx (Exp i)  Text)] -- ^ Additional symbol map entries.
               ScaleRepr i
pelletierRepr pf0 pf3 syms inf
              b o e ctx | o  0 = scaleRepr pf0 syms inf b o e ctx
                        | o  3 = scaleRepr pf3 syms inf b o e ctx
                        | otherwise = Nothing

quantityName  Text  Text  PostfixRepr i
quantityName s p _ ctx =
    case ctx of
      CtxMul _ (Lit 1) _  s
      CtxMul {}           p
      _                   s

ordQuantityName  Text  Text  Text  Text  PostfixRepr i
ordQuantityName sc so pc po _ ctx =
    case ctx of
      CtxMul _ (Lit 1) _ | outside    so
                         | otherwise  sc
      CtxMul {}          | outside    po
                         | otherwise  pc
      _                  | outside    so
                         | otherwise  sc
    where
      outside = isOutside R ctx