-- 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.PL.Rules
  ( rules
  ) where

import Prelude
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers (isGrain)
import Duckling.Numeral.Helpers (parseInt)
import Duckling.Ordinal.Types (OrdinalData(..))
import Duckling.Regex.Types
import Duckling.Time.Helpers
import Duckling.Time.Types (TimeData(..))
import Duckling.Time.Computed
import Duckling.Types
import qualified Duckling.Ordinal.Types as TOrdinal
import qualified Duckling.Time.Types as TTime
import qualified Duckling.TimeGrain.Types as TG

ruleOrdinalCycleTime :: Rule
ruleOrdinalCycleTime = Rule
  { name = "<ordinal> <cycle> <time>"
  , pattern =
    [ dimension Ordinal
    , dimension TimeGrain
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token Ordinal od:Token TimeGrain grain:Token Time td:_) ->
        tt $ cycleNthAfter True grain (TOrdinal.value od - 1) td
      _ -> Nothing
  }

ruleDaysOfWeek :: [Rule]
ruleDaysOfWeek = mkRuleDaysOfWeek
  [ ( "Monday" , "poniedzia(l|Å‚)(ek|ku|kowi|kiem|kowy)|pon\\.?" )
  , ( "Tuesday" , "wtorek|wtorku|wtorkowi|wtorkiem|wtr?\\.?" )
  , ( "Wednesday" , "(Åš|Å›|s)rod(a|Ä…|y|e|Ä™|zie|owy|o)|(s|Å›|Åš)ro?\\.?" )
  , ( "Thursday" , "czwartek|czwartku|czwartkowi|czwartkiem|czwr?\\.?" )
  , ( "Friday" , "piÄ…tek|piatek|piÄ…tku|piatku|piÄ…tkowi|piatkowi|piÄ…tkiem|piatkiem|pi(Ä…|a)tkowy|pia\\.?" )
  , ( "Saturday" , "sobota|soboty|sobocie|sobotÄ™|sobote|sobotÄ…|sobota|sobocie|soboto|sob\\.?" )
  , ( "Sunday" , "niedziel(a|i|Ä™|e|Ä…|o)|n(ie)?dz?\\.?" )
  ]

ruleMonths :: [Rule]
ruleMonths = mkRuleMonths
  [ ( "January" , "stycz(eÅ„|en|nia|niowi|niem|niu)|sty(cz)?\\.?" )
  , ( "February" , "lu(ty|tego|temu|t?\\.?)" )
  , ( "March" , "mar(zec|ca|cowi|cem|cu|z?\\.?)" )
  , ( "April" , "kwie(cieÅ„|cien|tnia|tniowi|tniem|tniu|t?\\.?)" )
  , ( "May" , "maj(a|owi|em|u)?" )
  , ( "June" , "czerw(iec|ca|cowi|cem|cu)|czer?\\.?" )
  , ( "July" , "lip(iec|ca|cowi|cem|cu)|lip\\.?" )
  , ( "August" , "sierp(ie(n|Å„)|ni(a|owi|em|u))|sierp\\.?|sier\\.?|sie\\.?" )
  , ( "September" , "wrze(s|Å›)(ie(Å„|n)|ni(a|owi|em|u))|wrz\\.?|wrze\\.?" )
  , ( "October" , "pa(z|Åº)dziernik(a|owi|iem|u)?|paÅº\\.?|paz\\.?" )
  , ( "November" , "listopad(a|owi|em|zie)?|lis\\.?|list\\.?" )
  , ( "December" , "grudz(ieÅ„|ien)|grudn(ia|iowi|iem|niu)|gru\\.?|grud\\.?" )
  ]

ruleRelativeMinutesTotillbeforeIntegerHourofday :: Rule
ruleRelativeMinutesTotillbeforeIntegerHourofday = Rule
  { name = "relative minutes to|till|before <integer> (hour-of-day)"
  , pattern =
    [ Predicate $ isIntegerBetween 1 59
    , regex "do|przed"
    , Predicate isAnHourOfDay
    ]
  , prod = \tokens -> case tokens of
      (token:_:Token Time td:_) -> do
        n <- getIntValue token
        Token Time <$> minutesBefore n td
      _ -> Nothing
  }

ruleRelativeMinutesAfterpastIntegerHourofday :: Rule
ruleRelativeMinutesAfterpastIntegerHourofday = Rule
  { name = "relative minutes after|past <integer> (hour-of-day)"
  , pattern =
    [ Predicate $ isIntegerBetween 1 59
    , regex "po"
    , Predicate isAnHourOfDay
    ]
  , prod = \tokens -> case tokens of
      (token:
       _:
       Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:
       _) -> do
        n <- getIntValue token
        tt $ hourMinute True hours n
      _ -> 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) _)}:
       token:
       _) -> do
        n <- getIntValue token
        tt $ hourMinute True hours n
      _ -> Nothing
  }

ruleQuarterTotillbeforeIntegerHourofday :: Rule
ruleQuarterTotillbeforeIntegerHourofday = Rule
  { name = "quarter to|till|before <integer> (hour-of-day)"
  , pattern =
    [ regex "kwadrans(ie|owi|em|a)? *(do|przed)"
    , Predicate isAnHourOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) -> Token Time <$> minutesBefore 15 td
      _ -> Nothing
  }

ruleQuarterAfterpastIntegerHourofday :: Rule
ruleQuarterAfterpastIntegerHourofday = Rule
  { name = "quarter after|past <integer> (hour-of-day)"
  , pattern =
    [ regex "kwadrans(ie|owi|em|a)? *po"
    , Predicate isAnHourOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:
       Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:
       _) -> tt $ hourMinute True hours 15
      _ -> Nothing
  }

ruleHourofdayQuarter :: Rule
ruleHourofdayQuarter = Rule
  { name = "<hour-of-day> quarter (as relative minutes)"
  , pattern =
    [ Predicate isAnHourOfDay
    , regex "kwadrans(ie|owi|em|a)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:
       _) -> tt $ hourMinute True hours 15
      _ -> Nothing
  }

ruleHalfTotillbeforeIntegerHourofday :: Rule
ruleHalfTotillbeforeIntegerHourofday = Rule
  { name = "half to|till|before <integer> (hour-of-day)"
  , pattern =
    [ regex "p(o|Ã³)(l|Å‚) *(do|przed)"
    , Predicate isAnHourOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) -> Token Time <$> minutesBefore 30 td
      _ -> Nothing
  }

ruleHalfAfterpastIntegerHourofday :: Rule
ruleHalfAfterpastIntegerHourofday = Rule
  { name = "half after|past <integer> (hour-of-day)"
  , pattern =
    [ regex "p(o|Ã³)(l|Å‚) *po"
    , Predicate isAnHourOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:
       Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:
       _) -> tt $ hourMinute True hours 30
      _ -> Nothing
  }

ruleHourofdayHalf :: Rule
ruleHourofdayHalf = Rule
  { name = "<hour-of-day> half (as relative minutes)"
  , pattern =
    [ Predicate isAnHourOfDay
    , regex "p(o|Ã³)(l|Å‚)"
    ]
  , prod = \tokens -> case tokens of
      (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:
       _) -> tt $ hourMinute True hours 30
      _ -> Nothing
  }

ruleLastTime :: Rule
ruleLastTime = Rule
  { name = "last <time>"
  , pattern =
    [ regex "ostatni(ego|ch|emu|mi|m|(a|Ä…)|ej)?|(po ?)?przedni(ego|ch|emu|e|mi|m|a)?"
    , Predicate isOkWithThisNext
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) ->
        tt $ predNth (-1) False td
      _ -> Nothing
  }

ruleDatetimeDatetimeInterval :: Rule
ruleDatetimeDatetimeInterval = Rule
  { name = "<datetime> - <datetime> (interval)"
  , pattern =
    [ Predicate isNotLatent
    , regex "\\-|(p|d)o|a(Å¼|z) (p|d)o"
    , Predicate isNotLatent
    ]
  , prod = \tokens -> case tokens of
      (Token Time td1:_:Token Time td2:_) ->
        Token Time <$> interval TTime.Closed td1 td2
      _ -> Nothing
  }

ruleCycleAfterTime :: Rule
ruleCycleAfterTime = Rule
  { name = "<cycle> after <time>"
  , pattern =
    [ dimension TimeGrain
    , regex "po"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token TimeGrain grain:_:Token Time td:_) ->
        tt $ cycleNthAfter False grain 1 td
      _ -> Nothing
  }

ruleInDuration :: Rule
ruleInDuration = Rule
  { name = "in <duration>"
  , pattern =
    [ regex "(w( ?(prze)?ciÄ…gu)?|za) ?(jeszcze)?|przez"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:Token Duration dd:_) ->
        tt $ inDuration dd
      _ -> Nothing
  }

ruleNow :: Rule
ruleNow = Rule
  { name = "now"
  , pattern =
    [ regex "(w)? ?(tym|tej)? ?(teraz|momencie|chwili|momeÅ„cie)"
    ]
  , prod = \_ -> tt now
  }

ruleLastCycleOfTime :: Rule
ruleLastCycleOfTime = Rule
  { name = "last <cycle> of <time>"
  , pattern =
    [ regex "ostatni(ego|ch|emu|mi|m|(a|Ä…)|ej)?"
    , dimension TimeGrain
    , regex "w(e)?|z(e)?"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_:Token Time td:_) ->
        tt $ cycleLastOf grain td
      _ -> Nothing
  }

ruleFromDatetimeDatetimeInterval :: Rule
ruleFromDatetimeDatetimeInterval = Rule
  { name = "from <datetime> - <datetime> (interval)"
  , pattern =
    [ regex "od|p(o|Ã³)(z|Åº)niej ni(z|Å¼)"
    , dimension Time
    , regex "\\-|do|po|aÅ¼ do|az do|aÅ¼ po|az po|ale przed"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td1:_:Token Time td2:_) ->
        Token Time <$> interval TTime.Closed td1 td2
      _ -> Nothing
  }

