import datetime
from datetime import date, datetime

import numpy as np
import pandas as pd
# noinspection PyUnresolvedReferences
from cloudmesh.timeseries.transform import Transform
from cloudmesh.timeseries.date import Date
from cloudmesh.timeseries.city import City
from cloudmesh.timeseries.source import Source

from ipywidgets import *

import ipywidgets as widgets
import matplotlib.pyplot as plt


# noinspection PyPep8,PyPep8,PyPep8
class Magic:

    def fit_browser(self, dailycases):
        """
        dailycases: df
        """

        values = dailycases.columns.to_list()
        index = widgets.IntSlider(min=0, max=len(values) - 1)
        cities = City3()
        maximum = dailycases.max().max()

        ui = widgets.HBox([index])

        def f(index):
            fips = values[index]
            df = self.normalized_fit(dailycases, fips, "2020-01-22", "2020-04-15", fitpointer=1)
            display(df.plot(ylim=(0, maximum)));
            display(df.plot());
            print(maximum, index, fips, cities.data[fips]['City'], "Population:", cities.data[fips]['Population'])

        out = widgets.interactive_output(f, {'index': index})

        display(ui, out);

    def __init__(self, file="fits.txt", days=200):

        directory = Source.directory
        filename = f"{directory}/{file}"

        self.table = pd.read_csv(filename)
        self.table = self.table.astype({"FIPS": int})
        self.table = self.table.set_index(["FIPS"])
        self.days = days
        self.data = self.create()
        self.fips = self.data.columns.to_list()
        self.dates = [d.strftime("%Y-%m-%d") for d in self.data.index.to_list()]
        self.mdays = [d.strftime("%m-%d") for d in self.data.index.to_list()]

    def max(self, fips):
        df = self.data[fips]
        v = df[df == df.max()]
        return v.values[0], v.index[0].to_pydatetime()

    # noinspection PyPep8Naming
    @staticmethod
    def dailymagicfit(day, maxday=None, sigma=None, Nmax=1, fit=0):

        # day 0 is January 1 2020
        # maxday is day of maximum daily count when cumulative is Nmax/2
        if fit > 0:

            uscaseday = 97.17
            uscasesigma = 9.10
            usdeathday = 107.71
            usdeathsigma = 10.62

            if fit == 1:
                maxday = uscaseday
                sigma = uscasesigma
            if fit == 2:
                maxday = usdeathday
                sigma = usdeathsigma

        return Nmax * np.exp(-(day - maxday) * (day - maxday) / (2 * sigma * sigma)) / (np.sqrt(2 * np.pi) * sigma)

    def create(self):
        sample = {}

        for fips in self.table.index:
            Y = []
            for day in range(1, self.days):
                y = self.get(fips, day, fit=0)
                Y.append(y)
                sample[fips] = Y

        df = pd.DataFrame(sample)
        df['date'] = pd.date_range(start='2020-1-1', periods=len(df), freq='D')
        df = df.set_index('date')
        return df

    def get(self, fips, day, fit=0):
        # day 0 is January 1 2020
        # maxday is day of maximum daily count when cumulative is Nmax/2

        thdays, sigma, Nmax, score, location = self.table.loc[fips].to_list()

        maxday = thdays

        if fit > 0:

            uscaseday = 97.17
            uscasesigma = 9.10
            usdeathday = 107.71
            usdeathsigma = 10.62

            if fit == 1:
                maxday = uscaseday
                sigma = uscasesigma
            if fit == 2:
                maxday = usdeathday
                sigma = usdeathsigma

        return Nmax * np.exp(-(day - maxday) * (day - maxday) / (2 * sigma * sigma)) / (np.sqrt(2 * np.pi) * sigma)

    def normalized_fit(self, df, fips, start_date="2020-01-01", end_date="2050-01-01", fitpointer=1):
        """
        df = daily cases
        fips = fips number
        """
        data = pd.DataFrame(df[fips].copy())
        data['fit'] = 0.0

        t_b, t_e, data = Date.get_time_period(
            data,
            start_date=start_date,
            end_date=end_date,
            inplace=True)

        dayzero = datetime(2020, 1, 1)
        t_b = t_b.to_pydatetime()
        offset = (t_b - dayzero).days

        datanorm = data[fips].sum()
        days = data.index
        count = offset
        for day in days:
            data.at[day, 'fit'] = Magic.dailymagicfit(count, fit=fitpointer)
            count = count + 1
        fitnorm = data['fit'].sum()
        data['fit'] = data['fit'] * (datanorm / fitnorm)
        data['fit'] = data['fit'].round().astype(int)
        return data
