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

{-|
[@ISO639-1@]        it

[@ISO639-2B@]       ita

[@ISO639-3@]        ita

[@Native name@]     Italiano

[@English name@]    Italian
-}

module Text.Numeral.Language.IT
    ( -- * Language entry
      entry
      -- * Conversions
    , cardinal
    , ordinal
      -- * Structure
    , struct
    , rule
      -- * Bounds
    , bounds
    ) where


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

import "base" Data.Bool     ( otherwise )
import "base" Data.Function ( ($), const, fix )
import "base" Data.Maybe    ( Maybe(Just) )
import "base" Data.Ord      ( Ord )
import "base" Prelude       ( Integral, (-), negate )
import "base-unicode-symbols" Data.Function.Unicode ( () )
import "base-unicode-symbols" Data.List.Unicode     ( () )
import "base-unicode-symbols" Data.Ord.Unicode      ( () )
import "base-unicode-symbols" Prelude.Unicode       (  )
import qualified "containers" Data.Map as M ( fromList, lookup )
import           "this" Text.Numeral
import qualified "this" Text.Numeral.BigNum  as BN
import qualified "this" Text.Numeral.Exp     as E
import qualified "this" Text.Numeral.Grammar as G
import           "this" Text.Numeral.Misc ( dec )
import "this" Text.Numeral.Entry
import "text" Data.Text ( Text )


--------------------------------------------------------------------------------
-- IT
--------------------------------------------------------------------------------

entry  Entry
entry = emptyEntry
    { entIso639_1       = Just "it"
    , entIso639_2       = ["ita"]
    , entIso639_3       = Just "ita"
    , entNativeNames    = ["Italiano"]
    , entEnglishName    = Just "Italian"
    , entCardinal       = Just Conversion
                          { toNumeral   = cardinal
                          , toStructure = struct
                          }
    , entOrdinal        = Just Conversion
                          { toNumeral   = ordinal
                          , toStructure = struct
                          }
    }

cardinal  (G.Masculine i, G.Feminine i, Integral α, E.Scale α)
          i  α  Maybe Text
cardinal inf = cardinalRepr inf  struct

ordinal  (G.Masculine i, G.Feminine i, Integral α, E.Scale α)
         i  α  Maybe Text
ordinal inf = ordinalRepr inf  struct

rule  ( Integral α, Ord α
       , E.Unknown β, E.Lit β, E.Add β, E.Mul β
       )
      Rule α β
rule = findRule (   0, lit         )
              [ (  11, add   10 L  )
              , (  17, add   10 R  )
              , (  20, lit         )
              , (  21, add   20 R  )
              , (  30, mul   10 R L)
              , ( 100, step  100   10 R L)
              , (1000, step 1000 1000 R L)
              ]
                (dec 6 - 1)

struct  ( Integral α, E.Scale α
         , E.Unknown β, E.Lit β, E.Neg β, E.Add β, E.Mul β, E.Scale β
         )
        α  β
struct = pos $ fix $ rule `combine` pelletierScale R L BN.rule

bounds  (Integral α)  (α, α)
bounds = let x = dec 60000 - 1 in (negate x, x)

genericRepr  Repr i
genericRepr = defaultRepr
              { reprAdd   = Just ()
              , reprMul   = Just ()
              , reprNeg   = Just $ \_ _  "meno "
              }
    where
      (Lit 10                 Lit 7) _ = "as"
      (Lit 10                 Lit 9) _ = "an"
      ((_ `Mul` Scale _ _ _)  _    ) _ = " "
      (_                      _    ) _ = ""


      (Lit n  Lit 10) _ | n  4 = "an"
      (_      Scale _ _ _) _ = " "
      (_      _          ) _ = ""

cardinalRepr  (G.Feminine i, G.Masculine i)  i  Exp i  Maybe Text
cardinalRepr = render genericRepr
               { reprValue = \inf n  M.lookup n (syms inf)
               , reprScale = BN.pelletierRepr
                               (BN.quantityName "ilione"  "ilioni")
                               (BN.quantityName "iliardo" "iliardi")
                               bigNumSyms
               }
    where
      syms inf =
          M.fromList
          [ (0, const "zero")
          , (1, \c  case c of
                       CtxAdd _ (Lit 10) _    "un"
                       _ | G.isFeminine  inf  "una"
                         | G.isMasculine inf  "un"
                         | otherwise          "uno"
            )
          , (2, forms "due" "do" "du")
          , (3, \c  case c of
                       CtxAdd R _        _  "tré"
                       CtxMul _ (Lit 10) _  "tren"
                       _                    "tre"
            )
          , (4, forms "quattro" "quattor" "quar")
          , (5, forms "cinque"  "quin"    "cinqu")
          , (6, forms "sei"     "se"      "sess")
          , (7, forms "sette"   "sette"   "sett")
          , (8, forms "otto"    "otto"    "ott")
          , (9, forms "nove"    "nove"    "nov")
          , (10, \c  case c of
                        CtxAdd _ (Lit _) _  "dici"
                        -- Last vowel removed because of a phonetic rule:
                        CtxMul _ (Lit _) (CtxAdd _ (Lit n) _)
                            | n  [1,8]     "t"
                        CtxMul R (Lit _) _  "ta"
                        _                   "dieci"
            )
          , (20, \c  case c of
                        CtxAdd _ (Lit n) _
                            | n  [1,8]    "vent"
                        _                  "venti"
            )
          , ( 100
            , let f c = case c of
                          CtxAdd _ (Lit 8)                      _   "cent"
                          CtxAdd _ (Lit 8 `Mul` Lit 10)         _   "cent"
                          CtxAdd _ (Lit 8 `Mul` Lit 10 `Add` _) _   "cent"
                          CtxMul _ (Lit _) c2  f c2
                          _  "cento"
              in f
            )
          , (1000, \c  case c of
                          CtxMul {}  "mila"
                          _          "mille"
            )
          ]

      forms  s  s  s  Ctx (Exp i)  s
      forms n a m c = case c of
                        CtxAdd _ (Lit 10) _  a
                        CtxMul _ (Lit 10) _  m
                        _                    n

