#
# Copyright 2013 Quantopian, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


# References:
# http://www.londonstockexchange.com
# /about-the-exchange/company-overview/business-days/business-days.htm
# http://en.wikipedia.org/wiki/Bank_holiday
# http://www.adviceguide.org.uk/england/work_e/work_time_off_work_e/
# bank_and_public_holidays.htm

import pytz

import pandas as pd

from datetime import datetime
from dateutil import rrule
from zipline.utils.tradingcalendar import end

start = datetime(2002, 1, 1, tzinfo=pytz.utc)

non_trading_rules = []
# Weekends
weekends = rrule.rrule(
    rrule.YEARLY,
    byweekday=(rrule.SA, rrule.SU),
    cache=True,
    dtstart=start,
    until=end
)
non_trading_rules.append(weekends)
# New Year's Day
new_year = rrule.rrule(
    rrule.MONTHLY,
    byyearday=1,
    cache=True,
    dtstart=start,
    until=end
)
# If new years day is on Saturday then Monday 3rd is a holiday
# If new years day is on Sunday then Monday 2nd is a holiday
weekend_new_year = rrule.rrule(
    rrule.MONTHLY,
    bymonth=1,
    bymonthday=[2, 3],
    byweekday=(rrule.MO),
    cache=True,
    dtstart=start,
    until=end
)
non_trading_rules.append(new_year)
non_trading_rules.append(weekend_new_year)
# Good Friday
good_friday = rrule.rrule(
    rrule.DAILY,
    byeaster=-2,
    cache=True,
    dtstart=start,
    until=end
)
non_trading_rules.append(good_friday)
# Easter Monday
easter_monday = rrule.rrule(
    rrule.DAILY,
    byeaster=1,
    cache=True,
    dtstart=start,
    until=end
)
non_trading_rules.append(easter_monday)
# Early May Bank Holiday (1st Monday in May)
may_bank = rrule.rrule(
    rrule.MONTHLY,
    bymonth=5,
    byweekday=(rrule.MO(1)),
    cache=True,
    dtstart=start,
    until=end
)
non_trading_rules.append(may_bank)
# Spring Bank Holiday (Last Monday in May)
spring_bank = rrule.rrule(
    rrule.MONTHLY,
    bymonth=5,
    byweekday=(rrule.MO(-1)),
    cache=True,
    dtstart=datetime(2003, 1, 1, tzinfo=pytz.utc),
    until=end
)
non_trading_rules.append(spring_bank)
# Summer Bank Holiday (Last Monday in August)
summer_bank = rrule.rrule(
    rrule.MONTHLY,
    bymonth=8,
    byweekday=(rrule.MO(-1)),
    cache=True,
    dtstart=start,
    until=end
)
non_trading_rules.append(summer_bank)
# Christmas Day
christmas = rrule.rrule(
    rrule.MONTHLY,
    bymonth=12,
    bymonthday=25,
    cache=True,
    dtstart=start,
    until=end
)
# If christmas day is Saturday Monday 27th is a holiday
# If christmas day is sunday the Tuesday 27th is a holiday
weekend_christmas = rrule.rrule(
    rrule.MONTHLY,
    bymonth=12,
    bymonthday=27,
    byweekday=(rrule.MO, rrule.TU),
    cache=True,
    dtstart=start,
    until=end
)

non_trading_rules.append(christmas)
non_trading_rules.append(weekend_christmas)
# Boxing Day
boxing_day = rrule.rrule(
    rrule.MONTHLY,
    bymonth=12,
    bymonthday=26,
    cache=True,
    dtstart=start,
    until=end
)
# If boxing day is saturday then Monday 28th is a holiday
# If boxing day is sunday then Tuesday 28th is a holiday
weekend_boxing_day = rrule.rrule(
    rrule.MONTHLY,
    bymonth=12,
    bymonthday=28,
    byweekday=(rrule.MO, rrule.TU),
    cache=True,
    dtstart=start,
    until=end
)

non_trading_rules.append(boxing_day)
non_trading_rules.append(weekend_boxing_day)

non_trading_ruleset = rrule.rruleset()

# In 2002 May bank holiday was moved to 4th June to follow the Queens
# Golden Jubilee
non_trading_ruleset.exdate(datetime(2002, 9, 27, tzinfo=pytz.utc))
non_trading_ruleset.rdate(datetime(2002, 6, 3, tzinfo=pytz.utc))
non_trading_ruleset.rdate(datetime(2002, 6, 4, tzinfo=pytz.utc))
# TODO: not sure why Feb 18 2008 is not available in the yahoo data
non_trading_ruleset.rdate(datetime(2008, 2, 18, tzinfo=pytz.utc))
# In 2011 The Friday before Mayday was the Royal Wedding
non_trading_ruleset.rdate(datetime(2011, 4, 29, tzinfo=pytz.utc))
# In 2012 May bank holiday was moved to 4th June to preceed the Queens
# Diamond Jubilee
non_trading_ruleset.exdate(datetime(2012, 5, 28, tzinfo=pytz.utc))
non_trading_ruleset.rdate(datetime(2012, 6, 4, tzinfo=pytz.utc))
non_trading_ruleset.rdate(datetime(2012, 6, 5, tzinfo=pytz.utc))

for rule in non_trading_rules:
    non_trading_ruleset.rrule(rule)

non_trading_days = non_trading_ruleset.between(start, end, inc=True)
non_trading_day_index = pd.DatetimeIndex(sorted(non_trading_days))

business_days = pd.DatetimeIndex(start=start, end=end,
                                 freq=pd.datetools.BDay())

trading_days = business_days.difference(non_trading_day_index)
