/*
 Copyright (C) 2003 Niels Elken Sønderby, Mark Treiber

 This file is part of QuantLib for Mathematica, a Mathematica extension for
 QuantLib, a free-software/open-source financial C++ library
 http://www.nielses.dk/quantlib/mma
 http://quantlib.org/

 QuantLib for Mathematica is free software: you can redistribute it and/or
 modify it under the terms of the QuantLib license.  You should have received
 a copy of the license along with this program; if not, please email
 ferdinando@ametrano.net The license is also available online at
 http://quantlib.org/html/license.html

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#include "mmacalendar.hpp"

// Converter-functions by Mark Treiber <mrtreibe@engmail.uwaterloo.ca>

RollingConvention getRollingConventionfromnum(int num)
{
    switch(num)
    {
    case 1:
        return Preceding;
        break;
    case 2:
        return ModifiedPreceding;
        break;
    case 3:
        return Following;
        break;
    case 4:
        return ModifiedFollowing;
        break;
    default:
        throw("unknown");
    }

}

Calendar *createCalendarfromnum(int num)
{

    switch(num)
    {
 //       case 1 :
 //           return new Calendar(Budapest());
 //           break;
        case 2 :
            return new Calendar(Frankfurt());
            break;
        case 3:
            return new Calendar(Helsinki());
            break;
        case 4:
            return new Calendar(Johannesburg());
            break;
        case 5:
            return new Calendar(London());
            break;
        case 6:
            return new Calendar(Milan());
            break;
        case 7:
            return new Calendar(NewYork());
            break;
 //       case 8:
 //           return new Calendar(Oslo());
 //           break;
 //       case 9:
 //           return new Calendar(Stockholm());
 //           break;
        case 10:
            return new Calendar(Sydney());
            break;
        case 11:
            return new Calendar(TARGET());
            break;
        case 12:
            return new Calendar(Tokyo());
            break;
        case 13:
            return new Calendar(Toronto());
            break;
 //       case 14:
 //           return new Calendar(Warsaw());
 //           break;
        case 15:
            return new Calendar(Wellington());
            break;
        case 16:
            return new Calendar(Zurich());
            break;
        default:
            throw("unknown calendar number");
    }
}

DayCounter *createDayCounterfromnum(int num)
{

    switch(num)
    {
        case 1 :
            return new DayCounter(Actual365());
            break;
        case 2 :
            return new DayCounter(Actual360());
            break;
        case 3:
            return new DayCounter(Thirty360(Thirty360::USA));
            break;
        case 4:
            return new DayCounter(Thirty360(Thirty360::European));
            break;
        case 5:
            return new DayCounter(Thirty360(Thirty360::Italian));
            break;
        case 6:
            return new DayCounter(ActualActual(ActualActual::Bond));
            break;
        case 7:
            return new DayCounter(ActualActual(ActualActual::Euro));
            break;
        case 8:
            return new DayCounter(ActualActual(ActualActual::Historical));
            break;
        default:
            throw("unknown");
    }
}

void qlDayOfWeek(int year, int month, int day)
{
    try {
        int ret;

        Date date((Day)day, (Month)month, (Year)year);

        ret = date.weekday();

        MLPutInteger(stdlink, ret);
    } QLML_HANDLE_EXCEPTIONS
}

void qlBusinessDayQ(int year, int month, int day, int calnum)
{
    try {
        bool ret;
        Date date((Day)day, (Month)month, (Year)year);
        Calendar *cal;

        cal = createCalendarfromnum(calnum);

        ret = cal->isBusinessDay(date);

        QLMLPutBoolean(stdlink, ret);
    } QLML_HANDLE_EXCEPTIONS
}

void qlShiftDate(int year, int month, int day,
                 int shiftYears, int shiftMonths, int shiftDays,
                 int calnum)
{
    try {
        Date date((Day)day, (Month)month, (Year)year);

        if (calnum!=0) {
            Calendar *cal;
            cal = createCalendarfromnum(calnum);

            if (shiftYears!=0)
                date = cal->advance(date, shiftYears, Years);
            if (shiftMonths!=0)
                date = cal->advance(date, shiftMonths, Months);
            if (shiftDays!=0)
                date = cal->advance(date, shiftDays, Days);
        } else { // calnum = 0 (HolidayCalendar -> None)
            if (shiftYears!=0)
                date = date.plusYears(shiftYears);
            if (shiftMonths!=0)
                date = date.plusMonths(shiftMonths);
            if (shiftDays!=0)
                date = date.plusDays(shiftDays);
        }

        QLMLPutDate(stdlink, date);
    } QLML_HANDLE_EXCEPTIONS
}

void qlRollDate(int year, int month, int day, int calnum, int rollnum)
{
    try {
        Date date((Day)day, (Month)month, (Year)year);

        if (calnum!=0) {
            Calendar *cal;
            cal = createCalendarfromnum(calnum);

            RollingConvention roll;
            roll = getRollingConventionfromnum(rollnum);

            date = cal->roll(date, roll);
        } else { // calnum = 0 (HolidayCalendar -> None)
            QL_ASSERT(true, "no holiday calendar chosen");
        }

        QLMLPutDate(stdlink, date);
    } QLML_HANDLE_EXCEPTIONS
}

void qlDaysBetween(int y1, int m1, int d1,
                   int y2, int m2, int d2,
                   int daycountnum)
{
    try {
        int ret;

        Date date1((Day)d1, (Month)m1, (Year)y1);
        Date date2((Day)d2, (Month)m2, (Year)y2);

        DayCounter *daycount;
        daycount = createDayCounterfromnum(daycountnum);

        ret = daycount->dayCount(date1, date2);

        MLPutInteger(stdlink, ret);
    } QLML_HANDLE_EXCEPTIONS
}

void qlYearsBetween(int y1, int m1, int d1,
                   int y2, int m2, int d2,
                   int refy1, int refm1, int refd1,
                   int refy2, int refm2, int refd2,
                   int daycountnum)
{
    try {
        double ret;

        Date date1((Day)d1, (Month)m1, (Year)y1);
        Date date2((Day)d2, (Month)m2, (Year)y2);
        Date refdate1;
        Date refdate2;

        if (refy1!=0 || refy2!=0) {
            refdate1 = Date((Day)refd1, (Month)refm1, (Year)refy1);
            refdate2 = Date((Day)refd2, (Month)refm2, (Year)refy2);
        }

        DayCounter *daycount;
        daycount = createDayCounterfromnum(daycountnum);

        ret = daycount->yearFraction(date1, date2, refdate1, refdate2);

        MLPutReal(stdlink, ret);
    } QLML_HANDLE_EXCEPTIONS
}

void qlLeapYearQ(int year)
{
    try {
        Date date;

        bool ret = date.isLeap(year);

        QLMLPutBoolean(stdlink, ret);
    } QLML_HANDLE_EXCEPTIONS
}

