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 NoRebindableSyntax #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Duckling.Time.RO.Rules
    ( rules ) where
  
  import Prelude
  
  import Duckling.Dimensions.Types
  import Duckling.Duration.Helpers (isGrain)
  import Duckling.Numeral.Helpers (parseInt)
  import Duckling.Regex.Types
  import Duckling.Time.Helpers
  import Duckling.Time.Types (TimeData(..))
  import Duckling.Types
  import qualified Data.Text as Text
  import qualified Duckling.Time.Types as TTime
  import qualified Duckling.TimeGrain.Types as TG
  
  ruleTheDayAfterTomorrow :: Rule
  ruleTheDayAfterTomorrow = Rule
    { name = "the day after tomorrow"
    , pattern =
      [ regex "poim(a|â)ine"
      ]
    , prod = \_ -> tt $ cycleNth TG.Day 2
    }
  
  ruleAcum :: Rule
  ruleAcum = Rule
    { name = "acum"
    , pattern =
      [ regex "(chiar)? ?acum|imediat"
      ]
    , prod = \_ -> tt now
    }
  
  ruleDupamiaza :: Rule
  ruleDupamiaza = Rule
    { name = "dupamiaza"
    , pattern =
      [ regex "dupamiaz(a|ă)|dup(a|ă) amiaz(a|ă)"
      ]
    , prod = \_ -> Token Time . mkLatent . partOfDay <$>
        interval TTime.Open (hour False 12) (hour False 19)
    }
  
  ruleValentinesDay :: Rule
  ruleValentinesDay = Rule
    { name = "valentine's day"
    , pattern =
      [ regex "sf\\.?((a|â)ntul)? Valentin"
      ]
    , prod = \_ -> tt $ monthDay 2 14
    }
  
  ruleSinceTimeofday :: Rule
  ruleSinceTimeofday = Rule
    { name = "since <time-of-day>"
    , pattern =
      [ regex "de|din"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ withDirection TTime.After td
        _ -> Nothing
    }
  
  ruleNewYearsDay :: Rule
  ruleNewYearsDay = Rule
    { name = "new year's day"
    , pattern =
      [ regex "(siua de )? an(ul)? nou"
      ]
    , prod = \_ -> tt $ monthDay 1 1
    }
  
  ruleOrdinalTrimestruYear :: Rule
  ruleOrdinalTrimestruYear = Rule
    { name = "<ordinal> trimestru <year>"
    , pattern =
      [ dimension Ordinal
      , Predicate $ isGrain TG.Quarter
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> do
          n <- getIntValue token
          tt $ cycleNthAfter False TG.Quarter (n - 1) td
        _ -> Nothing
    }
  
  ruleInNamedmonth :: Rule
  ruleInNamedmonth = Rule
    { name = "in <named-month>"
    , pattern =
      [ regex "(i|î)n"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (_:x:_) -> Just x
        _ -> Nothing
    }
  
  ruleLastCycleOfTime :: Rule
  ruleLastCycleOfTime = Rule
    { name = "last <cycle> of <time>"
    , pattern =
      [ regex "ultim(ul|a)"
      , dimension TimeGrain
      , regex "din"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleLastOf grain td
        _ -> Nothing
    }
  
  ruleIntroNamedday :: Rule
  ruleIntroNamedday = Rule
    { name = "intr-o <named-day>"
    , pattern =
      [ regex "((i|î)n(tr)?(\\-?o)?)"
      , Predicate isADayOfWeek
      ]
    , prod = \tokens -> case tokens of
        (_:x:_) -> Just x
        _ -> Nothing
    }
  
  ruleMonthDdddInterval :: Rule
  ruleMonthDdddInterval = Rule
    { name = "<month> dd-dd (interval)"
    , pattern =
      [ Predicate isAMonth
      , regex "(3[01]|[12]\\d|0?[1-9])"
      , regex "\\-"
      , regex "(3[01]|[12]\\d|0?[1-9])"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:
         Token RegexMatch (GroupMatch (m1:_)):
         _:
         Token RegexMatch (GroupMatch (m2:_)):
         _) -> do
          d1 <- parseInt m1
          d2 <- parseInt m2
          dom1 <- intersect (dayOfMonth d1) td
          dom2 <- intersect (dayOfMonth d2) td
          Token Time <$> interval TTime.Closed dom1 dom2
        _ -> Nothing
    }
  
  ruleInDuration :: Rule
  ruleInDuration = Rule
    { name = "in <duration>"
    , pattern =
      [ regex "(i|î)n"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) -> tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleIeri :: Rule
  ruleIeri = Rule
    { name = "ieri"
    , pattern =
      [ regex "ieri"
      ]
    , prod = \_ -> tt . cycleNth TG.Day $ - 1
    }
  
  ruleCycleAcesta :: Rule
  ruleCycleAcesta = Rule
    { name = "<cycle> acesta"
    , pattern =
      [ regex "aceasta|acest|(a|ă)sta"
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_) -> tt $ cycleNth grain 0
        _ -> Nothing
    }
  
  ruleYearLatent2 :: Rule
  ruleYearLatent2 = Rule
    { name = "year (latent)"
    , pattern =
      [ Predicate $ isIntegerBetween 2101 10000
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          v <- getIntValue token
          tt . mkLatent $ year v
        _ -> Nothing
    }
  
  ruleAzi :: Rule
  ruleAzi = Rule
    { name = "azi"
    , pattern =
      [ regex "a(st(a|ă))?zi"
      ]
    , prod = \_ -> tt today
    }
  
  ruleNoon :: Rule
  ruleNoon = Rule
    { name = "noon"
    , pattern =
      [ regex "noon"
      ]
    , prod = \_ -> tt $ hour False 12
    }
  
  ruleTimeTrecuta :: Rule
  ruleTimeTrecuta = Rule
    { name = "<time> trecut[aă]?"
    , pattern =
      [ dimension Time
      , regex "(trecut(a|ă)?)"
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ predNth (-1) False td
        _ -> Nothing
    }
  
  ruleThisnextDayofweek :: Rule
  ruleThisnextDayofweek = Rule
    { name = "this|next <day-of-week>"
    , pattern =
      [ Predicate isADayOfWeek
      , regex "aceasta|(a|ă)sta|urm(a|ă)toare"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) -> tt $ predNth 0 True td
        _ -> Nothing
    }
  
  ruleBetweenTimeofdayAndTimeofdayInterval :: Rule
  ruleBetweenTimeofdayAndTimeofdayInterval = Rule
    { name = "between <time-of-day> and <time-of-day> (interval)"
    , pattern =
      [ regex "(i|î)ntre"
      , Predicate isATimeOfDay
      , regex "(s|ș)i"
      , Predicate isATimeOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleUrmatoareaCycle :: Rule
  ruleUrmatoareaCycle = Rule
    { name = "urmatoarea <cycle>"
    , pattern =
      [ regex "(urm(a|ă)to(area|rul)|viito(are|r))"
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_) -> tt $ cycleNth grain 1
        _ -> Nothing
    }
  
  ruleCycleAcesta2 :: Rule
  ruleCycleAcesta2 = Rule
    { name = "<cycle> acesta"
    , pattern =
      [ dimension TimeGrain
      , regex "aceasta|acest|(a|ă)sta|curent(a|ă)"
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_) -> tt $ cycleNth grain 0
        _ -> Nothing
    }
  
  ruleCraciun :: Rule
  ruleCraciun = Rule
    { name = "craciun"
    , pattern =
      [ regex "(ziua de )?cr(a|ă)ciun"
      ]
    , prod = \_ -> tt $ monthDay 12 25
    }
  
  ruleTrimestruNumeralYear :: Rule
  ruleTrimestruNumeralYear = Rule
    { name = "trimestru <number> <year>"
    , pattern =
      [ Predicate $ isGrain TG.Quarter
      , dimension Numeral
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:token:Token Time td:_) -> do
          v <- getIntValue token
          tt $ cycleNthAfter False TG.Quarter (v - 1) td
        _ -> Nothing
    }
  
  ruleDdmm :: Rule
  ruleDdmm = Rule
    { name = "dd/mm"
    , pattern =
      [ regex "(3[01]|[12]\\d|0?[1-9])/(0?[1-9]|1[0-2])"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (dd:mm:_)):_) -> do
          m <- parseInt mm
          d <- parseInt dd
          tt $ monthDay m d
        _ -> Nothing
    }
  
  ruleLaTimeofday :: Rule
  ruleLaTimeofday = Rule
    { name = "la <time-of-day>"
    , pattern =
      [ regex "la|@ (ora)?"
      , Predicate isATimeOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleBlackFriday :: Rule
  ruleBlackFriday = Rule
    { name = "black friday"
    , pattern =
      [ regex "black frid?day"
      ]
    , prod = \_ -> tt $ nthDOWOfMonth 4 5 11
    }
  
  ruleChristmasEve :: Rule
  ruleChristmasEve = Rule
    { name = "christmas eve"
    , pattern =
      [ regex "ajun(ul)? (de )?cr(a|ă)ciun"
      ]
    , prod = \_ -> tt $ monthDay 12 24
    }
  
  rulePentruDuration :: Rule
  rulePentruDuration = Rule
    { name = "pentru <duration>"
    , pattern =
      [ regex "pentru"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) -> tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleHourofdayIntegerAsRelativeMinutes :: Rule
  ruleHourofdayIntegerAsRelativeMinutes = Rule
    { name = "<hour-of-day> <integer> (as relative minutes)"
    , pattern =
      [ Predicate $ and . sequence [isNotLatent, isAnHourOfDay]
      , Predicate $ isIntegerBetween 1 59
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:
         token:
         _) -> do
          n <- getIntValue token
          tt $ hourMinute is12H hours n
        _ -> Nothing
    }
  
  ruleHourofdaySfert :: Rule
  ruleHourofdaySfert = Rule
    { name = "<hour-of-day> sfert"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "((s|ș)i )?(un )?sfert"
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:
         _) -> tt $ hourMinute is12H hours 15
        _ -> Nothing
    }
  
  ruleHourofdayJumatate :: Rule
  ruleHourofdayJumatate = Rule
    { name = "<hour-of-day> sfert"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "((s|ș)i )?jum(a|ă)tate|jumate"
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:
         _) -> tt $ hourMinute is12H hours 30
        _ -> Nothing
    }
  
  ruleDiseara :: Rule
  ruleDiseara = Rule
    { name = "diseara"
    , pattern =
      [ regex "disear(a|ă)|((i|î)n aceas(a|ă) )?sear(a|ă)"
      ]
    , prod = \_ -> do
        td2 <- interval TTime.Open (hour False 18) (hour False 0)
        Token Time . partOfDay <$> intersect today td2
    }
  
  ruleIntersectBy :: Rule
  ruleIntersectBy = Rule
    { name = "intersect by \",\""
    , pattern =
      [ Predicate isNotLatent
      , regex ","
      , Predicate isNotLatent
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:_:Token Time td2:_) ->
          Token Time <$> intersect td1 td2
        _ -> Nothing
    }
  
  ruleDayofmonthNumeral :: Rule
  ruleDayofmonthNumeral = Rule
    { name = "<day-of-month> (number)"
    , pattern =
      [ Predicate isDOMInteger
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          v <- getIntValue token
          tt . mkLatent $ dayOfMonth v
        _ -> Nothing
    }
  
  ruleNthTimeAfterTime :: Rule
  ruleNthTimeAfterTime = Rule
    { name = "nth <time> after <time>"
    , pattern =
      [ dimension Ordinal
      , dimension Time
      , regex "dup(a|ă)"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (token:Token Time td1:_:Token Time td2:_) -> do
          v <- getIntValue token
          tt $ predNthAfter (v - 1) td1 td2
        _ -> Nothing
    }
  
  ruleMmdd :: Rule
  ruleMmdd = Rule
    { name = "mm/dd"
    , pattern =
      [ regex "(0?[1-9]|1[0-2])/(3[01]|[12]\\d|0?[1-9])"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (mm:dd:_)):_) -> do
          m <- parseInt mm
          d <- parseInt dd
          tt $ monthDay m d
        _ -> Nothing
    }
  
  ruleTimeofdayLatent :: Rule
  ruleTimeofdayLatent = Rule
    { name = "time-of-day (latent)"
    , pattern =
      [ Predicate $ isIntegerBetween 0 23
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          v <- getIntValue token
          tt . mkLatent $ hour True v
        _ -> Nothing
    }
  
  ruleFromTimeofdayTimeofdayInterval :: Rule
  ruleFromTimeofdayTimeofdayInterval = Rule
    { name = "from <time-of-day> - <time-of-day> (interval)"
    , pattern =
      [ regex "(dup(a|ă)|(i|î)ncep(a|â)nd cu)"
      , Predicate isATimeOfDay
      , regex "(dar |(s|ș)i )?((i|î)nainte|p(a|â)n(a|ă) la( de)?)"
      , Predicate isATimeOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleSeason3 :: Rule
  ruleSeason3 = Rule
    { name = "season"
    , pattern =
      [ regex "primavar(a|ă)"
      ]
    , prod = \_ -> Token Time <$>
        interval TTime.Open (monthDay 3 20) (monthDay 6 21)
    }
  
  ruleUrmatoareleNCycle :: Rule
  ruleUrmatoareleNCycle = Rule
    { name = "urmatoarele n <cycle>"
    , pattern =
      [ regex "urm(a|ă)to(arele|rii|area)"
      , Predicate $ isIntegerBetween 1 9999
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:token:Token TimeGrain grain:_) -> do
          v <- getIntValue token
          tt $ cycleN True grain v
        _ -> Nothing
    }
  
  ruleSeason :: Rule
  ruleSeason = Rule
    { name = "season"
    , pattern =
      [ regex "toamn(a|ă)"
      ]
    , prod = \_ -> Token Time <$>
        interval TTime.Open (monthDay 9 23) (monthDay 12 21)
    }
  
  ruleDupaDuration :: Rule
  ruleDupaDuration = Rule
    { name = "dupa <duration>"
    , pattern =
      [ regex "dup(a|ă)"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) ->
          tt . withDirection TTime.After $ inDuration dd
        _ -> Nothing
    }
  
  ruleNewYearsEve :: Rule
  ruleNewYearsEve = Rule
    { name = "new year's eve"
    , pattern =
      [ regex "(ajun(ul)? )?(de )?an(ul)? nou"
      ]
    , prod = \_ -> tt $ monthDay 12 31
    }
  
  ruleByTheEndOfTime :: Rule
  ruleByTheEndOfTime = Rule
    { name = "by the end of <time>"
    , pattern =
      [ regex "p(a|î)n(a|ă) ((i|î)n|la)"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> interval TTime.Closed now td
        _ -> Nothing
    }
  
  ruleNameddayPeDayofmonthNumeral :: Rule
  ruleNameddayPeDayofmonthNumeral = Rule
    { name = "<named-day> pe <day-of-month> (number)"
    , pattern =
      [ Predicate isADayOfWeek
      , regex "pe"
      , Predicate isDOMInteger
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_:token:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleDayofmonthNonOrdinalNamedmonth :: Rule
  ruleDayofmonthNonOrdinalNamedmonth = Rule
    { name = "<day-of-month> (non ordinal) <named-month>"
    , pattern =
      [ Predicate isDOMInteger
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleIntersect :: Rule
  ruleIntersect = Rule
    { name = "intersect"
    , pattern =
      [ Predicate isNotLatent
      , Predicate isNotLatent
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:Token Time td2:_) ->
          Token Time <$> intersect td1 td2
        _ -> Nothing
    }
  
  ruleAboutTimeofday :: Rule
  ruleAboutTimeofday = Rule
    { name = "about <time-of-day>"
    , pattern =
      [ regex "(cam|aproximativ|(i|î)n jur de)"
      , Predicate isATimeOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleUntilTimeofday :: Rule
  ruleUntilTimeofday = Rule
    { name = "until <time-of-day>"
    , pattern =
      [ regex "p(a|î)n(a|ă) ((i|î)n|la)"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) ->
          tt $ withDirection TTime.Before td
        _ -> Nothing
    }
  
  ruleDayofmonthnumberNamedmonth :: Rule
  ruleDayofmonthnumberNamedmonth = Rule
    { name = "<day-of-month>(number) <named-month>"
    , pattern =
      [ Predicate isDOMInteger
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleIntreDatetimeSiDatetimeInterval :: Rule
  ruleIntreDatetimeSiDatetimeInterval = Rule
    { name = "intre <datetime> si <datetime> (interval)"
    , pattern =
      [ regex "(i|î)nre"
      , dimension Time
      , regex "(s|ș)i"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleNthTimeOfTime :: Rule
  ruleNthTimeOfTime = Rule
    { name = "nth <time> of <time>"
    , pattern =
      [ dimension Ordinal
      , dimension Time
      , regex "din"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (token:Token Time td1:_:Token Time td2:_) -> do
          v <- getIntValue token
          Token Time . predNth (v - 1) False <$> intersect td2 td1
        _ -> Nothing
    }
  
  ruleTimePartofday :: Rule
  ruleTimePartofday = Rule
    { name = "<time> <part-of-day>"
    , pattern =
      [ dimension Time
      , Predicate isAPartOfDay
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:Token Time td2:_) ->
          Token Time <$> intersect td1 td2
        _ -> Nothing
    }
  
  ruleWeekend :: Rule
  ruleWeekend = Rule
    { name = "week-end"
    , pattern =
      [ regex "(week(\\s|\\-)?end|wkend)"
      ]
    , prod = \_ -> tt weekend
    }
  
  rulePeDayofmonthNonOrdinal :: Rule
  rulePeDayofmonthNonOrdinal = Rule
    { name = "pe <day-of-month> (non ordinal)"
    , pattern =
      [ regex "pe"
      , Predicate isDOMInteger
      ]
    , prod = \tokens -> case tokens of
        (_:token:_) -> do
          v <- getIntValue token
          tt . mkLatent $ dayOfMonth v
        _ -> Nothing
    }
  
  ruleTimeAceastaacestaasta :: Rule
  ruleTimeAceastaacestaasta = Rule
    { name = "<time> (aceasta|acesta|[aă]sta)"
    , pattern =
      [ dimension Time
      , regex "aceasta|(a|ă)sta|urm(a|ă)toare"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) ->
          tt $ predNth 0 False td
        _ -> Nothing
    }
  
  ruleEomendOfMonth :: Rule
  ruleEomendOfMonth = Rule
    { name = "EOM|End of month"
    , pattern =
      [ regex "sf(a|â)r(s|ș)itul lunii"
      ]
    , prod = \_ -> tt $ cycleNth TG.Month 1
    }
  
  rulePartofdayAsta :: Rule
  rulePartofdayAsta = Rule
    { name = "<part-of-day> asta"
    , pattern =
      [ Predicate isAPartOfDay
      , regex "asta"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) ->
          Token Time . partOfDay <$> intersect today td
        _ -> Nothing
    }
  
  ruleUltimaCycle :: Rule
  ruleUltimaCycle = Rule
    { name = "ultima <cycle>"
    , pattern =
      [ regex "ultim(a|ul)"
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_) -> tt . cycleNth grain $ - 1
        _ -> Nothing
    }
  
  ruleCycleUrmatoare :: Rule
  ruleCycleUrmatoare = Rule
    { name = "<cycle> urmatoare"
    , pattern =
      [ dimension TimeGrain
      , regex "(urm(a|ă)to(are|r)|viito(are|r))"
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_) -> tt $ cycleNth grain 1
        _ -> Nothing
    }
  
  ruleTheDayofmonthNumeral :: Rule
  ruleTheDayofmonthNumeral = Rule
    { name = "the <day-of-month> (number)"
    , pattern =
      [ regex "pe"
      , Predicate isDOMInteger
      ]
    , prod = \tokens -> case tokens of
        (_:token:_) -> do
          v <- getIntValue token
          tt $ dayOfMonth v
        _ -> Nothing
    }
  
  ruleYyyymmdd :: Rule
  ruleYyyymmdd = Rule
    { name = "yyyy-mm-dd"
    , pattern =
      [ regex "(\\d{2,4})-(0?[1-9]|1[0-2])-(3[01]|[12]\\d|0?[1-9])"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (yy:mm:dd:_)):_) -> do
          y <- parseInt yy
          m <- parseInt mm
          d <- parseInt dd
          tt $ yearMonthDay y m d
        _ -> Nothing
    }
  
  ruleCycleTrecut :: Rule
  ruleCycleTrecut = Rule
    { name = "<cycle> trecut"
    , pattern =
      [ dimension TimeGrain
      , regex "trecut(a|ă)?"
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_) ->
          tt . cycleNth grain $ - 1
        _ -> Nothing
    }
  
  ruleDayofmonthNumeralOfNamedmonth :: Rule
  ruleDayofmonthNumeralOfNamedmonth = Rule
    { name = "<day-of-month> (number) of <named-month>"
    , pattern =
      [ Predicate isDOMInteger
      , regex "din"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleDurationInainteDeTime :: Rule
  ruleDurationInainteDeTime = Rule
    { name = "<duration> inainte de <time>"
    , pattern =
      [ dimension Duration
      , regex "(i|î)nainte de"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_:Token Time td:_) ->
          tt $ durationBefore dd td
        _ -> Nothing
    }
  
  ruleDayofmonthNonOrdinalOfNamedmonth :: Rule
  ruleDayofmonthNonOrdinalOfNamedmonth = Rule
    { name = "<day-of-month> (non ordinal) of <named-month>"
    , pattern =
      [ Predicate isDOMInteger
      , regex "din"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleDurationInUrma :: Rule
  ruleDurationInUrma = Rule
    { name = "<duration> in urma"
    , pattern =
      [ dimension Duration
      , regex "(i|î)n urm(a|ă)"
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_) -> tt $ durationAgo dd
        _ -> Nothing
    }
  
  ruleDurationDeAcum :: Rule
  ruleDurationDeAcum = Rule
    { name = "<duration> de acum"
    , pattern =
      [ dimension Duration
      , regex "de (acum|azi)"
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_) -> tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleSezonAnotimp :: Rule
  ruleSezonAnotimp = Rule
    { name = "sezon anotimp"
    , pattern =
      [ regex "var(a|ă)"
      ]
    , prod = \_ ->
        Token Time <$> interval TTime.Open (monthDay 6 21) (monthDay 9 23)
    }
  
  ruleSearaNoapte :: Rule
  ruleSearaNoapte = Rule
    { name = "sear[aă] noapte"
    , pattern =
      [ regex "sear(a|ă)|noapte"
      ]
    , prod = \_ -> Token Time . mkLatent . partOfDay <$>
        interval TTime.Open (hour False 18) (hour False 0)
    }
  
  ruleYearLatent :: Rule
  ruleYearLatent = Rule
    { name = "year (latent)"
    , pattern =
      [ Predicate $ isIntegerBetween 1 999
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          v <- getIntValue token
          tt . mkLatent $ year v
        _ -> Nothing
    }
  
  ruleSeason2 :: Rule
  ruleSeason2 = Rule
    { name = "season"
    , pattern =
      [ regex "iarn(a|ă)"
      ]
    , prod = \_ ->
        Token Time <$> interval TTime.Open (monthDay 12 21) (monthDay 3 20)
    }
  
  ruleUltimeleNCycle :: Rule
  ruleUltimeleNCycle = Rule
    { name = "ultimele n <cycle>"
    , pattern =
      [ regex "ultim(ele|ii|a)"
      , Predicate $ isIntegerBetween 1 9999
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:token:Token TimeGrain grain:_) -> do
          v <- getIntValue token
          tt $ cycleN True grain (- v)
        _ -> Nothing
    }
  
  ruleAfterTimeofday :: Rule
  ruleAfterTimeofday = Rule
    { name = "after <time-of-day>"
    , pattern =
      [ regex "dup(a|ă)"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ withDirection TTime.After td
        _ -> Nothing
    }
  
  ruleDimineata :: Rule
  ruleDimineata = Rule
    { name = "diminea[tț][aă]"
    , pattern =
      [ regex "diminea(t|ț)(a|ă)"
      ]
    , prod = \_ -> Token Time . mkLatent . partOfDay <$>
        interval TTime.Open (hour False 4) (hour False 12)
    }
  
  ruleTimeUrmatoarer :: Rule
  ruleTimeUrmatoarer = Rule
    { name = "<time> urm[aă]to(are|r)"
    , pattern =
      [ regex "urm(a|ă)to(are|r)"
      , Predicate isNotLatent
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ predNth 0 True td
        _ -> Nothing
    }
  
  ruleTimeofdayFix :: Rule
  ruleTimeofdayFix = Rule
    { name = "<time-of-day> fix"
    , pattern =
      [ Predicate isATimeOfDay
      , regex "(fix|exact)"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleHhmm :: Rule
  ruleHhmm = Rule
    { name = "hh:mm"
    , pattern =
      [ regex "((?:[01]?\\d)|(?:2[0-3]))[:.]([0-5]\\d)"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (hh:mm:_)):_) -> do
          h <- parseInt hh
          m <- parseInt mm
          tt $ hourMinute True h m
        _ -> Nothing
    }
  
  ruleMaine :: Rule
  ruleMaine = Rule
    { name = "maine"
    , pattern =
      [ regex "m(a|â)ine"
      ]
    , prod = \_ -> tt $ cycleNth TG.Day 1
    }
  
  ruleTimezone :: Rule
  ruleTimezone = Rule
    { name = "<time> timezone"
    , pattern =
      [ Predicate $ and . sequence [isNotLatent, isATimeOfDay]
      , regex "\\b(YEKT|YEKST|YAKT|YAKST|WITA|WIT|WIB|WGT|WGST|WFT|WET|WEST|WAT|WAST|VUT|VLAT|VLAST|VET|UZT|UYT|UYST|UTC|ULAT|TVT|TMT|TLT|TKT|TJT|TFT|TAHT|SST|SRT|SGT|SCT|SBT|SAST|SAMT|RET|PYT|PYST|PWT|PST|PONT|PMST|PMDT|PKT|PHT|PHOT|PGT|PETT|PETST|PET|PDT|OMST|OMSST|NZST|NZDT|NUT|NST|NPT|NOVT|NOVST|NFT|NDT|NCT|MYT|MVT|MUT|MST|MSK|MSD|MMT|MHT|MDT|MAWT|MART|MAGT|MAGST|LINT|LHST|LHDT|KUYT|KST|KRAT|KRAST|KGT|JST|IST|IRST|IRKT|IRKST|IRDT|IOT|IDT|ICT|HOVT|HKT|GYT|GST|GMT|GILT|GFT|GET|GAMT|GALT|FNT|FKT|FKST|FJT|FJST|EST|EGT|EGST|EET|EEST|EDT|ECT|EAT|EAST|EASST|DAVT|ChST|CXT|CVT|CST|COT|CLT|CLST|CKT|CHAST|CHADT|CET|CEST|CDT|CCT|CAT|CAST|BTT|BST|BRT|BRST|BOT|BNT|AZT|AZST|AZOT|AZOST|AWST|AWDT|AST|ART|AQTT|ANAT|ANAST|AMT|AMST|ALMT|AKST|AKDT|AFT|AEST|AEDT|ADT|ACST|ACDT)\\b"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:
         Token RegexMatch (GroupMatch (tz:_)):
         _) -> Token Time <$> inTimezone (Text.toUpper tz) td
        _ -> Nothing
    }
  
  ruleYear :: Rule
  ruleYear = Rule
    { name = "year"
    , pattern =
      [ Predicate $ isIntegerBetween 1000 2100
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          v <- getIntValue token
          tt $ year v
        _ -> Nothing
    }
  
  ruleHalloweenDay :: Rule
  ruleHalloweenDay = Rule
    { name = "halloween day"
    , pattern =
      [ regex "hall?owe?en"
      ]
    , prod = \_ -> tt $ monthDay 10 31
    }
  
  ruleNamedmonthDayofmonthNonOrdinal :: Rule
  ruleNamedmonthDayofmonthNonOrdinal = Rule
    { name = "<named-month> <day-of-month> (non ordinal)"
    , pattern =
      [ Predicate isAMonth
      , Predicate isDOMInteger
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:token:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  rulePeDate :: Rule
  rulePeDate = Rule
    { name = "pe <date>"
    , pattern =
      [ regex "pe"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:x:_) -> Just x
        _ -> Nothing
    }
  
  ruleDayofmonthnumberNamedmonthYear :: Rule
  ruleDayofmonthnumberNamedmonthYear = Rule
    { name = "<day-of-month>(number) <named-month> year"
    , pattern =
      [ Predicate isDOMInteger
      , Predicate isAMonth
      , regex "(\\d{2,4})"
      ]
    , prod = \tokens -> case tokens of
        (token:
         Token Time td:
         Token RegexMatch (GroupMatch (match:_)):
         _) -> do
          v <- parseInt match
          dom <- intersectDOM td token
          Token Time <$> intersect dom (year v)
        _ -> Nothing
    }
  
  ruleAbsorptionOfAfterNamedDay :: Rule
  ruleAbsorptionOfAfterNamedDay = Rule
    { name = "absorption of , after named day"
    , pattern =
      [ Predicate isADayOfWeek
      , regex ","
      ]
    , prod = \tokens -> case tokens of
        (x:_) -> Just x
        _ -> Nothing
    }
  
  ruleLastDayofweekOfTime :: Rule
  ruleLastDayofweekOfTime = Rule
    { name = "last <day-of-week> of <time>"
    , pattern =
      [ regex "ultima"
      , Predicate isADayOfWeek
      , regex "din"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          tt $ predLastOf td1 td2
        _ -> Nothing
    }
  
  ruleOrdinalTrimestru :: Rule
  ruleOrdinalTrimestru = Rule
    { name = "<ordinal> trimestru"
    , pattern =
      [ dimension Ordinal
      , Predicate $ isGrain TG.Quarter
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          n <- getIntValue token
          tt . cycleNthAfter True TG.Quarter (n - 1) $
            cycleNth TG.Year 0
        _ -> Nothing
    }
  
  ruleByTime :: Rule
  ruleByTime = Rule
    { name = "by <time>"
    , pattern =
      [ regex "p(a|â)n(a|ă) (la|(i|î)n)"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> interval TTime.Open now td
        _ -> Nothing
    }
  
  ruleDdmmyyyy :: Rule
  ruleDdmmyyyy = Rule
    { name = "dd/mm/yyyy"
    , pattern =
      [ regex "(3[01]|[12]\\d|0?[1-9])[-/](0?[1-9]|1[0-2])[/-](\\d{2,4})"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (dd:mm:yy:_)):_) -> do
          y <- parseInt yy
          m <- parseInt mm
          d <- parseInt dd
          tt $ yearMonthDay y m d
        _ -> Nothing
    }
  
  ruleMmddyyyy :: Rule
  ruleMmddyyyy = Rule
    { name = "mm/dd/yyyy"
    , pattern =
      [ regex "(0?[1-9]|1[0-2])[/-](3[01]|[12]\\d|0?[1-9])[-/](\\d{2,4})"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (mm:dd:yy:_)):_) -> do
          y <- parseInt yy
          m <- parseInt mm
          d <- parseInt dd
          tt $ yearMonthDay y m d
        _ -> Nothing
    }
  
  ruleEoyendOfYear :: Rule
  ruleEoyendOfYear = Rule
    { name = "EOY|End of year"
    , pattern =
      [ regex "sf(a|â)r(s|ș)itul anului"
      ]
    , prod = \_ -> tt $ cycleNth TG.Year 1
    }
  
  ruleMothersDay :: Rule
  ruleMothersDay = Rule
    { name = "Mother's Day"
    , pattern =
      [ regex "ziua (mamei|memeii)"
      ]
    , prod = \_ -> tt $ nthDOWOfMonth 1 7 5
    }
  
  ruleNameddayDayofmonthNumeral :: Rule
  ruleNameddayDayofmonthNumeral = Rule
    { name = "<named-day> <day-of-month> (number)"
    , pattern =
      [ Predicate isADayOfWeek
      , Predicate isDOMInteger
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:token:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleHhmmss :: Rule
  ruleHhmmss = Rule
    { name = "hh:mm:ss"
    , pattern =
      [ regex "((?:[01]?\\d)|(?:2[0-3]))[:.]([0-5]\\d)[:.]([0-5]\\d)"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (hh:mm:ss:_)):_) -> do
          h <- parseInt hh
          m <- parseInt mm
          s <- parseInt ss
          tt $ hourMinuteSecond True h m s
        _ -> Nothing
    }
  
  ruleDaysOfWeek :: [Rule]
  ruleDaysOfWeek = mkRuleDaysOfWeek
    [ ( "luni"  , "lu(n(ea|i)?)?"                        )
    , ( "marti"  , "ma(r((t|ț)(ea|i))?)?"                )
    , ( "miercuri"  , "mi(e(rcur(ea|i))?)?"              )
    , ( "joi"  , "jo(ia?)?"                              )
    , ( "vineri"  , "vi(n(er(ea|i))?)?"                  )
    , ( "sambata" , "s(a|â)mb(a|ă)t(a|ă)|s(a|â)m"        )
    , ( "duminica"  , "du(m(inic(a|ă))?)?"               )
    ]
  
  ruleMonths :: [Rule]
  ruleMonths = mkRuleMonths
    [ ( "ianuarie"  , "ian(uarie)?"     )
    , ( "februarie"  , "feb(ruarie)?"   )
    , ( "martie"  , "martie|mar"        )
    , ( "aprilie"  , "apr(ilie)?"       )
    , ( "mai"  , "mai"                  )
    , ( "iunie"  , "iun(ie)?"           )
    , ( "iulie"  , "iul(ie)?"           )
    , ( "august"  , "aug(ust)?"         )
    , ( "septembrie"  , "sept(embrie)?" )
    , ( "octombrie"  , "oct(ombrie)?"   )
    , ( "noiembrie"  , "noi(embrie)?"   )
    , ( "decembrie"  , "dec(embrie)?"   )
    ]
  
  rules :: [Rule]
  rules =
    [ ruleAboutTimeofday
    , ruleAbsorptionOfAfterNamedDay
    , ruleAcum
    , ruleAfterTimeofday
    , ruleAzi
    , ruleBetweenTimeofdayAndTimeofdayInterval
    , ruleBlackFriday
    , ruleByTheEndOfTime
    , ruleByTime
    , ruleChristmasEve
    , ruleCraciun
    , ruleCycleAcesta
    , ruleCycleAcesta2
    , ruleCycleTrecut
    , ruleCycleUrmatoare
    , ruleDayofmonthNonOrdinalNamedmonth
    , ruleDayofmonthNonOrdinalOfNamedmonth
    , ruleDayofmonthNumeral
    , ruleDayofmonthNumeralOfNamedmonth
    , ruleDayofmonthnumberNamedmonth
    , ruleDayofmonthnumberNamedmonthYear
    , ruleDdmm
    , ruleDdmmyyyy
    , ruleDimineata
    , ruleDiseara
    , ruleDupaDuration
    , ruleDupamiaza
    , ruleDurationDeAcum
    , ruleDurationInUrma
    , ruleDurationInainteDeTime
    , ruleEomendOfMonth
    , ruleEoyendOfYear
    , ruleFromTimeofdayTimeofdayInterval
    , ruleHalloweenDay
    , ruleHhmm
    , ruleHhmmss
    , ruleHourofdayIntegerAsRelativeMinutes
    , ruleHourofdayJumatate
    , ruleHourofdaySfert
    , ruleIeri
    , ruleInDuration
    , ruleInNamedmonth
    , ruleIntersect
    , ruleIntersectBy
    , ruleIntreDatetimeSiDatetimeInterval
    , ruleIntroNamedday
    , ruleLaTimeofday
    , ruleLastCycleOfTime
    , ruleLastDayofweekOfTime
    , ruleMaine
    , ruleMmdd
    , ruleMmddyyyy
    , ruleMonthDdddInterval
    , ruleMothersDay
    , ruleNameddayDayofmonthNumeral
    , ruleNameddayPeDayofmonthNumeral
    , ruleNamedmonthDayofmonthNonOrdinal
    , ruleNewYearsDay
    , ruleNewYearsEve
    , ruleNoon
    , ruleNthTimeAfterTime
    , ruleNthTimeOfTime
    , ruleOrdinalTrimestru
    , ruleOrdinalTrimestruYear
    , rulePartofdayAsta
    , rulePeDate
    , rulePeDayofmonthNonOrdinal
    , rulePentruDuration
    , ruleSearaNoapte
    , ruleSeason
    , ruleSeason2
    , ruleSeason3
    , ruleSezonAnotimp
    , ruleSinceTimeofday
    , ruleTheDayofmonthNumeral
    , ruleThisnextDayofweek
    , ruleTimeAceastaacestaasta
    , ruleTimePartofday
    , ruleTimezone
    , ruleTimeTrecuta
    , ruleTimeUrmatoarer
    , ruleTimeofdayFix
    , ruleTimeofdayLatent
    , ruleTrimestruNumeralYear
    , ruleUltimaCycle
    , ruleUltimeleNCycle
    , ruleUntilTimeofday
    , ruleUrmatoareaCycle
    , ruleUrmatoareleNCycle
    , ruleValentinesDay
    , ruleWeekend
    , ruleYear
    , ruleYearLatent
    , ruleYearLatent2
    , ruleYyyymmdd
    , ruleTheDayAfterTomorrow
    ]
    ++ ruleDaysOfWeek
    ++ ruleMonths
  