ruleOrdinalAsHour :: Rule
ruleOrdinalAsHour = Rule
  { name = "<ordinal> (as hour)"
  , pattern =
    [ Predicate $ isOrdinalBetween 1 24
    ]
  , prod = \tokens -> case tokens of
      (Token Ordinal OrdinalData{TOrdinal.value = v}:_) ->
        tt . mkLatent $ hour True v
      _ -> Nothing
  }

ruleMonthDdddInterval :: Rule
ruleMonthDdddInterval = Rule
  { name = "<month> dd-dd (interval)"
  , pattern =
    [ Predicate isAMonth
    , regex "(3[01]|[12]\\d|0?[1-9])"
    , regex "\\-|do|po|aÅ¼ do|az do|aÅ¼ po|az po"
    , regex "(3[01]|[12]\\d|0?[1-9])"
    ]
  , prod = \tokens -> case tokens of
      ( Token Time td
       :Token RegexMatch (GroupMatch (d1:_))
       :_
       :Token RegexMatch (GroupMatch (d2:_))
       :_) -> do
        dd1 <- parseInt d1
        dd2 <- parseInt d2
        dom1 <- intersect (dayOfMonth dd1) td
        dom2 <- intersect (dayOfMonth dd2) td
        Token Time <$> interval TTime.Closed dom1 dom2
      _ -> Nothing
  }

ruleYearLatent2 :: Rule
ruleYearLatent2 = Rule
  { name = "year (latent)"
  , pattern =
    [ Predicate $ isIntegerBetween 2101 10000
    ]
  , prod = \tokens -> case tokens of
      (token:_) -> do
        n <- getIntValue token
        tt . mkLatent $ year n
      _ -> Nothing
  }

ruleTimeAfterNext :: Rule
ruleTimeAfterNext = Rule
  { name = "<time> after next"
  , pattern =
    [ dimension Time
    , regex "po kolejnym|po nast(e|Ä™)pn(ym|y|ego|emu|(a|Ä…)|ej|e)|po przysz(l|Å‚)(ym|y|ego|emu|(a|Ä…)|ej)"
    ]
  , prod = \tokens -> case tokens of
      (Token Time td:_) ->
        tt $ predNth 1 True td
      _ -> Nothing
  }

ruleTheIdesOfNamedmonth :: Rule
ruleTheIdesOfNamedmonth = Rule
  { name = "the ides of <named-month>"
  , pattern =
    [ regex "the ides? of"
    , Predicate isAMonth
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td@TimeData {TTime.form = Just (TTime.Month m)}:_) ->
        Token Time <$>
          intersect (dayOfMonth $ if elem m [3, 5, 7, 10] then 15 else 13) td
      _ -> Nothing
  }

ruleNoon :: Rule
ruleNoon = Rule
  { name = "noon"
  , pattern =
    [ regex "po(l|Å‚)udni(em|e|a|u)"
    ]
  , prod = \_ -> tt $ hour False 12
  }

ruleToday :: Rule
ruleToday = Rule
  { name = "today"
  , pattern =
    [ regex "dzisiejsz[yaÄ…]|dzisiaj|dzi[Å›s]|w ten dzieÅ„|w ten dzien|w obecny dzieÅ„|w obecny dzien|obecnego dnia"
    ]
  , prod = \_ -> tt today
  }

ruleThisnextDayofweek :: Rule
ruleThisnextDayofweek = Rule
  { name = "this|next <day-of-week>"
  , pattern =
    [ regex "kolejn(ym|y|ego|emu|e)|nast(e|Ä™)pn(ym|y|ego|emu|e|(a|Ä…)|ej|e)"
    , Predicate isADayOfWeek
    ]
  , 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 "(po|po )?miedzy|miÄ™dzy"
    , Predicate isATimeOfDay
    , regex "a|i"
    , Predicate isATimeOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td1:_:Token Time td2:_) ->
        Token Time <$> interval TTime.Closed td1 td2
      _ -> Nothing
  }

ruleNextCycle :: Rule
ruleNextCycle = Rule
  { name = "next <cycle>"
  , pattern =
    [ regex "kolejn(ym|y|ego|emu|(a|Ä…)|ej|e)|nast(e|Ä™)pn(ym|y|ego|emu|(a|Ä…)|ej|e)|przysz(l|Å‚)(ego|emu|ym|(a|Ä…)|ej|ych|i|ymi|y|e)|za"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) ->
        tt $ cycleNth grain 1
      _ -> Nothing
  }

ruleTheCycleOfTime :: Rule
ruleTheCycleOfTime = Rule
  { name = "the <cycle> of <time>"
  , pattern =
    [ regex "the"
    , dimension TimeGrain
    , regex "of"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_:Token Time td:_) ->
        tt $ cycleNthAfter True grain 0 td
      _ -> Nothing
  }

ruleTimeofdayApproximately :: Rule
ruleTimeofdayApproximately = Rule
  { name = "<time-of-day> approximately"
  , pattern =
    [ Predicate isATimeOfDay
    , regex "o?ko(l|Å‚)o|mniej wi(e|Ä™)cej"
    ]
  , prod = \tokens -> case tokens of
      (Token Time td:_) -> tt $ notLatent td
      _ -> Nothing
  }

ruleOnDate :: Rule
ruleOnDate = Rule
  { name = "on <date>"
  , pattern =
    [ regex "we?"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:x:_) -> Just x
      _ -> Nothing
  }

ruleLastDayofweekTime :: Rule
ruleLastDayofweekTime = Rule
  { name = "last <day-of-week> <time>"
  , pattern =
    [ regex "ostatni(ego|ch|emu|mi|m|(a|Ä…)|ej)?"
    , Predicate isADayOfWeek
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td1:Token Time td2:_) ->
        tt $ predLastOf td1 td2
      _ -> Nothing
  }

ruleDurationFromNow :: Rule
ruleDurationFromNow = Rule
  { name = "<duration> from now"
  , pattern =
    [ dimension Duration
    , regex "od (dzi(s|Å›)|teraz)"
    ]
  , prod = \tokens -> case tokens of
      (Token Duration dd:_) ->
        tt $ inDuration dd
      _ -> Nothing
  }

ruleLunch :: Rule
ruleLunch = Rule
  { name = "lunch"
  , pattern =
    [ regex "(na )?la?u?nc(z|h)|obiad"
    ]
  , prod = \_ ->
      let from = hour False 12
          to = hour False 14
      in Token Time . mkLatent . partOfDay <$>
           interval TTime.Open from to
  }

ruleAfternoon :: Rule
ruleAfternoon = Rule
  { name = "afternoon"
  , pattern =
    [ regex "po(l|Å‚)udni(em|e|a|u)"
    ]
  , prod = \_ ->
      let from = hour False 12
          to = hour False 19
      in Token Time . mkLatent . partOfDay <$>
           interval TTime.Open from to
  }

ruleEveningnight :: Rule
ruleEveningnight = Rule
  { name = "evening|night"
  , pattern =
    [ regex "wiecz[oÃ³]r(em|owi|ze|a|u)?|nocÄ…?"
    ]
  , prod = \_ ->
      let from = hour False 18
          to = hour False 0
      in Token Time . mkLatent . partOfDay <$>
           interval TTime.Open from to
  }

ruleLastCycle :: Rule
ruleLastCycle = Rule
  { name = "last <cycle>"
  , pattern =
    [ regex "ostatni(ego|ch|emu|mi|m|(a|Ä…)|ej|e)?|(po ?)?przedni(ego|ch|emu|mi|m|e|(a|Ä…)|ej)?"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) ->
        tt . cycleNth grain $ - 1
      _ -> Nothing
  }

ruleHourofdayHourofdayInterval :: Rule
ruleHourofdayHourofdayInterval = Rule
  { name = "<hour-of-day> - <hour-of-day> (interval)"
  , pattern =
    [ Predicate isATimeOfDay
    , regex "-|do|aÅ¼ po|po"
    , Predicate $ and . sequence [isNotLatent, isATimeOfDay]
    ]
  , prod = \tokens -> case tokens of
      (Token Time td1:_:Token Time td2:_) ->
        Token Time <$> interval TTime.Closed td1 td2
      _ -> Nothing
  }

ruleTimeBeforeLast :: Rule
ruleTimeBeforeLast = Rule
  { name = "<time> before last"
  , pattern =
    [ dimension Time
    , regex "przed ?ostatni(ego|ch|emu|mi|m|(a|Ä…)|ej)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Time td:_) ->
        tt $ predNth (-2) False td
      _ -> Nothing
  }

ruleNamedmonthDayofmonthOrdinal :: Rule
ruleNamedmonthDayofmonthOrdinal = Rule
  { name = "<named-month> <day-of-month> (ordinal)"
  , pattern =
    [ Predicate isAMonth
    , Predicate isDOMOrdinal
    ]
  , prod = \tokens -> case tokens of
      (Token Time td:token:_) -> Token Time <$> intersectDOM td token
      _ -> Nothing
  }

ruleInduringThePartofday :: Rule
ruleInduringThePartofday = Rule
  { name = "in|during the <part-of-day>"
  , pattern =
    [ regex "(w|na) ?(czas(ie)?)"
    , Predicate isAPartOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) ->
        tt $ notLatent td
      _ -> Nothing
  }

ruleDayofmonthordinalNamedmonth :: Rule
ruleDayofmonthordinalNamedmonth = Rule
  { name = "<day-of-month>(ordinal) <named-month>"
  , pattern =
    [ Predicate isDOMOrdinal
    , Predicate isAMonth
    ]
  , prod = \tokens -> case tokens of
      (token:Token Time td:_) -> Token Time <$> intersectDOM td token
      _ -> Nothing
  }

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
  }

