-- 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. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.


{-# LANGUAGE GADTs #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Time.EN.AU.Rules
  ( rules
  ) where

import Data.Maybe
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers (parseInt)
import Duckling.Regex.Types
import Duckling.Time.Computed (easterSunday)
import Duckling.Time.Helpers
import Duckling.Time.Types (TimeData (..))
import Duckling.Types
import qualified Duckling.Time.Types as TTime
import qualified Duckling.TimeGrain.Types as TG

ruleDDMM :: Rule
ruleDDMM = Rule
  { name = "dd/mm"
  , pattern =
    [ regex "(3[01]|[12]\\d|0?[1-9])\\s?[/-]\\s?(1[0-2]|0?[1-9])"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (dd:mm:_)):_) -> do
        d <- parseInt dd
        m <- parseInt mm
        tt $ monthDay m d
      _ -> Nothing
  }

ruleDDMMYYYY :: Rule
ruleDDMMYYYY = Rule
  { name = "dd/mm/yyyy"
  , pattern =
    [ regex "(3[01]|[12]\\d|0?[1-9])[-/\\s](1[0-2]|0?[1-9])[-/\\s](\\d{2,4})"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (dd:mm:yy:_)):_) -> do
        y <- parseInt yy
        d <- parseInt dd
        m <- parseInt mm
        tt $ yearMonthDay y m d
      _ -> Nothing
  }

-- Clashes with HHMMSS, hence only 4-digit years
ruleDDMMYYYYDot :: Rule
ruleDDMMYYYYDot = Rule
  { name = "dd.mm.yyyy"
  , pattern =
    [ regex "(3[01]|[12]\\d|0?[1-9])\\.(1[0-2]|0?[1-9])\\.(\\d{4})"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (dd:mm:yy:_)):_) -> do
        y <- parseInt yy
        d <- parseInt dd
        m <- parseInt mm
        tt $ yearMonthDay y m d
      _ -> Nothing
  }

rulePeriodicHolidays :: [Rule]
rulePeriodicHolidays = mkRuleHolidays
  -- Fixed dates, year over year
  [ ( "ANZAC Day", "anzac day", monthDay 4 25 )
  , ( "Australia Day", "(ana|anniversary|australia|foundation) day"
    , monthDay 1 26 )
  , ( "Harmony Day", "harmony day", monthDay 3 21 )
  , ( "National Sorry Day", "national sorry day", monthDay 5 26 )
  , ( "Queensland Day", "queensland day", monthDay 6 6 )
  , ( "Remembrance Day", "remembrance day", monthDay 11 11 )
  , ( "Take our Daughters and Sons to Work Day"
    , "take our daughters and sons to work day", monthDay 1 5 )

  -- Fixed day/week/month, year over year
  , ( "Adelaide Cup", "adelaide cup", nthDOWOfMonth 2 1 3 )
  , ( "Administrative Professionals' Day"
    , "(administrative professional|secretarie|admin)('?s'?)? day"
    , nthDOWOfMonth 1 5 5 )
  , ( "Canberra Day", "canberra day", nthDOWOfMonth 2 1 3 )
  , ( "Eight Hours Day", "eight hours day", nthDOWOfMonth 2 1 3 )
  , ( "Father's Day", "father'?s?'? day", nthDOWOfMonth 1 7 9 )
  , ( "Labour Day", "labour day", nthDOWOfMonth 1 1 10 )
  , ( "Melbourne Cup Day", "melbourne cup day", nthDOWOfMonth 1 2 11 )
  , ( "Mother's Day", "mother'?s?'? day", nthDOWOfMonth 2 7 5 )
  , ( "National Close the Gap Day", "national close the gap day"
    , nthDOWOfMonth 3 4 3 )
  , ( "National Tree Day", "(arbor|national tree) day"
    , predLastOf (dayOfWeek 7) (month 6) )
  , ( "National Schools Tree Day", "national schools tree day"
    , predLastOf (dayOfWeek 5) (month 6) )
  , ( "New South Wales Bank Holiday", "new south wales bank holiday"
    , nthDOWOfMonth 1 1 8 )
  , ( "Picnic Day", "(northern territory )?picnic day", nthDOWOfMonth 1 1 8 )
  , ( "Recreation Day", "recreation day", nthDOWOfMonth 1 1 10 )
  , ( "Thanksgiving Day", "thanks?giving( day)?", nthDOWOfMonth 4 4 11 )
  , ( "Western Australia Day", "western australia day", nthDOWOfMonth 1 1 6 )

  -- Other
  , ( "Reconciliation Day", "reconciliation\\s+day"
    , predNthAfter 0 (dayOfWeek 1) (monthDay 5 26) )
  ]

rulePeriodicHolidays' :: [Rule]
rulePeriodicHolidays' = mkRuleHolidays'
  -- Fixed day/week/month, year over year
  -- Week from Sunday of July until following Sunday that has the second Friday
  [ ( "NAIDOC Week"
    , "(naidoc|national aboriginal and islander day observance committee) week"
    , let fri = nthDOWOfMonth 2 5 7
          start = cycleNthAfter False TG.Day (- 5) fri
          end = cycleNthAfter False TG.Day 2 fri
      in interval TTime.Open start end )
  -- 3 days ending on the second Monday of February
  , ( "Royal Hobart Regatta", "royal hobart regatta"
    , let end = nthDOWOfMonth 2 1 2
      in interval TTime.Open (cycleNthAfter False TG.Day (- 2) end) end )

  -- Other
  -- Wednesday of the Royal Queensland Show
  -- Starts on the first Friday of August if it's not before August 5th
  -- Otherwise on the second Friday of August
  , ( "Royal Queensland Show Day"
    , "(royal (national agricultural|queensland)|rna) show day|ekka day"
    , let tentative = nthDOWOfMonth 1 5 8
          alternative = nthDOWOfMonth 2 5 8
      in do
        forbidden <- interval TTime.Open (monthDay 8 1) (monthDay 8 4)
        start <- intersectWithReplacement forbidden tentative alternative
        return $ cycleNthAfter False TG.Day 5 start )
  -- Starts on the first Friday of August if it's not before August 5th
  -- Otherwise on the second Friday of August
  , ( "Royal Queensland Show"
    , "ekka|(royal (national agricultural|queensland)|rna) show"
    , let tentative = nthDOWOfMonth 1 5 8
          alternative = nthDOWOfMonth 2 5 8
      in do
        forbidden <- interval TTime.Open (monthDay 8 1) (monthDay 8 4)
        start <- intersectWithReplacement forbidden tentative alternative
        interval TTime.Open start $ cycleNthAfter False TG.Day 9 start )
  ]

ruleComputedHolidays :: [Rule]
ruleComputedHolidays = mkRuleHolidays
  [ ( "Easter Tuesday", "easter\\s+tue(sday)?"
    , cycleNthAfter False TG.Day 2 easterSunday )
  ]

rules :: [Rule]
rules =
  [ ruleDDMM
  , ruleDDMMYYYY
  , ruleDDMMYYYYDot
  ]
  ++ ruleComputedHolidays
  ++ rulePeriodicHolidays
  ++ rulePeriodicHolidays'