# -*- coding: utf-8 -*-
"""
Created on Fri Feb 12 16:51:05 2016

@author: Dominic O'Kane
"""

from math import log, exp
import numpy as np

from ...finutils.FinHelperFunctions import inputTime, inputFrequency
from ...finutils.FinError import FinError
from ...finutils.FinDayCount import FinDayCount
from ...finutils.FinMath import testMonotonicity
from .FinInterpolate import interpolate, FinInterpMethods

##########################################################################


class FinDiscountCurve():

##########################################################################

    def __init__(self, curveDate, times, values,
                 interpMethod=FinInterpMethods.FLAT_FORWARDS):

        # Validate curve
        if len(times) < 1:
            raise FinError("Times has zero length")

        if len(times) != len(values):
            raise FinError("Times and Values are not the same")

        if times[0] != 0.0:
            raise FinError("First time is not zero")

        if values[0] != 1.0:
            raise FinError("First value is not zero" + str(values[0]))

        if testMonotonicity(times) is False:
            raise FinError("Times are not sorted in increasing order")

        self._curveDate = curveDate
        self._times = np.array(times)
        self._values = np.array(values)
        self._interpMethod = interpMethod

##########################################################################

    def zeroRate(self, dt, compoundingFreq=-1):
        ''' Calculate the zero rate to maturity date. '''
        t = inputTime(dt, self)
        f = inputFrequency(compoundingFreq)
        df = self.df(t)

        if f == 0:  # Simple interest
            zeroRate = (1.0/df-1.0)/t
        if f == -1:  # Continuous
            zeroRate = -np.log(df) / t
        else:
            zeroRate = (df**(-1.0/t/f) - 1) * f
        return zeroRate

##########################################################################

    def df(self, dt):
        t = inputTime(dt, self)
        z = interpolate(t, self._times, self._values, self._interpMethod.value)
        return z

##########################################################################

    def survProb(self, dt):
        t = inputTime(dt, self)
        q = interpolate(t, self._times, self._values, self._interpMethod.value)
        return q

##########################################################################

    def fwd(self, dt):
        ''' Calculate the continuous forward rate at the forward date. '''
        t = inputTime(dt, self)
        dt = 0.000001
        df1 = self.df(t)
        df2 = self.df(t+dt)
        fwd = np.log(df1/df2)/dt
        return fwd

##########################################################################

    def bump(self, bumpSize):
        ''' Calculate the continuous forward rate at the forward date. '''

        times = self._times.copy()
        values = self._values.copy()

        n = len(self._times)
        for i in range(0, n):
            t = times[i]
            values[i] = values[i] * exp(-bumpSize*t)

        discCurve = FinDiscountCurve(self._curveDate, times, values,
                                     self._interpMethod)

        return discCurve

##########################################################################

    def fwdRate(self, date1, date2, dayCountType):
        ''' Calculate the forward rate according to the specified
        day count convention. '''

        if date1 < self._curveDate:
            raise ValueError("Date1 before curve value date.")

        if date2 < date1:
            raise ValueError("Date2 must not be before Date1")

        dayCount = FinDayCount(dayCountType)
        yearFrac = dayCount.yearFrac(date1, date2)
        df1 = self.df(date1)
        df2 = self.df(date2)
        fwd = (df1 / df2 - 1.0) / yearFrac
        return fwd

##########################################################################

    def print(self):
        numPoints = len(self._times)
        print("TIMES,DISCOUNT FACTORS")
        for i in range(0, numPoints):
            print("%10.7f,%10.7f" % (self._times[i], self._values[i]))

#######################################################################