ruleNthTimeAfterTime :: Rule
ruleNthTimeAfterTime = Rule
  { name = "nth <time> after <time>"
  , pattern =
    [ dimension Ordinal
    , dimension Time
    , regex "po"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token Ordinal od:Token Time td1:_:Token Time td2:_) ->
        tt $ predNthAfter (TOrdinal.value od - 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
  }

ruleAfterDuration :: Rule
ruleAfterDuration = Rule
  { name = "after <duration>"
  , pattern =
    [ regex "po"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:Token Duration dd:_) ->
        tt . notLatent . withDirection TTime.After $ inDuration dd
      _ -> Nothing
  }

ruleDayofmonthOrdinalOfNamedmonth :: Rule
ruleDayofmonthOrdinalOfNamedmonth = Rule
  { name = "<day-of-month> (ordinal) of <named-month>"
  , pattern =
    [ Predicate isDOMOrdinal
    , regex "of|in"
    , Predicate isAMonth
    ]
  , prod = \tokens -> case tokens of
      (token:_:Token Time td:_) -> Token Time <$> intersectDOM td token
      _ -> Nothing
  }

ruleFromTimeofdayTimeofdayInterval :: Rule
ruleFromTimeofdayTimeofdayInterval = Rule
  { name = "from <time-of-day> - <time-of-day> (interval)"
  , pattern =
    [ regex "(niÅ¼|niz|od)"
    , Predicate isATimeOfDay
    , regex "((but )?before)|\\-|do|po|aÅ¼ do|az do|aÅ¼ po|az po"
    , Predicate isATimeOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td1:_:Token Time td2:_) ->
        Token Time <$> interval TTime.Closed td1 td2
      _ -> Nothing
  }

ruleExactlyTimeofday :: Rule
ruleExactlyTimeofday = Rule
  { name = "exactly <time-of-day>"
  , pattern =
    [ regex "(r(o|Ã³)wno|dok(l|Å‚)adnie)( o)?"
    , Predicate isATimeOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) -> tt $ notLatent td
      _ -> Nothing
  }

ruleIntegerLatentTimeofday :: Rule
ruleIntegerLatentTimeofday = Rule
  { name = "<integer> (latent time-of-day)"
  , pattern =
    [ Predicate $ isIntegerBetween 0 23
    ]
  , prod = \tokens -> case tokens of
      (token:_) -> do
        v <- getIntValue token
        tt . mkLatent $ hour True v
      _ -> Nothing
  }

ruleBetweenDatetimeAndDatetimeInterval :: Rule
ruleBetweenDatetimeAndDatetimeInterval = Rule
  { name = "between <datetime> and <datetime> (interval)"
  , pattern =
    [ regex "(po|po )?mi(e|Ä™)dzy"
    , dimension Time
    , regex "a|i"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td1:_:Token Time td2:_) ->
        Token Time <$> interval TTime.Closed td1 td2
      _ -> Nothing
  }

ruleDurationAgo :: Rule
ruleDurationAgo = Rule
  { name = "<duration> ago"
  , pattern =
    [ dimension Duration
    , regex "temu"
    ]
  , prod = \tokens -> case tokens of
      (Token Duration dd:_) ->
        tt $ durationAgo dd
      _ -> Nothing
  }

ruleByTheEndOfTime :: Rule
ruleByTheEndOfTime = Rule
  { name = "by the end of <time>"
  , pattern =
    [ regex "do (ko[Å„n]ca )?(tego)?"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) -> Token Time <$> interval TTime.Closed now td
      _ -> Nothing
  }

ruleDayBeforeYesterdaySingleWord :: Rule
ruleDayBeforeYesterdaySingleWord = Rule
  { name = "day-before-yesterday (single-word)"
  , pattern =
    [ regex "przedwczoraj"
    ]
  , prod = \_ -> tt $ cycleNth TG.Day (-2)
  }

ruleLastNCycle :: Rule
ruleLastNCycle = Rule
  { name = "last n <cycle>"
  , pattern =
    [ regex "ostatni(ego|ch|emu|mi|m|(a|Ä…)|ej|e)?|(po ?)?przedni(ego|ch|emu|mi|m|e|(a|Ä…)|ej)?"
    , 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
  }

ruleTimeofdaySharp :: Rule
ruleTimeofdaySharp = Rule
  { name = "<time-of-day> sharp"
  , pattern =
    [ Predicate isATimeOfDay
    , regex "r(o|Ã³)wno|dok(l|Å‚)adnie"
    ]
  , prod = \tokens -> case tokens of
      (Token Time td:_) -> tt $ notLatent td
      _ -> Nothing
  }

ruleWithinDuration :: Rule
ruleWithinDuration = Rule
  { name = "within <duration>"
  , pattern =
    [ regex "(w )?ci(a|Ä…)gu|zakresie|obrÄ™bie|obrebie"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:Token Duration dd:_) ->
        Token Time <$> interval TTime.Open now (inDuration dd)
      _ -> Nothing
  }

ruleTimeofdayRano :: Rule
ruleTimeofdayRano = Rule
  { name = "<time-of-day> rano"
  , pattern =
    [ Predicate isATimeOfDay
    , regex "(z )?ran(o|a|u|em)"
    ]
  , prod = \tokens -> case tokens of
      (Token Time td:_) -> tt $ timeOfDayAMPM True td
      _ -> 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 "o?ko(l|Å‚)o|mniej wi(e|Ä™)cej|tak o"
    , 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 "(a(Å¼|z) )?do|przed"
    , Predicate isATimeOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) ->
        tt . notLatent $ withDirection TTime.Before td
      _ -> Nothing
  }

ruleAtTimeofday :: Rule
ruleAtTimeofday = Rule
  { name = "at <time-of-day>"
  , pattern =
    [ regex "o|na|@"
    , Predicate isATimeOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) -> tt $ notLatent td
      _ -> Nothing
  }

ruleNthTimeOfTime :: Rule
ruleNthTimeOfTime = Rule
  { name = "nth <time> of <time>"
  , pattern =
    [ dimension Ordinal
    , dimension Time
    , regex "w(e)?|z(e)?"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token Ordinal od:Token Time td1:_:Token Time td2:_) -> Token Time .
        predNth (TOrdinal.value od - 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 "((wek|week|wik)(\\s|-)?end|wkend)"
    ]
  , prod = \_ -> tt $ mkOkForThisNext weekend
  }

ruleEomendOfMonth :: Rule
ruleEomendOfMonth = Rule
  { name = "EOM|End of month"
  , pattern =
    [ regex "(na |w )?(koniec|ko(n|Å„)ca|ko(n|Å„)cu|ko(n|Å„)cowi|ko(n|Å„)cem|ko(n|Å„)c(o|Ã³)wke) (miesi(a|Ä…)ca|msc)"
    ]
  , prod = \_ -> tt $ cycleNth TG.Month 1
  }

ruleNextTime :: Rule
ruleNextTime = Rule
  { name = "next <time>"
  , pattern =
    [ regex "kolejn(ym|y|ego|emu|(a|Ä…)|ej|e)|nast(e|Ä™)pn(ym|y|ego|emu|e|(a|Ä…)|ej|e)|przysz(l|Å‚)(ego|emu|ym|(a|Ä…)|ej|ych|i|ymi|y|e)"
    , Predicate $ and . sequence [isNotLatent, isOkWithThisNext]
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) ->
        tt $ predNth 0 True td
      _ -> Nothing
  }

ruleDayaftertomorrowSingleword :: Rule
ruleDayaftertomorrowSingleword = Rule
  { name = "day-after-tomorrow (single-word)"
  , pattern =
    [ regex "(po ?jutr(o|ze))"
    ]
  , prod = \_ -> tt $ cycleNth TG.Day 2
  }

ruleOrdinalQuarterYear :: Rule
ruleOrdinalQuarterYear = Rule
  { name = "<ordinal> quarter <year>"
  , pattern =
    [ dimension Ordinal
    , Predicate $ isGrain TG.Quarter
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token Ordinal od:_:Token Time td:_) ->
        tt $ cycleNthAfter False TG.Quarter (TOrdinal.value od - 1) td
      _ -> 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
  }

ruleTheOrdinalCycleAfterTime :: Rule
ruleTheOrdinalCycleAfterTime = Rule
  { name = "the <ordinal> <cycle> after <time>"
  , pattern =
    [ regex "the"
    , dimension Ordinal
    , dimension TimeGrain
    , regex "after"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token Ordinal od:Token TimeGrain grain:_:Token Time td:_) ->
        tt $ cycleNthAfter True grain (TOrdinal.value od - 1) td
      _ -> Nothing
  }

ruleIntersectByOfFromS :: Rule
ruleIntersectByOfFromS = Rule
  { name = "intersect by \"of\", \"from\", \"'s\""
  , pattern =
    [ Predicate isNotLatent
    , regex "z"
    , Predicate isNotLatent
    ]
  , prod = \tokens -> case tokens of
      (Token Time td1:_:Token Time td2:_) ->
        Token Time <$> intersect td1 td2
      _ -> Nothing
  }

ruleNextNCycle :: Rule
ruleNextNCycle = Rule
  { name = "next n <cycle>"
  , pattern =
    [ regex "kolejn(ym|y|ego|emu|(a|Ä…)|ej|e)|nast(e|Ä™)pn(ym|y|ego|emu|(a|Ä…)|ej|e)"
    , 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
  }

ruleMorning :: Rule
ruleMorning = Rule
  { name = "morning"
  , pattern =
    [ regex "rano|poran(ek|ku|ka)|z rana|(s|Å›)witem"
    ]
  , prod = \_ ->
      let from = hour False 4
          to = hour False 12
      in Token Time . mkLatent . partOfDay <$>
           interval TTime.Open from to
  }

ruleThisPartofday :: Rule
ruleThisPartofday = Rule
  { name = "this <part-of-day>"
  , pattern =
    [ regex "ten|tego|ta|to"
    , Predicate isAPartOfDay
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) -> Token Time . partOfDay <$> intersect today td
      _ -> Nothing
  }

ruleThisCycle :: Rule
ruleThisCycle = Rule
  { name = "this <cycle>"
  , pattern =
    [ regex "te(mu|n|go|j)|tym|t(a|Ä…)|nadchodz(a|Ä…)c(ym|y|ego|emu|(a|Ä…)|ej)|obecn(ym|y|emu|ego|nym|(a|Ä…)|ej)"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) ->
        tt $ cycleNth grain 0
      _ -> Nothing
  }

ruleThisTime :: Rule
ruleThisTime = Rule
  { name = "this <time>"
  , pattern =
    [ regex "te(mu|n|go|j)|ta|to|tym|nadchodz(a|Ä…)c(ym|y|ego|emu|(a|Ä…)|ej)"
    , Predicate isOkWithThisNext
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) ->
        tt $ predNth 0 False td
      _ -> Nothing
  }

ruleDurationHence :: Rule
ruleDurationHence = Rule
  { name = "<duration> hence"
  , pattern =
    [ dimension Duration
    , regex "p(o|Ã³)(z|Åº)niej|potem"
    ]
  , prod = \tokens -> case tokens of
      (Token Duration dd:_) ->
        tt $ inDuration dd
      _ -> Nothing
  }

ruleDayofmonthNonOrdinalOfNamedmonth :: Rule
ruleDayofmonthNonOrdinalOfNamedmonth = Rule
  { name = "<day-of-month> (non ordinal) of <named-month>"
  , pattern =
    [ Predicate isDOMInteger
    , regex "of|in"
    , Predicate isAMonth
    ]
  , prod = \tokens -> case tokens of
      (token:_:Token Time td:_) -> Token Time <$> intersectDOM td token
      _ -> Nothing
  }

ruleNthTimeTime :: Rule
ruleNthTimeTime = Rule
  { name = "nth <time> <time>"
  , pattern =
    [ dimension Ordinal
    , dimension Time
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token Ordinal od:Token Time td1:Token Time td2:_) -> Token Time .
        predNth (TOrdinal.value od - 1) False <$> intersect td2 td1
      _ -> Nothing
  }

ruleTimeofdayPopoudniuwieczoremwNocy :: Rule
ruleTimeofdayPopoudniuwieczoremwNocy = Rule
  { name = "<time-of-day> popoÅ‚udniu/wieczorem/w nocy"
  , pattern =
    [ Predicate isATimeOfDay
    , regex "(w )?noc(y|(a|Ä…))|(po ?)?po(l|Å‚)udni(em|e|a|u)|wiecz(o|Ã³)r(i|u|a|owi|em|rze)"
    ]
  , prod = \tokens -> case tokens of
      (Token Time td:_) -> tt $ timeOfDayAMPM False td
      _ -> Nothing
  }

ruleOnANamedday :: Rule
ruleOnANamedday = Rule
  { name = "on a named-day"
  , pattern =
    [ regex "we?"
    , Predicate isADayOfWeek
    ]
  , prod = \tokens -> case tokens of
      (_:x:_) -> Just x
      _ -> Nothing
  }

ruleYearLatent :: Rule
ruleYearLatent = Rule
  { name = "year (latent)"
  , pattern =
    [ Predicate $ isIntegerBetween 1 999
    ]
  , prod = \tokens -> case tokens of
      (token:_) -> do
         n <- getIntValue token
         tt . mkLatent $ year n
      _ -> Nothing
  }

ruleYesterday :: Rule
ruleYesterday = Rule
  { name = "yesterday"
  , pattern =
    [ regex "wczoraj(szym|szy)?|wczrj|wczor"
    ]
  , prod = \_ -> tt . cycleNth TG.Day $ - 1
  }

ruleAfterTimeofday :: Rule
ruleAfterTimeofday = Rule
  { name = "after <time-of-day>"
  , pattern =
    [ regex "po"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) ->
        tt $ withDirection TTime.After td
      _ -> Nothing
  }

ruleDayofmonthOrdinal :: Rule
ruleDayofmonthOrdinal = Rule
  { name = "<day-of-month> (ordinal)"
  , pattern =
    [ Predicate isDOMOrdinal
    ]
  , prod = \tokens -> case tokens of
      (Token Ordinal OrdinalData{TOrdinal.value = v}:_) ->
        tt . mkLatent $ dayOfMonth v
      _ -> Nothing
  }

ruleOrdinalCycleAfterTime :: Rule
ruleOrdinalCycleAfterTime = Rule
  { name = "<ordinal> <cycle> after <time>"
  , pattern =
    [ dimension Ordinal
    , dimension TimeGrain
    , regex "after"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token Ordinal od:Token TimeGrain grain:_:Token Time td:_) ->
        tt $ cycleNthAfter True grain (TOrdinal.value od - 1) td
      _ -> Nothing
  }

ruleOrdinalCycleOfTime :: Rule
ruleOrdinalCycleOfTime = Rule
  { name = "<ordinal> <cycle> of <time>"
  , pattern =
    [ dimension Ordinal
    , dimension TimeGrain
    , regex "w(e)?|z(e)?"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token Ordinal od:Token TimeGrain grain:_:Token Time td:_) ->
        tt $ cycleNthAfter True grain (TOrdinal.value od - 1) 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
  }

ruleFromHourofdayHourofdayInterval :: Rule
ruleFromHourofdayHourofdayInterval = Rule
  { name = "from <hour-of-day> - <hour-of-day> (interval)"
  , pattern =
    [ regex "od"
    , Predicate isATimeOfDay
    , regex "-|to|th?ru|through|until"
    , Predicate $ and . sequence [isNotLatent, isATimeOfDay]
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td1:_:Token Time td2:_) ->
        Token Time <$> interval TTime.Closed td1 td2
      _ -> Nothing
  }

ruleYear :: Rule
ruleYear = Rule
  { name = "year"
  , pattern =
    [ Predicate $ isIntegerBetween 1000 2100
    ]
  , prod = \tokens -> case tokens of
      (token:_) -> do
        n <- getIntValue token
        tt $ year n
      _ -> Nothing
  }

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
  }

ruleHhmmMilitary :: Rule
ruleHhmmMilitary = Rule
  { name = "hhmm (military)"
  , 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 . mkLatent $ hourMinute False h m
      _ -> 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 "ostatni(ego|ch|emu|mi|m|(a|Ä…)|ej)?"
    , Predicate isADayOfWeek
    , regex "w(e)?|z(e)?"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td1:_:Token Time td2:_) ->
        tt $ predLastOf td1 td2
      _ -> Nothing
  }