ordinalRepr  (G.Feminine i)  i  Exp i  Maybe Text
ordinalRepr = render genericRepr
              { reprValue = \inf n  M.lookup n (syms inf)
              , reprScale = BN.pelletierRepr
                              ( BN.ordQuantityName "ilione" "ilionesimo"
                                                   "ilioni" "ilionesimo"
                              )
                              ( BN.ordQuantityName "iliardo" "iliardesimo"
                                                   "iliardi" "iliardesimo"
                              )
                              bigNumSyms
              }
    where
      syms inf =
          M.fromList
          [ (0, const "zero")
          , (1, forms "prima" "primo" "unesimo" "uno" "un" "un")
          , (2, forms "seconda" "secondo" "duesimo" "due" "do" "due")
          , (3, \c  case c of
                       CtxEmpty
                         | G.isFeminine inf  "terza"
                         | otherwise         "terzo"
                       _ | isOutside R c     "treesimo"
                       CtxAdd R _        _   "tré"
                       CtxMul _ (Lit 10) _   "tren"
                       _                     "tre"
            )
          , (4, forms "quarta"  "quarto"  "quattresimo" "quattro" "quattor" "quar")
          , (5, forms "quinta"  "quinto"  "cinquesimo"  "cinque"  "quin"    "cinqu")
          , (6, forms "sesta"   "sesto"   "seiesimo"    "sei"     "se"      "sess")
          , (7, forms "settimo" "settimo" "settesimo"   "sette"   "sette"   "sett")
          , (8, forms "ottave"  "ottavo"  "ottesimo"    "otto"    "otto"    "ott")
          , (9, forms "nono"    "nono"    "novesimo"    "nove"    "nove"    "nov")
          , (10, \c  case c of
                        CtxAdd _ (Lit _) _ | isOutside R c  "dicesimo"
                                           | otherwise      "dici"
                        -- Last vowel removed because of a phonetic rule:
                        CtxMul _ (Lit _) (CtxAdd _ (Lit n) _)
                            | n  [1,8]                     "t"
                        CtxMul R (Lit _) _ | isOutside R c  "tesimo"
                                           | otherwise      "ta"
                        _                  | isOutside R c  "decimo"
                                           | otherwise      "dieci"
            )
          , (20, \c  case c of
                        _ | isOutside R c   "ventesimo"
                        CtxAdd _ (Lit n) _
                          | n  [1,8]       "vent"
                        _                   "venti"
            )
          , ( 100
            , let f c = case c of
                          _ | isOutside R c                         "centesimo"
                          CtxAdd _ (Lit 8)                      _   "cent"
                          CtxAdd _ (Lit 8 `Mul` Lit 10)         _   "cent"
                          CtxAdd _ (Lit 8 `Mul` Lit 10 `Add` _) _   "cent"
                          CtxMul _ (Lit _) c2                       f c2
                          _                                         "cento"
              in f
            )
          , (1000, \c  case c of
                          _ | isOutside R c  "millesimo"
                          CtxMul {}          "mila"
                          _                  "mille"
            )
          ]
          where
            forms  s  s  s  s  s  s  Ctx (Exp i)  s
            forms fo1 o1 o2 n a m c =
                case c of
                  CtxEmpty
                    | G.isFeminine inf  fo1
                    | otherwise         o1
                  _ | isOutside R c     o2
                  CtxAdd _ (Lit 10) _   a
                  CtxMul _ (Lit 10) _   m
                  _                     n

bigNumSyms  [(, Ctx (Exp i)  Text)]
bigNumSyms =
  [ (6, BN.forms "sest" "sex"    "ses"    "sexa"   "ses")
  , (7, BN.forms "sett" "septen" "septem" "septua" "septin")
  , (8, BN.forms "ott"  "otto"   "otto"   "otto"   "ottin")
  ]