content: -- Copyright (c) 2016-present, Facebook, Inc.
  -- All rights reserved.
  --
  -- This source code is licensed under the BSD-style license found in the
  -- LICENSE file in the root directory of this source tree.
  
  
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE NamedFieldPuns #-}
  
  module Duckling.AmountOfMoney.Helpers
    ( currencyOnly
    , valueOnly
    , isSimpleAmountOfMoney
    , isCent
    , isCents
    , isDollarCoin
    , isCurrencyOnly
    , isDime
    , isMoneyWithValue
    , isWithoutCents
    , withCents
    , withInterval
    , withMax
    , withMin
    , withValue
    , mkLatent
    , dollarCoins
    )
    where
  
  import Data.HashMap.Strict (HashMap)
  import Data.Maybe (isJust)
  import Data.String
  import Data.Text (Text)
  import Prelude
  
  import Duckling.AmountOfMoney.Types
    ( Currency (..)
    , AmountOfMoneyData (..)
    , amountOfMoneyData'
    )
  import Duckling.Numeral.Types (getIntValue, isInteger)
  import Duckling.Dimensions.Types
  import Duckling.Types hiding (Entity(..))
  
  import qualified Data.HashMap.Strict as HashMap
  
  -- -----------------------------------------------------------------
  -- Dollar coin Types
  
  dollarCoins :: HashMap Text Double
  dollarCoins = HashMap.fromList
    [ ("nickel", 0.05)
    , ("nickels", 0.05)
    , ("dime", 0.1)
    , ("dimes", 0.1)
    , ("quarter", 0.25)
    , ("quarters", 0.25)
    ]
  
  -- -----------------------------------------------------------------
  -- Patterns
  
  isCents :: Predicate
  isCents (Token AmountOfMoney AmountOfMoneyData{value = Just _, currency = Cent}) = True
  isCents _ = False
  
  isWithoutCents :: Predicate
  isWithoutCents (Token AmountOfMoney AmountOfMoneyData{currency = Cent}) = False
  isWithoutCents (Token AmountOfMoney AmountOfMoneyData{value = Just v}) = isInteger v
  isWithoutCents _ = False
  
  isMoneyWithValue :: Predicate
  isMoneyWithValue (Token AmountOfMoney AmountOfMoneyData{value = v1, minValue = v2, maxValue = v3}) =
   any isJust [v1, v2, v3]
  isMoneyWithValue _ = False
  
  isCurrencyOnly :: Predicate
  isCurrencyOnly (Token AmountOfMoney AmountOfMoneyData
    {value = Nothing, minValue = Nothing, maxValue = Nothing}) = True
  isCurrencyOnly _ = False
  
  isDollarCoin :: Predicate
  isDollarCoin (Token AmountOfMoney AmountOfMoneyData{value = Just d, currency}) =
    elem d [0.05, 0.1, 0.25] && elem currency [Dollar, AUD, CAD, JMD,
                                               NZD,    SGD, TTD, USD]
  isDollarCoin _ = False
  
  isSimpleAmountOfMoney :: Predicate
  isSimpleAmountOfMoney (Token AmountOfMoney AmountOfMoneyData
    {value = Just _, minValue = Nothing, maxValue = Nothing}) = True
  isSimpleAmountOfMoney _ = False
  
  isDime :: Predicate
  isDime (Token AmountOfMoney AmountOfMoneyData
    {value = Just d, currency = Cent}) =
    maybe False (\i -> (i `mod` 10) == 0) $ getIntValue d
  isDime _ = False
  
  isCent :: Predicate
  isCent (Token AmountOfMoney AmountOfMoneyData
    {value = Just c, currency = Cent}) =
    maybe False (\i -> i >= 0 && i <= 9) $ getIntValue c
  isCent _ = False
  
  -- -----------------------------------------------------------------
  -- Production
  
  currencyOnly :: Currency -> AmountOfMoneyData
  currencyOnly c = amountOfMoneyData'{currency = c}
  
  valueOnly :: Double -> AmountOfMoneyData
  valueOnly x = amountOfMoneyData'{value = Just x}
  
  withValue :: Double -> AmountOfMoneyData -> AmountOfMoneyData
  withValue x fd = fd {value = Just x}
  
  withCents :: Double -> AmountOfMoneyData -> AmountOfMoneyData
  withCents x fd@AmountOfMoneyData {value = Just value} = fd
    {value = Just $ value + x / 100}
  withCents x AmountOfMoneyData {value = Nothing} =
    amountOfMoneyData'{value = Just x, currency = Cent}
  
  withInterval :: (Double, Double) -> AmountOfMoneyData -> AmountOfMoneyData
  withInterval (from, to) fd = fd
    {minValue = Just from, maxValue = Just to, value = Nothing}
  
  withMin :: Double -> AmountOfMoneyData -> AmountOfMoneyData
  withMin x fd = fd {minValue = Just x}
  
  withMax :: Double -> AmountOfMoneyData -> AmountOfMoneyData
  withMax x fd = fd {maxValue = Just x}
  
  mkLatent :: AmountOfMoneyData -> AmountOfMoneyData
  mkLatent fd = fd {latent = True}
  