ruleLastCycleTime :: Rule
ruleLastCycleTime = Rule
  { name = "last <cycle> <time>"
  , pattern =
    [ regex "ostatni(ego|ch|emu|mi|m|(a|Ä…)|ej)?"
    , dimension TimeGrain
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:Token Time td:_) ->
        tt $ cycleLastOf grain td
      _ -> Nothing
  }

ruleByTime :: Rule
ruleByTime = Rule
  { name = "by <time>"
  , pattern =
    [ regex "(a(z|Å¼) )?do"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (_:Token Time td:_) -> Token Time <$> interval TTime.Open now td
      _ -> Nothing
  }

ruleHhmmMilitaryAmpm :: Rule
ruleHhmmMilitaryAmpm = Rule
  { name = "hhmm (military) am|pm"
  , pattern =
    [ regex "((?:1[012]|0?\\d))([0-5]\\d)"
    , regex "([ap])\\.?m?\\.?"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (hh:mm:_)):
       Token RegexMatch (GroupMatch (ap:_)):
       _) -> do
        h <- parseInt hh
        m <- parseInt mm
        tt . timeOfDayAMPM (Text.toLower ap == "a") $ hourMinute True h m
      _ -> Nothing
  }

ruleCycleBeforeTime :: Rule
ruleCycleBeforeTime = Rule
  { name = "<cycle> before <time>"
  , pattern =
    [ dimension TimeGrain
    , regex "przed"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token TimeGrain grain:_:Token Time td:_) ->
        tt $ cycleNthAfter False grain (-1) td
      _ -> Nothing
  }

ruleTimeofdayTimeofdayInterval :: Rule
ruleTimeofdayTimeofdayInterval = Rule
  { name = "<time-of-day> - <time-of-day> (interval)"
  , pattern =
    [ Predicate $ and . sequence [isNotLatent, isATimeOfDay]
    , regex "\\-|:|do|po|aÅ¼ do|az do|aÅ¼ po|az po"
    , Predicate isATimeOfDay
    ]
  , prod = \tokens -> case tokens of
      (Token Time td1:_:Token Time td2:_) ->
        Token Time <$> interval TTime.Closed td1 td2
      _ -> Nothing
  }

ruleDurationAfterTime :: Rule
ruleDurationAfterTime = Rule
  { name = "<duration> after <time>"
  , pattern =
    [ dimension Duration
    , regex "po"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token Duration dd:_:Token Time td:_) ->
        tt $ durationAfter dd td
      _ -> Nothing
  }

ruleOrdinalQuarter :: Rule
ruleOrdinalQuarter = Rule
  { name = "<ordinal> quarter"
  , 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
  }

ruleDurationBeforeTime :: Rule
ruleDurationBeforeTime = Rule
  { name = "<duration> before <time>"
  , pattern =
    [ dimension Duration
    , regex "do|przed"
    , dimension Time
    ]
  , prod = \tokens -> case tokens of
      (Token Duration dd:_:Token Time td:_) ->
        tt $ durationBefore dd td
      _ -> 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 "(na |w )?(koniec|ko(n|Å„)ca|ko(n|Å„)cu|ko(n|Å„)cowi|ko(n|Å„)cem|ko(n|Å„)c(o|Ã³)wke) (rok(u|owi|iem))"
    ]
  , prod = \_ -> tt $ cycleNth TG.Year 1
  }

ruleTomorrow :: Rule
ruleTomorrow = Rule
  { name = "tomorrow"
  , pattern =
    [ regex "jutr(o|a|u|em|ze(jszy|jsza)?)|jtr|jutr"
    ]
  , prod = \_ -> tt $ cycleNth TG.Day 1
  }

ruleTimeofdayOclock :: Rule
ruleTimeofdayOclock = Rule
  { name = "<time-of-day> o'clock"
  , pattern =
    [ Predicate isATimeOfDay
    , regex "godzina"
    ]
  , prod = \tokens -> case tokens of
      (Token Time td:_) ->
        tt $ notLatent td
      _ -> Nothing
  }

ruleDayofmonthordinalNamedmonthYear :: Rule
ruleDayofmonthordinalNamedmonthYear = Rule
  { name = "<day-of-month>(ordinal) <named-month> year"
  , pattern =
    [ Predicate isDOMOrdinal
    , Predicate isAMonth
    , regex "(\\d{2,4})"
    ]
  , prod = \tokens -> case tokens of
      (token:
       Token Time td:
       Token RegexMatch (GroupMatch (match:_)):
       _) -> do
         y <- parseInt match
         dom <- intersectDOM td token
         Token Time <$> intersect dom (year y)
      _ -> 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
  }

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
  }

ruleHolidays :: [Rule]
ruleHolidays =
  mkRuleHolidays
    [ ( "New Year's Day", "nowy rok", monthDay 1 1)
    , ( "Three Kings' Day", "((Å›|s)wi(e|Ä™)to)? ?trzech kr(o|Ã³)li", monthDay 1 6)
    , ( "Valentine's Day", "walentynki", monthDay 2 14)
    , ( "Women's Day", "dzie(n|Å„) kobiet", monthDay 3 8)
    , ( "Labour Day", "(s|Å›)wi(e|Ä™)to pracy", monthDay 5 1)
    , ( "Constitution Day"
      , "(Å›|s)wi(e|Ä™)to konstytucj(i|a) ?((trzeciego|3) maja)?"
      , monthDay 5 3
      )
    , ( "Mother's Day", "dzie(n|Å„) ma(my|tki|m)", monthDay 5 26)
    , ( "Father's Day", "dzie(n|Å„) (taty|ojca)", monthDay 6 23)
    , ( "Assumption Day"
      , "(Å›|s)wi(e|Ä™)to wojska polskiego|wniebowzi(e|Ä™)cie naj(s|Å›)wi(e|Ä™)tszej maryi panny"
      , monthDay 8 15
      )
    , ( "Halloween Day", "hall?owe?en( day)?", monthDay 10 31)
    , ( "All Saints' Day"
      , "((Å›|s)wi(e|Ä™)to)? ?wszystkich (s|Å›)wi(e|Ä™)tych"
      , monthDay 11 1
      )
    , ( "Polish Independence Day"
      , "(s|Å›)wi(e|Ä™)t(a|o) niepodleg(l|Å‚)o(s|Å›)ci|(Å›|s)w\\.? niepodleg(l|Å‚)o(s|Å›)ci"
      , monthDay 11 11
      )
    , ( "Saint Nicholas Day", "mikoÅ‚ajki", monthDay 12 6)
    , ( "Christmas Eve"
      , "(wigilia|wigilii|wigili(e|Ä™)|wigili(a|Ä…)|wigilio) ?(bo(z|Å¼)ego narodzenia)?"
      , monthDay 12 24
      )
    , ( "Christmas"
      , "((Åš|Å›|s)wi(e|Ä™)ta)? ?bo(z|Å¼)(ym|ego|e) narodzeni(e|a|u)"
      , monthDay 12 25
      )
    , ( "St Stephen's Day"
      , "drugi dzie(n|Å„) (s|Å›)wi(a|Ä…)t bo(z|Å¼)ego narodzenia"
      , monthDay 12 26
      )
    , ( "New Year's Eve", "sylwester", monthDay 12 31)
    , ( "Thanksgiving Day"
      , "((s|Å›)wiet(a|o)|(dzie(n|Å„)))? ?dzi(e|Ä™)kczynieni(e|a)"
      , nthDOWOfMonth 4 4 11
      )
    ]

ruleComputedHolidays :: [Rule]
ruleComputedHolidays =
  mkRuleHolidays
    [ ( "Ash Wednesday"
      , "popielec"
      , cycleNthAfter False TG.Day (-46) easterSunday
      )
    , ( "Palm Sunday"
      , "niedziela palmowa"
      , cycleNthAfter False TG.Day (-7) easterSunday
      )
    , ( "Good Friday"
      , "wielki pi(a|Ä…)tek"
      , cycleNthAfter False TG.Day (-2) easterSunday
      )
    , ( "Holy Saturday"
      , "wielka sobota"
      , cycleNthAfter False TG.Day (-1) easterSunday
      )
    , ( "Easter Sunday", "wielkanoc|niedziela wielkanocna", easterSunday)
    , ( "Easter Monday"
      , "poniedzia(l|Å‚)ek wielkanocny"
      , cycleNthAfter False TG.Day 1 easterSunday
      )
    , ( "Pentecost"
      , "zielone (s|Å›)wi(a|Ä…)tki|zes(l|Å‚)anie ducha (Å›|s)wi(e|Ä™)tego"
      , cycleNthAfter False TG.Day 49 easterSunday
      )
    , ( "Corpus Christi"
      , "bo(z|Å¼)e cia(l|Å‚)o"
      , cycleNthAfter False TG.Day 60 easterSunday
      )
    ]

ruleSeasons :: [Rule]
ruleSeasons =
  mkRuleSeasons
    [ ("lato", "lato|lata|latu|latem|lecie", monthDay 6 21, monthDay 9 23)
    , ( "jesieÅ„"
      , "jesieÅ„|jesien|jesieni|jesieniÄ…|jesienia"
      , monthDay 9 23
      , monthDay 12 21
      )
    , ( "zima"
      , "zima|zimy|zimie|zimÄ™|zime|zimÄ…|zima|zimie|zimo"
      , monthDay 12 21
      , monthDay 3 20
      )
    , ( "wiosna"
      , "wiosna|wiosny|wioÅ›nie|wiosnie|wiosnÄ™|wiosne|wiosnÄ…|wiosna|wioÅ›nie|wiosnie|wiosno"
      , monthDay 3 20
      , monthDay 6 21
      )
    ]

rules :: [Rule]
rules =
  [ ruleAboutTimeofday
  , ruleAbsorptionOfAfterNamedDay
  , ruleAfterDuration
  , ruleAfterTimeofday
  , ruleAfternoon
  , ruleAtTimeofday
  , ruleBetweenDatetimeAndDatetimeInterval
  , ruleBetweenTimeofdayAndTimeofdayInterval
  , ruleByTheEndOfTime
  , ruleByTime
  , ruleCycleAfterTime
  , ruleCycleBeforeTime
  , ruleDatetimeDatetimeInterval
  , ruleDayaftertomorrowSingleword
  , ruleDayBeforeYesterdaySingleWord
  , ruleDayofmonthNonOrdinalNamedmonth
  , ruleDayofmonthNonOrdinalOfNamedmonth
  , ruleDayofmonthOrdinal
  , ruleDayofmonthOrdinalOfNamedmonth
  , ruleDayofmonthordinalNamedmonth
  , ruleDayofmonthordinalNamedmonthYear
  , ruleDurationAfterTime
  , ruleDurationAgo
  , ruleDurationBeforeTime
  , ruleDurationFromNow
  , ruleDurationHence
  , ruleEomendOfMonth
  , ruleEoyendOfYear
  , ruleEveningnight
  , ruleExactlyTimeofday
  , ruleFromDatetimeDatetimeInterval
  , ruleFromHourofdayHourofdayInterval
  , ruleFromTimeofdayTimeofdayInterval
  , ruleHhmm
  , ruleHhmmMilitary
  , ruleHhmmMilitaryAmpm
  , ruleHhmmss
  , ruleHourofdayHourofdayInterval
  , ruleHourofdayIntegerAsRelativeMinutes
  , ruleHourofdayQuarter
  , ruleHourofdayHalf
  , ruleInDuration
  , ruleInduringThePartofday
  , ruleIntegerLatentTimeofday
  , ruleIntersect
  , ruleIntersectBy
  , ruleIntersectByOfFromS
  , ruleLastCycle
  , ruleLastCycleOfTime
  , ruleLastCycleTime
  , ruleLastDayofweekOfTime
  , ruleLastDayofweekTime
  , ruleLastNCycle
  , ruleLastTime
  , ruleLunch
  , ruleMmdd
  , ruleMmddyyyy
  , ruleMonthDdddInterval
  , ruleMorning
  , ruleNamedmonthDayofmonthNonOrdinal
  , ruleNamedmonthDayofmonthOrdinal
  , ruleNextCycle
  , ruleNextNCycle
  , ruleNextTime
  , ruleNoon
  , ruleNow
  , ruleNthTimeAfterTime
  , ruleNthTimeOfTime
  , ruleNthTimeTime
  , ruleOnANamedday
  , ruleOnDate
  , ruleOrdinalAsHour
  , ruleOrdinalCycleAfterTime
  , ruleOrdinalCycleOfTime
  , ruleOrdinalCycleTime
  , ruleOrdinalQuarter
  , ruleOrdinalQuarterYear
  , ruleRelativeMinutesAfterpastIntegerHourofday
  , ruleQuarterAfterpastIntegerHourofday
  , ruleHalfAfterpastIntegerHourofday
  , ruleRelativeMinutesTotillbeforeIntegerHourofday
  , ruleQuarterTotillbeforeIntegerHourofday
  , ruleHalfTotillbeforeIntegerHourofday
  , ruleTheCycleOfTime
  , ruleTheIdesOfNamedmonth
  , ruleTheOrdinalCycleAfterTime
  , ruleThisCycle
  , ruleThisPartofday
  , ruleThisTime
  , ruleThisnextDayofweek
  , ruleTimeAfterNext
  , ruleTimeBeforeLast
  , ruleTimePartofday
  , ruleTimeofdayApproximately
  , ruleTimeofdayOclock
  , ruleTimeofdayPopoudniuwieczoremwNocy
  , ruleTimeofdayRano
  , ruleTimeofdaySharp
  , ruleTimeofdayTimeofdayInterval
  , ruleToday
  , ruleTomorrow
  , ruleUntilTimeofday
  , ruleWeekend
  , ruleWithinDuration
  , ruleYear
  , ruleYearLatent
  , ruleYearLatent2
  , ruleYesterday
  , ruleYyyymmdd
  , ruleTimezone
  ]
  ++ ruleDaysOfWeek
  ++ ruleMonths
  ++ ruleHolidays
  ++ ruleSeasons
  ++ ruleComputedHolidays


-- 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 OverloadedStrings #-}

module Duckling.Time.PT.Corpus
  ( corpus
  , negativeCorpus
  ) where

import Data.String
import Prelude

import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types hiding (examples)
import Duckling.Time.Corpus
import Duckling.Time.Types hiding (Month)
import Duckling.TimeGrain.Types hiding (add)

corpus :: Corpus
corpus = (testContext {locale = makeLocale PT Nothing}, testOptions, allExamples)

negativeCorpus :: NegativeCorpus
negativeCorpus = (testContext {locale = makeLocale PT Nothing}, testOptions, examples)
  where
    examples =
      [ "no 987"
      , "um"
      , "um dos"
      , "um dos minutos"
      , "ter"
      ]

allExamples :: [Example]
allExamples = concat
  [ examples (datetime (2013, 2, 12, 4, 30, 0) Second)
             [ "agora"
             , "jÃ¡"
             , "ja"
             , "nesse instante"
             , "neste instante"
             ]
  , examples (datetime (2013, 2, 12, 0, 0, 0) Day)
             [ "hoje"
             , "nesse momento"
             , "neste momento"
             ]
  , examples (datetime (2013, 2, 11, 0, 0, 0) Day)
             [ "ontem"
             ]
  , examples (datetime (2013, 2, 10, 0, 0, 0) Day)
             [ "antes de ontem"
             , "anteontem"
             ]
  , examples (datetime (2013, 2, 13, 0, 0, 0) Day)
             [ "amanhÃ£"
             , "amanha"
             ]
  , examples (datetime (2013, 2, 14, 0, 0, 0) Day)
             [ "depois de amanhÃ£"
             , "depois de amanha"
             ]
  , examples (datetime (2013, 2, 18, 0, 0, 0) Day)
             [ "segunda-feira"
             , "segunda feira"
             , "segunda"
             , "seg."
             , "seg"
             , "essa segunda-feira"
             , "essa segunda feira"
             , "essa segunda"
             ]
  , examples (datetime (2013, 2, 18, 0, 0, 0) Day)
             [ "segunda, 18 de fevereiro"
             ]
  , examples (datetime (2013, 2, 19, 0, 0, 0) Day)
             [ "terÃ§a-feira"
             , "terÃ§a feira"
             , "terÃ§a"
             , "terca-feira"
             , "terca feira"
             , "terca"
             , "ter."
             ]
  , examples (datetime (2013, 2, 13, 0, 0, 0) Day)
             [ "quarta-feira"
             , "quarta feira"
             , "quarta"
             , "qua."
             , "qua"
             ]
  , examples (datetime (2013, 2, 14, 0, 0, 0) Day)
             [ "quinta-feira"
             , "quinta feira"
             , "quinta"
             , "qui."
             , "qui"
             ]
  , examples (datetime (2013, 2, 15, 0, 0, 0) Day)
             [ "sexta-feira"
             , "sexta feira"
             , "sexta"
             , "sex."
             , "sex"
             ]
  , examples (datetime (2013, 2, 16, 0, 0, 0) Day)
             [ "sÃ¡bado"
             , "sabado"
             , "sÃ¡b."
             , "sÃ¡b"
             , "sab."
             , "sab"
             ]
  , examples (datetime (2013, 2, 17, 0, 0, 0) Day)
             [ "domingo"
             , "dom."
             , "dom"
             ]
  , examples (datetime (2013, 5, 5, 0, 0, 0) Day)
             [ "5 de maio"
             , "5 maio"
             , "05 maio"
             , "cinco de maio"
             , "cinco maio"
             , "maio 5"
             , "maio cinco"
             ]
  , examples (datetime (2013, 5, 5, 0, 0, 0) Day)
             [ "cinco de maio de 2013"
             , "5 de maio de 2013"
             , "5 maio de 2013"
             , "5 de maio 2013"
             , "5 maio 2013"
             , "5/5"
             , "5/5/2013"
             ]
  , examples (datetime (2013, 7, 4, 0, 0, 0) Day)
             [ "4 de julho"
             , "04 de julho"
             , "04 julho"
             , "quatro de julho"
             , "quatro julho"
             , "julho 4"
             , "julho quatro"
             , "4/7"
             , "4/7/2013"
             ]
  , examples (datetime (2013, 3, 3, 0, 0, 0) Day)
             [ "3 de marÃ§o"
             , "trÃªs de marÃ§o"
             , "tres de marÃ§o"
             , "3/3"
             , "3/3/2013"
             ]
  , examples (datetime (2013, 4, 5, 0, 0, 0) Day)
             [ "5 de abril"
             , "cinco de abril"
             ]
  , examples (datetime (2013, 3, 1, 0, 0, 0) Day)
             [ "1 de marÃ§o"
             , "primeiro de marÃ§o"
             , "um de marÃ§o"
             , "1o de marÃ§o"
             ]
  , examples (datetime (2013, 3, 1, 0, 0, 0) Day)
             [ "1-3-2013"
             , "1.3.2013"
             , "1/3/2013"
             ]
  , examples (datetime (2013, 2, 16, 0, 0, 0) Day)
             [ "essa dia 16"
             , "16 de fevereiro"
             ]
  , examples (datetime (2013, 2, 17, 0, 0, 0) Day)
             [ "este dia 17"
             , "17 de fevereiro"
             , "17/2"
             , "no domingo"
             , "no dia 17"
             , "dia 17"
             ]
  , examples (datetime (2013, 2, 20, 0, 0, 0) Day)
             [ "esse dia 20"
             , "20 de fevereiro"
             , "20/2"
             ]
  , examples (datetime (1974, 10, 31, 0, 0, 0) Day)
             [ "31/10/1974"
             , "31/10/74"
             ]
  , examples (datetime (2013, 2, 19, 0, 0, 0) Day)
             [ "prÃ³xima terÃ§a-feira"
             , "prÃ³xima terÃ§a feira"
             , "prÃ³xima terÃ§a"
             , "proxima terÃ§a-feira"
             , "proxima terÃ§a feira"
             , "proxima terÃ§a"
             ]
  , examples (datetime (2013, 2, 20, 0, 0, 0) Day)
             [ "quarta que vem"
             , "quarta da semana que vem"
             , "quarta da prÃ³xima semana"
             ]
  , examples (datetime (2013, 2, 12, 0, 0, 0) Day)
             [ "terÃ§a desta semana"
             , "terÃ§a dessa semana"
             , "terÃ§a agora"
             ]
  , examples (datetime (2013, 2, 11, 0, 0, 0) Week)
             [ "esta semana"
             ]
  , examples (datetime (2013, 2, 4, 0, 0, 0) Week)
             [ "semana passada"
             , "semana anterior"
             , "passada semana"
             , "anterior semana"
             , "Ãºltima semana"
             , "ultima semana"
             ]
  , examples (datetime (2013, 2, 18, 0, 0, 0) Week)
             [ "semana que vem"
             , "proxima semana"
             ]
  , examples (datetime (2013, 1, 0, 0, 0, 0) Month)
             [ "mÃªs passado"
             ]
  , examples (datetime (2013, 3, 0, 0, 0, 0) Month)
             [ "mes que vem"
             , "prÃ³ximo mÃªs"
             ]
  , examples (datetime (2012, 0, 0, 0, 0, 0) Year)
             [ "ano passado"
             ]
  , examples (datetime (2013, 0, 0, 0, 0, 0) Year)
             [ "este ano"
             ]
  , examples (datetime (2014, 0, 0, 0, 0, 0) Year)
             [ "ano que vem"
             , "proximo ano"
             ]
  , examples (datetime (2013, 2, 10, 0, 0, 0) Day)
             [ "domingo passado"
             , "domingo da semana passada"
             ]
  , examples (datetime (2013, 2, 5, 0, 0, 0) Day)
             [ "terÃ§a passada"
             ]
  , examples (datetime (2013, 2, 12, 15, 0, 0) Hour)
             [ "Ã s tres da tarde"
             , "Ã s tres"
             , "Ã s 3 pm"
             , "Ã s 15 horas"
             ]
  , examples (datetime (2013, 2, 12, 20, 0, 0) Hour)
             [ "Ã s oito da noite"
             ]
  , examples (datetime (2013, 2, 13, 20, 0, 0) Hour)
             [ "amanhÃ£ Ã s oito da noite"
             ]
  , examples (datetime (2013, 2, 15, 18, 0, 0) Hour)
             [ "no dia 15 Ã s 18"
             , "dia 15 Ã s 18"
             , "dia 15 Ã s 18 horas"
             , "dia 15 Ã s dezoito"
             , "dia quinze Ã s dezoito"
             ]
  , examples (datetime (2013, 2, 14, 18, 0, 0) Hour)
             [ "quinta-feira Ã s 18 horas"
             , "quinta Ã s 18"
             ]
  , examples (datetime (2013, 2, 15, 18, 15, 0) Minute)
             [ "dia 15 de Fevereiro Ã s 18:15"
             , "dia 15 Ã s 18:15"
             ]
  , examples (datetime (2013, 2, 12, 15, 0, 0) Minute)
             [ "15:00"
             , "15.00"
             , "Ã s 15:00"
             , "Ã s 15.00"
             ]
  , examples (datetime (2013, 2, 13, 0, 0, 0) Hour)
             [ "meianoite"
             , "meia noite"
             ]
  , examples (datetime (2013, 2, 12, 12, 0, 0) Hour)
             [ "meio dia"
             , "meiodia"
             ]
  , examples (datetime (2013, 2, 12, 12, 15, 0) Minute)
             [ "meio dia e quinze"
             ]
  , examples (datetime (2013, 2, 12, 11, 55, 0) Minute)
             [ "5 para meio dia"
             ]
  , examples (datetime (2013, 2, 12, 12, 30, 0) Minute)
             [ "meio dia e meia"
             ]
  , examples (datetime (2013, 2, 12, 6, 0, 0) Hour)
             [ "as seis da manha"
             , "as seis pela manha"
             ]
  , examples (datetime (2013, 2, 12, 15, 15, 0) Minute)
             [ "Ã s tres e quinze"
             , "Ã s tres e quinze da tarde"
             , "Ã s tres e quinze pela tarde"
             , "15:15"
             , "15.15"
             ]
  , examples (datetime (2013, 2, 12, 15, 30, 0) Minute)
             [ "Ã s tres e meia"
             , "Ã s 3 e trinta"
             , "Ã s tres e meia da tarde"
             , "Ã s 3 e trinta da tarde"
             , "15:30"
             , "15.30"
             ]
  , examples (datetime (2013, 2, 12, 11, 45, 0) Minute)
             [ "quinze para meio dia"
             , "quinze para o meio dia"
             , "11:45"
             , "as onze e quarenta e cinco"
             , "hoje quinze para o meio dia"
             , "hoje Ã s onze e quarenta e cinco"
             ]
  , examples (datetime (2013, 2, 12, 5, 15, 0) Minute)
             [ "5 e quinze"
             ]
  , examples (datetime (2013, 2, 12, 6, 0, 0) Hour)
             [ "6 da manhÃ£"
             ]
  , examples (datetime (2013, 2, 13, 11, 0, 0) Hour)
             [ "quarta Ã s onze da manhÃ£"
             ]
  , examples (datetime (2014, 9, 12, 0, 0, 0) Day)
             [ "sexta, 12 de setembro de 2014"
             , "sexta feira, 12 de setembro de 2014"
             , "12 de setembro de 2014, sexta"
             , "12 de setembro de 2014 sexta feira"
             , "sexta feira 12 de setembro de 2014"
             ]
  , examples (datetime (2013, 2, 12, 4, 30, 1) Second)
             [ "em um segundo"
             ]
  , examples (datetime (2013, 2, 12, 4, 31, 0) Second)
             [ "em um minuto"
             , "em 1 min"
             ]
  , examples (datetime (2013, 2, 12, 4, 32, 0) Second)
             [ "em 2 minutos"
             , "em dois minutos"
             ]
  , examples (datetime (2013, 2, 12, 5, 30, 0) Second)
             [ "em 60 minutos"
             ]
  , examples (datetime (2013, 2, 12, 5, 30, 0) Minute)
             [ "em uma hora"
             ]
  , examples (datetime (2013, 2, 12, 2, 30, 0) Minute)
             [ "fazem duas horas"
             ]
  , examples (datetime (2013, 2, 13, 4, 30, 0) Minute)
             [ "em 24 horas"
             , "em vinte e quatro horas"
             ]
  , examples (datetime (2013, 2, 13, 4, 0, 0) Hour)
             [ "em um dia"
             ]
  , examples (datetime (2013, 2, 19, 4, 0, 0) Hour)
             [ "em 7 dias"
             ]
  , examples (datetime (2013, 2, 19, 0, 0, 0) Day)
             [ "em uma semana"
             ]
  , examples (datetime (2013, 1, 22, 0, 0, 0) Day)
             [ "faz tres semanas"
             , "faz trÃªs semanas"
             ]
  , examples (datetime (2013, 4, 12, 0, 0, 0) Day)
             [ "em dois meses"
             ]
  , examples (datetime (2012, 11, 12, 0, 0, 0) Day)
             [ "faz tres meses"
             ]
  , examples (datetime (2014, 2, 0, 0, 0, 0) Month)
             [ "em um ano"
             , "em 1 ano"
             ]
  , examples (datetime (2011, 2, 0, 0, 0, 0) Month)
             [ "faz dois anos"
             ]
  , examples (datetimeInterval ((2013, 6, 21, 0, 0, 0), (2013, 9, 24, 0, 0, 0)) Day)
             [ "este verÃ£o"
             , "este verao"
             ]
  , examples (datetimeInterval ((2012, 12, 21, 0, 0, 0), (2013, 3, 21, 0, 0, 0)) Day)
             [ "este inverno"
             ]
  , examples (datetime (2013, 12, 25, 0, 0, 0) Day)
             [ "Natal"
             ]
  , examples (datetime (2013, 12, 31, 0, 0, 0) Day)
             [ "vÃ©spera de ano novo"
             ]
  , examples (datetime (2014, 1, 1, 0, 0, 0) Day)
             [ "ano novo"
             , "reveillon"
             , "Reveillon"
             ]
  , examples (datetimeInterval ((2013, 2, 12, 18, 0, 0), (2013, 2, 13, 0, 0, 0)) Hour)
             [ "esta noite"
             , "essa noite"
             ]
  , examples (datetimeInterval ((2013, 2, 13, 18, 0, 0), (2013, 2, 14, 0, 0, 0)) Hour)
             [ "amanhÃ£ a noite"
             , "amanhÃ£ Ã  noite"
             ]
  , examples (datetimeInterval ((2013, 2, 11, 18, 0, 0), (2013, 2, 12, 0, 0, 0)) Hour)
             [ "ontem a noite"
             , "ontem Ã  noite"
             ]
  , examples (datetimeInterval ((2013, 2, 13, 12, 0, 0), (2013, 2, 13, 19, 0, 0)) Hour)
             [ "amanhÃ£ a tarde"
             , "amanhÃ£ Ã  tarde"
             ]
  , examples (datetimeInterval ((2013, 2, 11, 12, 0, 0), (2013, 2, 11, 19, 0, 0)) Hour)
             [ "ontem a tarde"
             , "ontem Ã  tarde"
             ]
  , examples (datetimeInterval ((2013, 2, 15, 18, 0, 0), (2013, 2, 18, 0, 0, 0)) Hour)
             [ "este final de semana"
             , "este fim de semana"
             ]
  , examples (datetimeInterval ((2013, 2, 18, 4, 0, 0), (2013, 2, 18, 12, 0, 0)) Hour)
             [ "segunda de manhÃ£"
             ]
  , examples (datetimeInterval ((2013, 2, 15, 4, 0, 0), (2013, 2, 15, 12, 0, 0)) Hour)
             [ "dia 15 de fevereiro pela manhÃ£"
             , "dia 15 de fevereiro de manhÃ£"
             ]
  , examples (datetime (2013, 2, 12, 20, 0, 0) Hour)
             [ "Ã s 8 da noite"
             ]
  , examples (datetimeInterval ((2013, 2, 12, 4, 29, 58), (2013, 2, 12, 4, 30, 0)) Second)
             [ "2 segundos atras"
             ]
  , examples (datetimeInterval ((2013, 2, 12, 4, 30, 1), (2013, 2, 12, 4, 30, 4)) Second)
             [ "proximos 3 segundos"
             ]
  , examples (datetimeInterval ((2013, 2, 12, 4, 28, 0), (2013, 2, 12, 4, 30, 0)) Minute)
             [ "2 minutos atrÃ¡s"
             ]
  , examples (datetimeInterval ((2013, 2, 12, 4, 31, 0), (2013, 2, 12, 4, 34, 0)) Minute)
             [ "proximos 3 minutos"
             ]
  , examples (datetimeInterval ((2013, 2, 12, 5, 0, 0), (2013, 2, 12, 8, 0, 0)) Hour)
             [ "proximas 3 horas"
             ]
  , examples (datetimeInterval ((2013, 2, 10, 0, 0, 0), (2013, 2, 12, 0, 0, 0)) Day)
             [ "passados 2 dias"
             ]
  , examples (datetimeInterval ((2013, 2, 13, 0, 0, 0), (2013, 2, 16, 0, 0, 0)) Day)
             [ "proximos 3 dias"
             ]
  , examples (datetimeInterval ((2013, 1, 28, 0, 0, 0), (2013, 2, 11, 0, 0, 0)) Week)
             [ "duas semanas atras"
             ]
  , examples (datetimeInterval ((2013, 2, 18, 0, 0, 0), (2013, 3, 11, 0, 0, 0)) Week)
             [ "3 proximas semanas"
             , "3 semanas que vem"
             ]
  , examples (datetimeInterval ((2012, 12, 0, 0, 0, 0), (2013, 2, 0, 0, 0, 0)) Month)
             [ "passados 2 meses"
             , "Ãºltimos 2 meses"
             , "2 meses anteriores"
             , "2 Ãºltimos meses"
             , "2 anteriores meses"
             ]
  , examples (datetimeInterval ((2013, 3, 0, 0, 0, 0), (2013, 6, 0, 0, 0, 0)) Month)
             [ "3 prÃ³ximos meses"
             , "proximos tres meses"
             , "tres meses que vem"
             ]
  , examples (datetimeInterval ((2011, 0, 0, 0, 0, 0), (2013, 0, 0, 0, 0, 0)) Year)
             [ "passados 2 anos"
              , "Ãºltimos 2 anos"
              , "2 anos anteriores"
              , "2 Ãºltimos anos"
              , "2 anteriores anos"
             ]
  , examples (datetimeInterval ((2014, 0, 0, 0, 0, 0), (2017, 0, 0, 0, 0, 0)) Year)
             [ "3 prÃ³ximos anos"
             , "proximo tres anos"
             , "3 anos que vem"
             ]
  , examples (datetimeInterval ((2013, 7, 13, 0, 0, 0), (2013, 7, 16, 0, 0, 0)) Day)
             [ "13 a 15 de julho"
             , "13 - 15 de julho de 2013"
             ]
  , examples (datetimeInterval ((2013, 2, 12, 9, 30, 0), (2013, 2, 12, 11, 1, 0)) Minute)
             [ "9:30 - 11:00"
             ]
  , examples (datetimeInterval ((2013, 12, 21, 0, 0, 0), (2014, 1, 7, 0, 0, 0)) Day)
             [ "21 de Dez. a 6 de Jan"
             ]
  , examples (datetimeInterval ((2013, 2, 12, 4, 30, 0), (2013, 2, 12, 7, 30, 0)) Second)
             [ "dentro de tres horas"
             ]
  , examples (datetime (2013, 2, 12, 16, 0, 0) Hour)
             [ "as quatro da tarde"
             ]
  , examples (datetime (2013, 2, 12, 13, 0, 0) Minute)
             [ "as quatro CET"
             ]
  , examples (datetimeOpenInterval After (2013, 2, 12, 12, 0, 0) Hour)
             [ "apÃ³s ao meio dia"
             , "depois do meio dia"
             ]
  , examples (datetimeOpenInterval Before (2013, 2, 12, 12, 0, 0) Hour)
             [ "antes do meio dia"
             , "nÃ£o mais que meio dia"
             ]
  , examples (datetimeOpenInterval After (2013, 2, 13, 15, 0, 0) Hour)
             [ "amanhÃ£ depois das 15hs"
             , "amanha apÃ³s as quinze horas"
             ]
  , examples (datetimeOpenInterval Before (2013, 2, 13, 0, 0, 0) Hour)
             [ "antes da meia noite"
             , "atÃ© a meia noite"
             ]
 ,examples (datetime (2013, 2, 12, 3, 0, 0) Hour)
             [ "Ãºltima hora"
             , "hora anterior"
             , "hora passada"
             ]
  , examples (datetime (2013, 1, 1, 0, 0, 0) Quarter)
             [ "este trimestre"
             , "trimestre actual"
             , "trimestre atual"
             ]
  , examples (datetime (2013, 1, 1, 0, 0, 0) Month)
             [ "primeiro mÃªs de 2013"
             , "primeiro mÃªs 2013"
             ]
  , examples (datetime (2013, 4, 1, 0, 0, 0) Quarter)
             [ "prÃ³ximo trimestre"
             , "segundo trimestre de 2013"
             , "segundo trimestre"
             ]
  , examples (datetime (2013, 7, 1, 0, 0, 0) Quarter)
             [ "terceiro trimestre"
             ]
  , examples (datetime (2018, 10, 1, 0, 0, 0) Quarter)
             [ "quarto trimestre de 2018"
             , "quarto trimestre 2018"
             ]
  , examples (datetime (2012, 10, 1, 0, 0, 0) Quarter)
             [ "trimestre passado"
             , "trimestre anterior"
             , "Ãºltimo trimestre"
             , "ultimo trimestre"
             ]
  , examples (datetime (2013, 12, 1, 0, 0, 0) Month)
             [ "dÃ©cimo segundo mÃªs de 2013"
             , "Ãºltimo mÃªs de 2013"
             , "Ãºltimo mÃªs 2013"
             ]
  , examples (datetime (2015, 10, 1, 0, 0, 0) Quarter)
             [ "Ãºltimo trimestre de 2015"
             , "Ãºltimo trimestre 2015"
             ]
  , examples (datetimeInterval ((2013, 7, 13, 0, 0, 0), (2013, 7, 16, 0, 0, 0)) Day)
             [ "desde 13 a 15 de Julho"
             , "a partir de 13 atÃ© 15 de Julho"
             , "desde 13 atÃ© 15 de Julho"
             , "13-15 de Julho"
             , "13 atÃ© 15 de Julho"
             , "13 a 15 de Julho"
             , "desde 13 a 15 Julho"
             , "a partir de 13 atÃ© 15 Julho"
             , "desde 13 atÃ© 15 Julho"
             , "13-15 Julho"
             , "13 atÃ© 15 Julho"
             , "13 a 15 Julho"
             ]
  , examples (datetimeInterval ((2017, 1, 1, 0, 0, 0), (2017, 10, 1, 0, 0, 0)) Quarter)
             [ "de primeiro trimestre de 2017 atÃ© terceiro trimestre de 2017"
             , "de primeiro trimestre de 2017 atÃ© ao terceiro trimestre de 2017"
             , "do primeiro trimestre de 2017 atÃ© terceiro trimestre de 2017"
             , "do primeiro trimestre de 2017 atÃ© ao terceiro trimestre de 2017"
             , "desde primeiro trimestre de 2017 atÃ© terceiro trimestre de 2017"
             , "desde primeiro trimestre de 2017 atÃ© ao terceiro trimestre de 2017"
             , "a partir do primeiro trimestre de 2017 atÃ© terceiro trimestre de 2017"
             , "a partir do primeiro trimestre de 2017 atÃ© ao terceiro trimestre de 2017"
             , "a partir de primeiro trimestre de 2017 atÃ© terceiro trimestre de 2017"
             , "a partir de primeiro trimestre de 2017 atÃ© ao terceiro trimestre de 2017"
             , "primeiro trimestre de 2017 a terceiro trimestre de 2017"
             , "primeiro trimestre de 2017 ao terceiro trimestre de 2017"
             , "primeiro trimestre de 2017 - terceiro trimestre de 2017"
             , "entre primeiro trimestre de 2017 e terceiro trimestre de 2017"
             , "entre o primeiro trimestre de 2017 e terceiro trimestre de 2017"
             , "primeiro trimestre de 2017 atÃ© terceiro trimestre de 2017"
             , "primeiro trimestre de 2017 atÃ© ao terceiro trimestre de 2017"
             , "primeiro trimestre 2017 a terceiro trimestre 2017"
             , "primeiro trimestre 2017 ao terceiro trimestre 2017"
             , "primeiro trimestre 2017 - terceiro trimestre 2017"
             , "primeiro trimestre 2017 atÃ© terceiro trimestre 2017"
             , "primeiro trimestre 2017 atÃ© ao terceiro trimestre 2017"
             , "entre primeiro trimestre 2017 e terceiro trimestre 2017"
             , "entre o primeiro trimestre 2017 e terceiro trimestre 2017"
             ]
  , examples (datetimeInterval ((2017, 3, 1, 0, 0, 0), (2017, 10, 1, 0, 0, 0)) Month)
              [ "de terceiro mÃªs de 2017 atÃ© nono mÃªs de 2017"
              , "de terceiro mÃªs de 2017 atÃ© ao nono mÃªs de 2017"
              , "do terceiro mÃªs de 2017 atÃ© nono mÃªs de 2017"
              , "do terceiro mÃªs de 2017 atÃ© ao nono mÃªs de 2017"
              , "desde terceiro mÃªs de 2017 atÃ© nono mÃªs de 2017"
              , "desde terceiro mÃªs de 2017 atÃ© ao nono mÃªs de 2017"
              , "a partir do terceiro mÃªs de 2017 atÃ© nono mÃªs de 2017"
              , "a partir do terceiro mÃªs de 2017 atÃ© ao nono mÃªs de 2017"
              , "a partir de terceiro mÃªs de 2017 atÃ© nono mÃªs de 2017"
              , "a partir de terceiro mÃªs de 2017 atÃ© ao nono mÃªs de 2017"
              , "terceiro mÃªs de 2017 a nono mÃªs de 2017"
              , "terceiro mÃªs de 2017 ao nono mÃªs de 2017"
              , "terceiro mÃªs de 2017 - nono mÃªs de 2017"
              , "entre terceiro mÃªs de 2017 e nono mÃªs de 2017"
              , "entre o terceiro mÃªs de 2017 e nono mÃªs de 2017"
              , "terceiro mÃªs de 2017 atÃ© nono mÃªs de 2017"
              , "terceiro mÃªs de 2017 atÃ© ao nono mÃªs de 2017"
              , "terceiro mÃªs 2017 a nono mÃªs 2017"
              , "terceiro mÃªs 2017 ao nono mÃªs 2017"
              , "terceiro mÃªs 2017 - nono mÃªs 2017"
              , "terceiro mÃªs 2017 atÃ© nono mÃªs 2017"
              , "terceiro mÃªs 2017 atÃ© ao nono mÃªs 2017"
              , "entre terceiro mÃªs 2017 e nono mÃªs 2017"
              , "entre o terceiro mÃªs 2017 e nono mÃªs 2017"
              ]
  ]