import datetime
import time
import calendar
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from datetime import datetime, timedelta
from django import forms
from leave.models import LeavePlan, LeaveHoliday, LeaveEmployee, LeaveType, EmployeeInstance
from django.forms.widgets import HiddenInput
from django.contrib.auth.models import User
from decimal import Decimal
from django.db.models import Sum
from .rules import *
from django.utils.dateparse import parse_datetime
from django import forms
from django.template.defaultfilters import filesizeformat
from django.forms import Textarea

current_year = datetime.now().year
standard_start_working_hour = 0
standard_stop_working_hour = 23

class EmployeeM1247Form(forms.ModelForm):
    start_working_hour = 0
    stop_working_hour = 59
    hour_range = ((0,'00'),(1,'01'),(2,'02'),(3,'03'),(4,'04'),(5,'05'),(6,'06'),(7,'07'),(8,'08'),(9,'09'),(10,'10'),(11,'11'),(12,'12'),(13,'13'),(14,'14'),(15,'15'),(16,'16'),(17,'17'),(18,'18'),(19,'19'),(20,'20'),(21,'21'),(22,'22'),(23,'23'))
    minute_range = ((0,'00'),(30,'30'),(59,'59'))

    leave_type = forms.ModelChoiceField(label=_('Select Leave Type'), queryset=None, required=True)

    start_date = forms.DateField(label=_('Start'), required=True, error_messages={'required': 'กรุณาป้อนข้อมูลวันที่ลา'})
    start_hour = forms.IntegerField(widget=forms.Select(choices=hour_range))
    start_minute = forms.IntegerField(widget=forms.Select(choices=minute_range))

    end_date = forms.DateField(label=_('End'), required=True, error_messages={'required': 'กรุณาป้อนข้อมูลลาถึงวันที่'})
    end_hour = forms.IntegerField(widget=forms.Select(choices=hour_range))
    end_minute = forms.IntegerField(widget=forms.Select(choices=minute_range))

    employee_type = forms.CharField(required=False, widget=forms.HiddenInput(), initial="M1247")
    document = forms.FileField(required=False)
    leave_reason = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2, 'cols': 20}), max_length=200)

    search_emp_id = forms.CharField(widget=forms.HiddenInput())

    class Meta:
        model = EmployeeInstance
        fields = ['start_date', 'end_date', 'leave_type', 'lve_act', 'lve_act_hr', 'document', 'leave_reason']

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user')
        super(EmployeeM1247Form, self).__init__(*args, **kwargs)

        default_end_date =  datetime.now()+timedelta(days=1)
        self.fields['leave_type'].widget.attrs={'class': 'form-control'}
        self.fields['leave_type'].queryset=LeaveType.objects.filter(leaveplan__emp_id=self.user.username, leaveplan__lve_year=current_year)

        self.fields['start_date'].widget.attrs={'class': 'form-control datepicker border-bottom-1 border-left-0 rounded-0 bg-white'}
        self.initial['start_date'] = datetime.now().strftime("%Y-%m-%d")
        self.fields['start_date'].widget.attrs['placeholder'] = "YYYY-MM-DD"
        self.fields['start_hour'].widget.attrs={'class': 'form-control border-top-0 border-left-1 rounded-0 bg-white'}
        self.initial['start_hour'] = 8
        self.fields['start_minute'].widget.attrs={'class': 'form-control border-top-0 rounded-0 bg-white'}
        self.initial['start_minute'] = 0

        self.fields['end_date'].widget.attrs={'class': 'form-control datepicker border-bottom-1 border-left-0 rounded-0 bg-white'}
        self.initial['end_date'] = datetime.now().strftime("%Y-%m-%d") #default_end_date.strftime("%Y-%m-%d")
        self.fields['end_date'].widget.attrs['placeholder'] = "YYYY-MM-DD"
        self.fields['end_hour'].widget.attrs={'class': 'form-control border-top-0 border-left-1 rounded-0 bg-white'}
        self.initial['end_hour'] = 17
        self.fields['end_minute'].widget.attrs={'class': 'form-control border-top-0 rounded-0 bg-white'}
        self.initial['end_minute'] = 0

        self.fields['leave_reason'].widget.attrs={'class': 'form-control rounded-0'}

        self.initial['search_emp_id'] = ""

    def clean(self):
        datetime_format = "%Y-%m-%d %H:%M:%S"
        cleaned_data = super(EmployeeM1247Form, self).clean()

        leave_type = self.cleaned_data.get('leave_type')
        leave_type_id = self.data.get('leave_type')

        start_date = self.cleaned_data.get('start_date')
        start_hour = self.cleaned_data.get('start_hour')
        start_minute = self.cleaned_data.get('start_minute')

        end_date = self.cleaned_data.get('end_date')
        end_hour = self.cleaned_data.get('end_hour')
        end_minute = self.cleaned_data.get('end_minute')

        document = self.cleaned_data.get('document')
        if document is not None:
            document_type = document.content_type.split('/')[1]
            print("type:",  document_type)
            if document_type in settings.CONTENT_TYPES:
                if document.size > settings.MAX_UPLOAD_SIZE:
                    raise forms.ValidationError(_('ไฟล์แนบมีขนาดเกิน 5 เมกะไบท์'))
            else:
                raise forms.ValidationError(_('ระบบสามารถแนบไฟล์ (png, jpg, jpeg, pdf) ได้เท่านั้น'))

        # raise forms.ValidationError(_('Test'))

        search_emp_id = self.cleaned_data.get('search_emp_id')
        print("search_emp_id = ", search_emp_id)
        # username = self.user.username
        # username = search_emp_id
        employee_type = 'M1'

        d1 = str(start_date) + ' ' + str(start_hour) + ':' + str(start_minute) + ':00'
        d2 = str(end_date) + ' ' + str(end_hour) + ':' + str(end_minute) + ':00'
        start_date = datetime.strptime(d1, datetime_format)
        end_date = datetime.strptime(d2, datetime_format)

        if start_date != None:
            # ------------------------------------------------
            # Check master remaining hour (Leave_Plan table)
            # ------------------------------------------------
            leave_plan = LeavePlan.objects.filter(emp_id__exact=search_emp_id).filter(lve_id__exact=leave_type_id).filter(lve_year=current_year).values_list('lve_plan', flat=True).get()
            print("Plan111 : " + str(leave_plan))
            total_leave_quota_remaining_day = LeavePlan.objects.filter(emp_id__exact=search_emp_id).filter(lve_id__exact=leave_type_id).filter(lve_year=current_year).values_list('lve_miss', flat=True).get()
            print("remain day : " + str(total_leave_quota_remaining_day))
            total_leave_quota_remaining_hour = LeavePlan.objects.filter(emp_id__exact=search_emp_id).filter(lve_id__exact=leave_type_id).filter(lve_year=current_year).values_list('lve_miss_hr', flat=True).get()
            print("remain hour : " + str(total_leave_quota_remaining_hour))
            grand_total_leave_quota_remaining_hour = (total_leave_quota_remaining_hour + (total_leave_quota_remaining_day * 8))
            # 26d + 0h
            # 26 * 8 = 208
            print("total remain hour : " + str(grand_total_leave_quota_remaining_hour))

            # ------------------------------------------------
            # Check transaction remaing hour (leave_employeeinstance table) (filter status = p, a, F)
            # ------------------------------------------------
            total_pending_approve_syncfail_status_history_day = EmployeeInstance.objects.filter(emp_id__exact=search_emp_id).filter(leave_type_id__exact=leave_type_id).filter(status__in=('p','a')).aggregate(sum=Sum('lve_act'))['sum'] or 0
            print("sync day : " + str(total_pending_approve_syncfail_status_history_day))
            total_pending_approve_syncfail_status_history_hour = EmployeeInstance.objects.filter(emp_id__exact=search_emp_id).filter(leave_type_id__exact=leave_type_id).filter(status__in=('p','a')).aggregate(sum=Sum('lve_act_hr'))['sum'] or 0
            print("sync hour : " + str(total_pending_approve_syncfail_status_history_hour))
            grand_total_pending_approve_syncfail_status_history_hour = total_pending_approve_syncfail_status_history_hour + (total_pending_approve_syncfail_status_history_day * 8)
            # 25d + 16h
            # (25 * 8) + 16 = 216
            print("total sync hour : " + str(grand_total_pending_approve_syncfail_status_history_hour))

            grand_total_leave_quota_remaining_hour = grand_total_leave_quota_remaining_hour - grand_total_pending_approve_syncfail_status_history_hour
            print("grand : " + str(grand_total_leave_quota_remaining_hour))

            # ------------------------------------------------
            # Check Standard Business Rules
            # ------------------------------------------------
            found_standard_error = checkM1247StandardBusinessRules(start_date, end_date, leave_type_id)
            if found_standard_error[0]:
                raise forms.ValidationError(_(found_standard_error[1]))

            # ------------------------------------------------
            # Check M1247 Business Rules
            # ------------------------------------------------
            found_m1247_error = checkM1247BusinessRules('M1247', start_date, end_date, leave_type_id)
            if found_m1247_error[0]:
                raise forms.ValidationError(found_m1247_error[1])
            else:
                total_leave_request_hour = found_m1247_error[1]

            #raise forms.ValidationError(total_leave_request_hour)
            print(str(total_leave_request_hour) + " : total_leave_request_hour")
            print(str(grand_total_leave_quota_remaining_hour) + " : 1grand_total_leave_quota_remaining_hour")

            # RULE 1: Check not over leave quota
            # print("rule1", total_leave_request_hour, grand_total_leave_quota_remaining_hour, "TEST")

            if (total_leave_request_hour) > grand_total_leave_quota_remaining_hour:
                raise forms.ValidationError(_("เลือกวันเกินจำนวนสิทธิ์ที่กำหนด"))
            else:
                if grand_total_leave_quota_remaining_hour <= 0:
                    raise forms.ValidationError(_("ใช้วัน" + str(leave_type) + "หมดแล้ว"))

            # RULE 2: Check duplicate leave
            #select id from leave_employeeinstance where not (start_date > @end_date OR end_date < @start_date)
            sql = "select id from leave_employeeinstance where not (start_date > '" + str(end_date.strftime("%Y-%m-%d %H:00") + "' or end_date < '" + str(start_date.strftime("%Y-%m-%d %H:01")) + "')") + " and emp_id=" + search_emp_id + " and status in ('a','p','C','F')"
            print(sql)
            print(search_emp_id, start_date, end_date)
            queryset = EmployeeInstance.objects.raw("select id from leave_employeeinstance where not (start_date > '" + str(end_date.strftime("%Y-%m-%d %H:00") + "' or end_date < '" + str(start_date.strftime("%Y-%m-%d %H:01")) + "')") + " and emp_id=" + search_emp_id + " and status in ('a','p','C','F')")
            if len(queryset) > 0:
                raise forms.ValidationError(_("วันที่ " + str(start_date) + " - " + str(end_date) + " มีการลาไปแล้ว"))

            # RULE 3: Check public holidays
            if checkLeaveTypeIncludePublicHoliday(leave_type_id):
                queryset = LeaveHoliday.objects.filter(hol_date__range=(start_date.strftime("%Y-%m-%d"), end_date.strftime("%Y-%m-%d"))).values_list('pub_th', flat=True)
                holiday_list = str(list(queryset)).replace("'", '')
                if len(queryset) > 0:
                    #raise forms.ValidationError({'start_date': "เลือกวันลาตรงกับวันหยุด - " + str(holiday_list)})
                    raise forms.ValidationError(_("ช่วงวันลาตรงกับวันหยุดนักขัตฤกษ์ - " + str(holiday_list)))

            # RULE 4: Check not allow over month
            if(checkLeaveRequestOverMonth("M1", start_date, end_date)):
                #raise forms.ValidationError({'start_date': "เลือกวันลาข้ามเดือน"})
                raise forms.ValidationError(_("ไม่สามารถเลือกวันลาข้ามเดือน กรุณาแบ่งทำ 2 รายการแยกเดือนกัน"))

        return cleaned_data


# script concerned with evaluating the general accuracy, effects of geometry, pixelsize and fem grid selection.

from pyTFM.utilities_TFM import round_flexible, gaussian_with_nans, make_display_mask, createFolder
from pyTFM.plotting import *
from pyTFM.graph_theory_for_cell_boundaries import mask_to_graph, find_path_circular
import sys
from collections import defaultdict
from skimage.morphology import binary_erosion
from itertools import chain, product
import os

sys.path.insert(0, '/home/user/Software/pyTFM/analysis_and_testing/')
from simulating_deformation import *
from playing_with_strains import *
from evaluation_functions import cut_arrays

def display_mask(fig, mask, display_type, type=1, color="C1", d=np.sqrt(2), ax=None, dm=True, lw=9):
    if not dm:
        return
    mask = mask.astype(int)
    if display_type == "outline":
        out_line = mask - binary_erosion((mask))
        out_line = custom_edge_filter(out_line)  # risky
        out_line_graph, points = mask_to_graph(out_line, d=d)
        circular_path = find_path_circular(out_line_graph, 0)
        circular_path.append(circular_path[0])  # to plot a fully closed loop
        if type == 1:
            ax = fig.axes[0] if ax is None else ax
            ax.plot(points[circular_path][:, 1], points[circular_path][:, 0], "--", color=color, linewidth=lw)
        if type == 2:
            for ax in fig.axes:
                ax.plot(points[circular_path][:, 1], points[circular_path][:, 0], "--", color=color, linewidth=lw)

    if display_type == "overlay":

        mask_show = make_display_mask(mask)
        if type == 1:
            ax = fig.axes[0] if ax is None else ax
            ax.imshow(mask_show, alpha=0.4)
        if type == 2:
            for ax in fig.axes:
                ax.imshow(mask_show, alpha=0.4)

    if display_type == "windowed":

        mask_show = make_display_mask(mask)
        mask_window = copy.deepcopy(mask_show)
        mask_window[np.isnan(mask_show)] = 1
        mask_window[mask_show] = np.nan
        if type == 1:
            ax = fig.axes[0] if ax is None else ax
            ax.imshow(mask_window, alpha=0.4)
        if type == 2:
            for ax in fig.axes:
                ax.imshow(mask_window, alpha=0.4)

def bar_plot_ax(ax, values, labels, at=False, types=None, vmax=None):
    pos = list(chain.from_iterable([[p - 0.2, p + 0.2] for p in range(int(len(values) / 2))]))
    # figsize = (1.4 * len(pos), 4.8)

    # fig = plt.figure(figsize=figsize)

    # plt.bar(pos[::2],values_r[::2],width=0.4, color="#729fcf",label="backwards",alpha=0.83)
    # plt.bar(pos[1::2], values_r[1::2], width=0.4, color="#cc0066",label="forwards",alpha=0.83)
    ax.bar(pos[::2], values[::2], width=0.4, color="C1", label="backwards", alpha=1)
    ax.bar(pos[1::2], values[1::2], width=0.4, color="C2", label="forwards", alpha=1)
    for px, py in zip(pos, values):
        if py < np.inf:
            if py < 10 ** -15:
                t = "0"
            else:
                if np.abs(np.log(py)) > 3:
                    t = "%.1e" % py
                else:
                    t = "%.2f" % py
            ax.text(px, py, t, color="black", fontsize=15, horizontalalignment="center", verticalalignment="bottom")

    if not isinstance(vmax, (int, float)):
        vmax = np.max(values)
    ax.set_ylim((0, vmax * 1.1))
    # plt.xticks(pos,lables,rotation="70",fontsize=15)
    ax.set_xticks(list(range(int(len(labels) / 2))))
    ax.tick_params(axis="both", which="both", color="black", length=4, width=2, labelsize=20, labelcolor="black",
                   labelbottom=False)
    set_axis_attribute(ax, "set_color", "black")
    set_axis_attribute(ax, "set_linewidth", 2)
    if at and isinstance(types, (dict, defaultdict)):
        if "m" in types.keys():
            plt.title(types["m"])

    # ax.tight_layout()
    return ax

def bar_plots_pylu(key_values):
    values1 = [key_values["contractile_force_b"] / key_values["contractile_force_b"],
               key_values["contractile_force_f"] / key_values["contractile_force_b"]]
    labels1 = ["contractility", "contractility"]
    values2 = [key_values['mean_normal_stress_b'], key_values['mean_normal_stress_f'], key_values["max_shear_b"],
               key_values["max_shear_f"]]
    labels2 = ["mean normal stress", "mean normal stress", "mean shear stress", "mean shear stress"]
    values3 = [key_values["cv_b"], key_values["cv_f"]]
    lables3 = ["cv", "cv"]

    # import pylustrator
    # pylustrator.start()

    fig, axs = plt.subplots(1, 3)
    bar_plot_ax(axs[0], values1, labels1, at=False, types=None, vmax=1)
    bar_plot_ax(axs[1], values2, labels2, at=False, types=None, vmax=1)
    bar_plot_ax(axs[2], values3, lables3, at=False, types=None, vmax=1)

    fig.ax_dict = {ax.get_label(): ax for ax in fig.axes}
    fig.set_size_inches(24.580000 / 2.54, 12.050000 / 2.54, forward=True)
    fig.axes[0].set_position([0.086072, 0.193550, 0.138920, 0.701868])
    fig.axes[0].texts[0].set_fontsize(14)
    fig.axes[0].texts[1].set_fontsize(14)
    fig.axes[1].set_position([0.389371, 0.193550, 0.301451, 0.770000])
    fig.axes[1].texts[0].set_fontsize(14)
    fig.axes[1].texts[0].set_rotation(0.0)
    fig.axes[1].texts[1].set_fontsize(14)
    fig.axes[1].texts[2].set_fontsize(14)
    fig.axes[1].texts[2].set_position([0.800000, 0.021978])
    fig.axes[1].texts[2].set_rotation(0.0)
    fig.axes[1].texts[3].set_fontsize(14)
    fig.axes[1].texts[3].set_position([1.193033, 0.021978])
    fig.axes[1].texts[3].set_rotation(0.0)
    fig.axes[1].texts[3].set_text("2.7e-8")
    fig.axes[1].get_yaxis().get_major_ticks()[0].label1.set_fontsize(15)
    fig.axes[1].get_yaxis().get_major_ticks()[1].label1.set_fontsize(15)
    fig.axes[1].get_yaxis().get_major_ticks()[2].label1.set_fontsize(15)
    fig.axes[1].get_yaxis().get_major_ticks()[3].label1.set_fontsize(15)
    fig.axes[1].get_yaxis().get_major_ticks()[4].label1.set_fontsize(15)
    fig.axes[1].get_yaxis().get_major_ticks()[5].label1.set_fontsize(15)
    fig.axes[1].get_yaxis().get_major_ticks()[6].label1.set_fontsize(15)
    fig.axes[2].set_position([0.830096, 0.193550, 0.152689, 0.770000])
    fig.axes[2].texts[0].set_fontsize(14)
    fig.axes[2].texts[0].set_position([-0.223257, 0.021978])
    fig.axes[2].texts[1].set_fontsize(14)
    fig.axes[2].get_yaxis().get_major_ticks()[0].label1.set_fontsize(15)
    fig.axes[2].get_yaxis().get_major_ticks()[1].label1.set_fontsize(15)
    fig.axes[2].get_yaxis().get_major_ticks()[2].label1.set_fontsize(15)
    fig.axes[2].get_yaxis().get_major_ticks()[3].label1.set_fontsize(15)
    fig.axes[2].get_yaxis().get_major_ticks()[4].label1.set_fontsize(15)
    fig.axes[2].get_yaxis().get_major_ticks()[5].label1.set_fontsize(15)
    fig.axes[2].get_yaxis().get_major_ticks()[6].label1.set_fontsize(15)

    fig.ax_dict = {ax.get_label(): ax for ax in fig.axes}

    fig.axes[1].texts[2].set_position([0.772830, 0.021978])
    fig.axes[1].texts[3].set_text(str(np.round(values2[3],3)))
    # % start: automatic generated code from pylustrator
    fig.ax_dict = {ax.get_label(): ax for ax in fig.axes}
    # import matplotlib as mpl
    fig.axes[0].set_position([0.086072, 0.193550, 0.138920, 0.770000])
    fig.axes[0].get_yaxis().get_major_ticks()[0].label1.set_fontsize(15)
    fig.axes[0].get_yaxis().get_major_ticks()[1].label1.set_fontsize(15)
    fig.axes[0].get_yaxis().get_major_ticks()[2].label1.set_fontsize(15)
    fig.axes[0].get_yaxis().get_major_ticks()[3].label1.set_fontsize(15)
    fig.axes[0].get_yaxis().get_major_ticks()[4].label1.set_fontsize(15)
    fig.axes[0].get_yaxis().get_major_ticks()[5].label1.set_fontsize(15)
    fig.axes[1].set_position([0.386269, 0.193550, 0.301451, 0.770000])
    # % end: automatic generated code from pylustrator
    # plt.show()

    return fig

def display_stress_tensor(stress_tensor, mask=None, title_str=""):
    fig, axs = plt.subplots(1, 3)
    plt.suptitle(title_str)
    im = axs[0].imshow(stress_tensor[:, :, 0, 0])
    axs[0].set_title("sig_xx", x=0.5, y=0.9, transform=axs[0].transAxes, color="white")
    # plt.colorbar(im)
    im = axs[1].imshow(stress_tensor[:, :, 1, 1])
    axs[1].set_title("sig_yy", x=0.5, y=0.9, transform=axs[1].transAxes, color="white")
    im = axs[2].imshow(stress_tensor[:, :, 1, 0])
    axs[2].set_title("sig_xy", x=0.5, y=0.9, transform=axs[2].transAxes, color="white")

    return fig

def set_axis_attribute(ax, attribute, value):
    for p in ["left", "bottom", "right", "top"]:
        if hasattr(ax.spines[p], attribute):
            try:
                getattr(ax.spines[p], attribute)(value)
            except:
                setattr(ax.spines[p], attribute, value)
        else:
            raise AttributeError("Spines object has no attribute " + attribute)

def get_axis_attribute(ax, attribute):
    l = []
    for p in ["left", "bottom", "right", "top"]:
        l.append(getattr(ax.spines[p], attribute))
    return l

def draw_outline(ax, do=True, lw=2, color="black"):
    if do:
        ax.set_axis_on()
        ax.tick_params(axis="both", which="both", bottom=False, left=False, labelbottom=False,
                       labelleft=False)
        set_axis_attribute(ax, "set_visible", True)
        set_axis_attribute(ax, "set_linewidth", lw)
        set_axis_attribute(ax, "set_color", color)

def name_add(add="", cb=False, dm=False):
    if dm:
        add = add + "ma"
    if cb:
        add = add + "cb"
    return add

def draw_cbar_only(vmin, vmax, aspect=8, shrink=1, cbar_axes_fraction=1.2, cmap="coolwarm", tick_length=4, tick_width=2,
                   labelsize=20, lablecolor="black"):
    # cbar for stress
    fig = plt.figure(figsize=(3.2, 4.75))
    plt.gca().set_axis_off()
    cbar = add_colorbar(vmin=vmin, vmax=vmax, aspect=aspect, shrink=shrink, cbar_axes_fraction=cbar_axes_fraction,
                        cmap=cmap,
                        cbar_style="not-clickpoints")
    set_axis_attribute(cbar.ax, "set_color", "black")
    cbar.ax.tick_params(axis="both", which="both", color="black", length=tick_length, width=tick_width,
                        labelsize=labelsize, labelcolor=lablecolor)
    return fig

def show_forces_forward(fx_f=None, fy_f=None, figsize=None, arrow_scale=None, arrow_width=None
                        , headlength=None, headwidth=None, headaxislength=None, cmap=None, max_dict=None, cb=None,
                        do=None, at=None, dm=None, types=None, mask=None, display_type=None,
                        mask_fm=None, mask_fm_color=None, out_folder=None, ext=None, add_name="", show_fm=False,
                        **kwargs):
    fig, ax = show_quiver(fx_f, fy_f, figsize=figsize, scale_ratio=arrow_scale - 0.03, filter=[0, 10],
                          width=arrow_width,
                          headlength=headlength, headwidth=headwidth,
                          headaxislength=headaxislength, cbar_tick_label_size=30, cmap=cmap,
                          cbar_style="not-clickpoints",
                          vmin=0, vmax=max_dict["force"], plot_cbar=cb)
    draw_outline(ax, do=do)
    add_title(fig, types["f_f"], at=at)
    display_mask(fig, cut_arrays(fx_f.shape, mask, mode="match"), display_type=display_type, dm=dm)
    if isinstance(mask_fm, np.ndarray) and show_fm:
        display_mask(fig, cut_arrays(fx_f.shape, mask_fm, mode="match"), display_type=display_type, color=mask_fm_color,
                     d=np.sqrt(2), dm=dm)

    plt.tight_layout()
    fig.savefig(os.path.join(out_folder, types["f_f"] + name_add(add=add_name, cb=cb, dm=dm) + ext))
    return fig, ax

def general_display(plot_types=[], pixelsize=1, display_type="outline", f_type="not circular", cmap="coolwarm"
                    , max_dict=defaultdict(lambda: None),
                    mean_normal_list=None, mask_exp_list=None, out_folder="", fields={},
                    border_ex_test=None, be_avm_list=None, scalar_comaprisons=None,
                    key_values=None, plot_gt_exp=True, dm=True, at=True, cb=True, do=True):
    '''
    plot_types=["deformation_forward","deformation_backwards","mask","forces_forward","forces_forward","shear_forward","mean_normal_stress_forward",
    "shear_backward","mean_normal_stress_backward","full_stress_tensor_forward","full_stress_tensor_backward"]

    :param plot_types:
    :param pixelsize:
    :return:
    '''
    u_b, v_b, fx_b, fy_b, fx_f, fy_f, stress_tensor_f, stress_tensor_b, mask_fm, mask_fem, mask = \
        [fields[x] for x in
         ["u_b", "v_b", "fx_b", "fy_b", "fx_f", "fy_f", "stress_tensor_f", "stress_tensor_b", "mask_fm", "mask_fem",
          "mask"]]
    figs = {}
    createFolder(out_folder)
    types = {"def_f": "deformation_forward", "def_b": "deformation_backwards", "mask": "mask",
             "f_f": "forces_forward", "f_b": "forces_backward", "st_f": "full_stress_tensor_forward",
             "st_b": "full_stress_tensor_backward", "sh_f": "shear_forward", "sh_b": "shear_backward",
             "norm_f": "mean_normal_stress_forward", "norm_b": "mean_normal_stress_backward",
             "m": "key measures", "r": "correlation", "exp_test1": "be1", "exp_test2": "be2",
             "exp_test3": "be3", "exp_test4": "be4", "exp_test5": "be5",
             "mask_outline": "mask_outline", "cbars": "cbars_only"}

    figsize = (7, 7)
    arrow_scale = 0.13  # 0.1
    arrow_width = 0.004
    headlength = 4
    headwidth = 4  # 6
    headaxislength = 3
    ext = ".svg"
    mask_fm_color = "C2"
    mask_fem_color = "#666666"

    ps_new_ex = 0.845  # pixelsize when using windowsize 20, overlapp 19.25
    vmax_x_ep = 65 * 0.845
    pd = {"figsize": figsize,
          "arrow_scale": arrow_scale,
          "arrow_width": arrow_width,
          "headlength": headlength,
          "headwidth": headwidth,
          "headaxislength": headaxislength,
          "ext": ext,
          "mask_fm_color": mask_fm_color,
          "mask_fem_color": mask_fem_color}
    paras = {**locals(), **pd}

    if isinstance(mask_fem, np.ndarray):
        mask_fem = mask_fem.astype(bool)

    if types["def_b"] in plot_types or plot_types == "all":
        fig, ax = show_quiver(u_b, v_b, scale_ratio=arrow_scale, filter=[0, 5], width=arrow_width,
                              headlength=headlength, headwidth=headwidth,
                              headaxislength=2, cbar_tick_label_size=30,
                              cmap=cmap, cbar_style="not-clickpoints", vmin=0, vmax=max_dict["def"], plot_cbar=cb)
        draw_outline(ax, do=do)
        add_title(fig, types["def_b"], at=at)
        display_mask(fig, mask, display_type=display_type, dm=dm)
        display_mask(fig, mask_fm, display_type=display_type, color=mask_fm_color, d=np.sqrt(2), dm=dm)
        fig.savefig(os.path.join(out_folder, types["def_b"] + name_add(cb=cb, dm=dm) + ext))

    if types["cbars"] in plot_types or plot_types == "all":
        # cbar for stress
        fig = plt.figure(figsize=(3.2, 4.75))
        plt.gca().set_axis_off()
        cbar = add_colorbar(vmin=0, vmax=1 + 0.01, aspect=8, shrink=1, cbar_axes_fraction=1.2, cmap=cmap,
                            cbar_style="not-clickpoints")
        set_axis_attribute(cbar.ax, "set_color", "black")
        cbar.ax.tick_params(axis="both", which="both", color="black", length=4, width=2, labelsize=20,
                            labelcolor="black")
        fig.savefig(os.path.join(out_folder, types["cbars"] + "force" + name_add(cb=cb, dm=dm) + ext))

        # cbar for stress
        fig = plt.figure(figsize=(3.2, 4.75))
        plt.gca().set_axis_off()
        cbar = add_colorbar(vmin=0, vmax=1 + 0.01, aspect=8, shrink=1, cbar_axes_fraction=1.2, cmap=cmap,
                            cbar_style="not-clickpoints")
        set_axis_attribute(cbar.ax, "set_color", "black")
        cbar.ax.tick_params(axis="both", which="both", color="black", length=4, width=2, labelsize=20,
                            labelcolor="black")
        fig.savefig(os.path.join(out_folder, types["cbars"] + "stress" + name_add(cb=cb, dm=dm) + ext))

    if types["mask"] in plot_types or plot_types == "all":
        fig = plt.figure()
        plt.imshow(mask)
        add_title(fig, types["mask"], at=at)
        fig.savefig(os.path.join(out_folder, types["mask"] + ext))

    if types["mask_outline"] in plot_types or plot_types == "all":
        fig = plt.figure()
        plt.imshow(np.zeros(fx_f.shape), cmap=cmap, vmin=0, vmax=1)
        draw_outline(plt.gca(), do=do)
        display_mask(fig, mask, display_type="outline", color="C1", dm=True)
        display_mask(fig, mask_fm, display_type="outline", color=mask_fm_color, d=np.sqrt(2), dm=True)
        display_mask(fig, mask_fem, display_type="outline", color=mask_fem_color, d=np.sqrt(2), dm=True)

    if types["f_f"] in plot_types or plot_types == "all":
        show_forces_forward(**paras)

    if types["f_b"] in plot_types or plot_types == "all":

        if f_type == "circular":
            fx_filtered, fy_filtered = filter_arrows_for_square(fx_b, fy_b,
                                                                filter=12)  # only works when initial forces are circular
            fig, ax = show_quiver(fx_filtered, fy_filtered, figsize=figsize, scale_ratio=arrow_scale, filter=[0, 0],
                                  width=arrow_width,
                                  headlength=headlength, headwidth=headwidth,
                                  headaxislength=headaxislength, cbar_tick_label_size=30, cmap=cmap,
                                  cbar_style="not-clickpoints",
                                  vmin=0, vmax=max_dict["force"], plot_cbar=cb)
            im = fig.axes[0].imshow(np.sqrt(fx_b ** 2 + fy_b ** 2), cmap=cmap,
                                    vmin=0, vmax=max_dict["force"])
            if cb:
                fig.axes[1].remove()
                cb = plt.colorbar(im)
                cb.ax.tick_params(labelsize=30)

            plt.tight_layout()
        else:
            fx_filtered, fy_filtered = fx_b, fy_b
            fig, ax = show_quiver(fx_filtered, fy_filtered, figsize=figsize, scale_ratio=arrow_scale - 0.03,
                                  filter=[0, 10],
                                  width=arrow_width,
                                  headlength=headlength, headwidth=headwidth,
                                  headaxislength=headaxislength, cbar_tick_label_size=30, cmap=cmap,
                                  cbar_style="not-clickpoints",
                                  vmin=0, vmax=max_dict["force"], plot_cbar=cb)
        draw_outline(ax, do=do)
        add_title(fig, types["f_b"], at=at)
        display_mask(fig, mask, display_type=display_type, dm=dm)
        # display_mask(fig, mask_fm, display_type=display_type, color=mask_fm_color,d=np.sqrt(2), dm=dm)
        plt.tight_layout()
        fig.savefig(os.path.join(out_folder, types["f_b"] + name_add(cb=cb, dm=dm) + ext))

    if types["sh_f"] in plot_types or plot_types == "all":
        shear = stress_tensor_f[:, :, 0, 1]  # shear component of the stress tensor
        fig, ax = show_map_clickpoints(shear, show_mask=mask_fem, figsize=figsize, cbar_style="out", cmap=cmap,
                                       cbar_tick_label_size=30, vmin=0, vmax=max_dict["stress"], plot_cbar=cb)
        draw_outline(ax, do=do)
        add_title(fig, types["sh_f"], at=at)
        display_mask(fig, mask, display_type=display_type, dm=dm)
        plt.tight_layout()
        fig.savefig(os.path.join(out_folder, types["sh_f"] + name_add(cb=cb, dm=dm) + ext))
    if types["norm_f"] in plot_types or plot_types == "all":
        mean_normal_stress = ((stress_tensor_f[:, :, 0, 0] + stress_tensor_f[:, :, 1, 1]) / 2)
        mean_normal_stress[~mask_fem] = np.nan
        fig, ax = show_map_clickpoints(mean_normal_stress, show_mask=mask_fem, figsize=figsize, cbar_style="out",
                                       background_color="white",
                                       cmap=cmap,
                                       cbar_tick_label_size=30, vmin=0, vmax=max_dict["stress"], plot_cbar=cb)
        draw_outline(ax, do=do)
        add_title(fig, types["norm_f"], at=at)
        display_mask(fig, mask, display_type=display_type, dm=dm)
        plt.tight_layout()
        # display_mask(fig, mask_fem, display_type=display_type, color=mask_fem_color, d=np.sqrt(2), dm=dm)
        fig.savefig(os.path.join(out_folder, types["norm_f"] + name_add(cb=cb, dm=dm) + ext))

    if types["sh_b"] in plot_types or plot_types == "all":
        shear = stress_tensor_b[:, :, 0, 1]  # shear component of the stress tensor
        fig, ax = show_map_clickpoints(shear, show_mask=mask_fem, figsize=figsize, cbar_style="out", cmap=cmap,
                                       cbar_tick_label_size=30, vmin=0, vmax=max_dict["stress"], plot_cbar=cb)
        display_mask(fig, mask, display_type=display_type, dm=dm)
        draw_outline(ax, do=do)
        add_title(fig, types["sh_b"], at=at)
        plt.tight_layout()
        fig.savefig(os.path.join(out_folder, types["sh_b"] + name_add(cb=cb, dm=dm) + ext))

    if types["norm_b"] in plot_types or plot_types == "all":
        mean_normal_stress = ((stress_tensor_b[:, :, 0, 0] + stress_tensor_b[:, :, 1, 1]) / 2)
        mean_normal_stress[~mask_fem] = np.nan
        fig, ax = show_map_clickpoints(mean_normal_stress, show_mask=np.ones(mean_normal_stress.shape), figsize=figsize,
                                       cbar_style="out", background_color="white",
                                       cmap=cmap, cbar_tick_label_size=30, vmin=0, vmax=max_dict["stress"],
                                       plot_cbar=cb)
        draw_outline(ax, do=do)
        add_title(fig, types["norm_b"], at=at)
        display_mask(fig, mask, display_type=display_type, dm=dm)
        # display_mask(fig, mask_fem, display_type=display_type, color=mask_fem_color, d=np.sqrt(2), dm=dm)
        plt.tight_layout()
        fig.savefig(os.path.join(out_folder, types["norm_b"] + name_add(cb=cb, dm=dm) + ext))

    if types["st_f"] in plot_types or plot_types == "all":
        fig = display_stress_tensor(stress_tensor_f, mask, title_str=types["st_f"])
        display_mask(fig, mask, display_type=display_type, type=2, dm=dm)
        plt.tight_layout()
        fig.savefig(os.path.join(out_folder, types["st_f"] + name_add(cb=cb, dm=dm) + ext))

    if types["st_b"] in plot_types or plot_types == "all":
        fig = display_stress_tensor(stress_tensor_b, mask, title_str=types["st_b"])
        display_mask(fig, mask, display_type=display_type, type=2, dm=dm)
        plt.tight_layout()
        fig.savefig(os.path.join(out_folder, types["st_b"] + name_add(cb=cb, dm=dm) + ext))

    if types["m"] in plot_types or plot_types == "all":
        fig = bar_plots_pylu(key_values)
        fig.savefig(os.path.join(out_folder, types["m"] + ext))
        # values1 = [key_values["contractile_force_b"],key_values["contractile_force_f"]]
        # labels1 = ["contractility", "contractility"]
        # values2 =[key_values['mean_normal_stress_b'],key_values['mean_normal_stress_f'], key_values["mean_shear_b"], key_values["mean_shear_f"]]
        # labels2 =["mean normal stress", "mean normal stress", "meanshear stress", "mean shear stress"]
        # values3 = [key_values["cv_b"], key_values["cv_f"]]
        # lables3 = ["cv", "cv"]

    # fig1, ax1 = bar_plot(ax, values1, labels1, at=False, types=types)
    # fig1.savefig(os.path.join(out_folder, types["m"] + "1" + ext))
    # fig2, ax2 = bar_plot(values2, labels2, at=False, types=types)
    # fig2.savefig(os.path.join(out_folder, types["m"] + "2" + ext))
    # fig3, ax3 = bar_plot(values3, lables3, at=False, types=types, vmax=1)
    #  fig3.savefig(os.path.join(out_folder, types["m"] + "3" + ext))

    if types["r"] in plot_types or plot_types == "all":
        rs = {key: value['r_squared'] for key, value in scalar_comaprisons.items() if
              not np.isnan(value['r_squared']) and key in ["forces", "mean normal stress"]}
        fig = plt.figure(figsize=(3.2, 4.75))
        pos = [1, 1.7]
        plt.bar(pos, list(rs.values()), width=0.4, color="C5")
        # plt.xticks(rotation="70",fontsize=15)
        # plt.xticks(rotation="70",fontsize=15)
        plt.xticks(pos)
        plt.gca().tick_params(axis="both", which="both", color="black", length=4, width=2, labelsize=20,
                              labelcolor="black", labelbottom=False)
        set_axis_attribute(plt.gca(), "set_color", "black")
        set_axis_attribute(plt.gca(), "set_linewidth", 2)
        if at:
            plt.title(types["r"])
        plt.ylim((0, 1))
        plt.tight_layout()
        fig.savefig(os.path.join(out_folder, types["r"] + ext))

    if types["exp_test1"] in plot_types or plot_types == "all" and len(mean_normal_list) > 0:
        fig = show_exp_test(mean_normal_list=mean_normal_list, max_dict=max_dict, mask_exp_list=mask_exp_list)
        fig.savefig(os.path.join(out_folder, "expansion1" + ext))

    if types["exp_test2"] in plot_types or plot_types == "all":
        # average normal stress relative to groundtruth stress
        fig = plt.figure()
        be = np.array(border_ex_test) * ps_new_ex
        if plot_gt_exp:
            plt.plot(be, be_avm_list, color="C3", linewidth=5)
            plt.plot(be, [1] * (len(be_avm_list)), color="C4", linewidth=5)
            plt.ylim((0, max_dict["stress"] * 1.2))
        else:
            plt.plot(be, be_avm_list, color="C3", linewidth=5)
            plt.ylim((0, max_dict["stress"] * 1.2))
        plt.xlim(0, vmax_x_ep)
        plt.gca().tick_params(axis="both", which="both", color="black", length=4, width=2, labelsize=20,
                              labelcolor="black")
        set_axis_attribute(plt.gca(), "set_color", "black")
        set_axis_attribute(plt.gca(), "set_linewidth", 2)

        # plt.title(types["exp_test2"])
        plt.tight_layout()
        fig.savefig(os.path.join(out_folder, "avg_normal_stress_expansion" + ext))

    if types["exp_test2"] in plot_types or plot_types == "all":
        # average normal stress relative to groundtruth stress
        fig = plt.figure()
        be = np.array(border_ex_test) * ps_new_ex
        be_avm_list = np.array(be_avm_list) / np.max(be_avm_list)  # normalizing
        if plot_gt_exp:
            plt.plot(be, be_avm_list, color="C3", linewidth=5)
            plt.plot(be, [1] * (len(be_avm_list)), color="C4", linewidth=5)
            plt.ylim((0,1.1))
        else:
            plt.plot(be, be_avm_list, color="C3", linewidth=5)
            plt.ylim((0, 1.1))
        plt.xlim(0, vmax_x_ep)
        plt.gca().tick_params(axis="both", which="both", color="black", length=4, width=2, labelsize=20,
                              labelcolor="black")
        set_axis_attribute(plt.gca(), "set_color", "black")
        set_axis_attribute(plt.gca(), "set_linewidth", 2)

        # plt.title(types["exp_test2"])
        plt.tight_layout()
        fig.savefig(os.path.join(out_folder, "avg_normal_stress_expansion_normalized" + ext))

    if types["exp_test3"] in plot_types or plot_types == "all" and len(mean_normal_list) > 0:
        # displaying forces and masks
        nf = createFolder(os.path.join(out_folder, "exp_plots_forces"))
        max_dict_local = {"force": None }
        for i, m_exp in enumerate(mask_exp_list):
            pl = copy.deepcopy(paras)
            pl.update({"dm": True, "show_fm": True, "mask_fm": m_exp.astype(bool), "add_name": "_exp" + str(i),
                       "out_folder": nf, "max_dict": max_dict_local, "fx_f": fields["fx_f_exp"],
                       "fy_f": fields["fy_f_exp"], "mask": fields["mask_exp"]})
            try:
                fig, ax = show_forces_forward(**pl)
            except RecursionError:
                pass

    if types["exp_test4"] in plot_types or plot_types == "all" and len(mean_normal_list) > 0:
        sub_folder_stress = createFolder(os.path.join(out_folder, "stresses"))
        sub_folder_force = createFolder(os.path.join(out_folder, "forces"))
        for i, (ms, mask_expand) in enumerate(zip(mean_normal_list, mask_exp_list)):
            fig, ax = show_map_clickpoints(ms, cbar_style="out", figsize=figsize, cmap=cmap, cbar_tick_label_size=30,
                                           vmin=0,
                                           vmax=max_dict["stress"])
            add_title(fig, types["norm_b"], at=at)
            display_mask(fig, mask, display_type=display_type, color="#FFEF00")
            display_mask(fig, mask_expand, display_type=display_type, color="C3")
            fig.savefig(os.path.join(sub_folder_stress, types["exp_test4"] + "%s" % str(i) + ext))

            fig, ax = show_quiver(fx_f, fy_f, figsize=figsize, scale_ratio=arrow_scale, filter=[0, 10],
                                  width=arrow_width, headlength=3,
                                  headwidth=4,
                                  headaxislength=2, cbar_tick_label_size=30, cmap=cmap, cbar_style="not-clickpoints",
                                  vmin=0, vmax=1600)
            add_title(fig, types["f_f"], at=at)
            display_mask(fig, mask, display_type=display_type, color="#FFEF00", dm=dm)
            display_mask(fig, mask_expand, display_type=display_type, color="C3", dm=dm)
            plt.tight_layout()
            fig.savefig(os.path.join(sub_folder_force, types["exp_test4"] + "%s" % str(i) + ext))

    if types["exp_test5"] in plot_types or plot_types == "all" and len(mean_normal_list) > 0:

        pl = copy.deepcopy(paras)
        pl["dm"] = False

        pl["figsize"] = (np.mean(paras["figsize"]) *  fields["mask_exp"].shape[1] / fields["mask_exp"].shape[0],
                         np.mean(paras["figsize"]) * 1.022 * mask.shape[0] / fields["mask_exp"].shape[1])
        pl["fx_f"] = fields["fx_f_exp"]
        pl["fy_f"] = fields["fy_f_exp"]
        pl["max_dict"] = {"force" :None}

        fig, ax = show_forces_forward(**pl)
        ax.set_xlim(-0.5, 0.5 + fields["mask_exp"].shape[1])
        ax.set_ylim(0.5 + fields["mask_exp"].shape[0], -0.5)
        add_title(fig, types["f_f"], at=at)
        be = np.array(border_ex_test) * ps_new_ex
        # display_mask(fig, mask_exp_list[0], display_type=display_type, color=mask_fm_color, d=np.sqrt(2), dm=True, lw=7)
        max_id = np.argmax(be_avm_list)

        try:
            # max_id=23
            display_mask(fig, mask_exp_list[max_id + 12], display_type=display_type, color=mask_fm_color, d=np.sqrt(2),
                         dm=True,
                         lw=7)  # mask_exp_list[max_id+6]
            display_mask(fig, fields["mask_exp"], display_type=display_type, color="C1", dm=True, lw=7)
            end_id = np.argmin(np.abs(be - vmax_x_ep))
            display_mask(fig, mask_exp_list[end_id], display_type=display_type, color=mask_fm_color, d=np.sqrt(2),
                         dm=True, lw=7)
        except RecursionError as e:
            print(e)

        ax.set_position([0, 0, 1, 1])
        fig.set_frameon(False)
        if ext == ".svg":
            print(out_folder)
            fig.savefig(os.path.join(out_folder, "exp_forces_example" + ext))
        else:
            fig.savefig(os.path.join(out_folder, "exp_forces_example" + ext), dpi=300)

def show_exp_test(mean_normal_list=None, max_dict=None, mask_exp_list=None):
    n = int(np.ceil(np.sqrt(len(mean_normal_list))))
    fig, axs = plt.subplots(n, n)
    axs = axs.flatten()
    for i in range(len(mean_normal_list)):
        axs[i].imshow(mean_normal_list[i], vmax=max_dict["stress"])
        axs[i].set_title("mean normal stress #%s" % str(i), x=0.5, y=0.9, transform=axs[i].transAxes, color="white")
        display_mask(fig, mask_exp_list[i], display_type="outline", type=1, color="C1", d=np.sqrt(2), ax=axs[i])

    return fig

def plot_gradient_normal_stress(stress_tensor):
    dsxx_x = np.gradient(stress_tensor[:, :, 0, 0], axis=1)
    dsyy_y = np.gradient(stress_tensor[:, :, 1, 1], axis=0)
    dsxx_y = np.gradient(stress_tensor[:, :, 0, 0], axis=0)
    dsyy_x = np.gradient(stress_tensor[:, :, 1, 1], axis=1)

    fig, axs = plt.subplots(2, 2)
    plt.suptitle("gradient")
    im = axs[0, 0].imshow(dsxx_x)
    axs[0, 0].set_title("dsxx_x", x=0.5, y=0.9, transform=axs[0, 0].transAxes, color="white")
    im = axs[0, 1].imshow(dsyy_y)
    axs[0, 1].set_title("dsyy_y", x=0.5, y=0.9, transform=axs[0, 1].transAxes, color="white")
    im = axs[1, 0].imshow(dsxx_y)
    axs[1, 0].set_title("dsxx_y", x=0.5, y=0.9, transform=axs[1, 0].transAxes, color="white")
    im = axs[1, 1].imshow(dsyy_x)
    axs[1, 1].set_title("dsyy_x", x=0.5, y=0.9, transform=axs[1, 1].transAxes, color="white")

def add_title(fig, title_str, at=True):
    if at:
        ax = fig.axes[0]
        ax.set_title(title_str, x=0.5, y=0.9, transform=ax.transAxes, color="white")

def add_mask(fig, mask):
    mask_show = make_display_mask(mask)
    ax = fig.axes[0]
    ax.imshow(mask, alpha=0.4)

def filter_arrows_for_square(fx, fy, filter=6):
    mask_filtered = np.zeros(fx.shape)
    fx_filtered, fy_filtered = np.zeros(fx.shape), np.zeros(fx.shape)

    mask_uf = np.logical_or(fx != 0, fy != 0)
    out_line_graph, points = mask_to_graph(mask_uf, d=np.sqrt(2))
    circular_path = find_path_circular(out_line_graph, 0)
    circular_path = [x for i, x in enumerate(circular_path) if i % filter == 0]
    circular_path.append(circular_path[0])  # to plot a fully closed loop
    mask_filtered[points[circular_path][:, 0], points[circular_path][:, 1]] = 1
    mask_filtered = mask_filtered.astype(bool)

    fx_filtered[mask_filtered] = fx[mask_filtered]
    fy_filtered[mask_filtered] = fy[mask_filtered]
    return fx_filtered, fy_filtered

from scipy.signal import convolve2d

def custom_edge_filter(arr):
    arr_out = copy.deepcopy(arr).astype(int)
    shape1 = np.array([[0, 1, 0], [1, 1, 0], [0, 0, 0]])
    shape2 = np.array([[0, 1, 0], [0, 1, 1], [0, 0, 0]])
    shape3 = np.array([[0, 0, 0], [0, 1, 1], [0, 1, 0]])
    shape4 = np.array([[0, 0, 0], [1, 1, 0], [0, 1, 0]])
    for s in [shape1, shape2, shape3, shape4]:
        rem_mask = convolve2d(arr, s, mode="same") == 3
        arr_out[rem_mask] = 0
    return arr_out.astype(bool)


# coding: utf-8

# In[1]:

import math
import numpy as np
import time
import pandas as pd
import os
import configparser   #來存取ini文檔
import numpy as np
import matplotlib.pyplot as plt
import xlsxwriter

def matmult(a,b): #input兩個矩陣返回乘法結果
    zip_b = zip(*b)
    # uncomment next line if python 3 :
    # zip_b = list(zip_b)
    return [[sum(ele_a*ele_b for ele_a, ele_b in zip(row_a, col_b))
             for col_b in zip_b] for row_a in a]

def hh(z):
    return (z + math.exp(z*-1))/2

def Hmat(u, v):
    return hh(u+v) - hh(abs(u-v))

def Galfa(alfa, Q, mm, umax, nrofcoup, T2, Tau):
    h = []
    temp1 = []
    # for i in range(umax*nrofcoup):
    #     h.append([])
    #     for j in range(umax*nrofcoup):
    #         h[i].append([])
    # for i in range(mm):
    #     h.append([])
    #     for j in range(1):
    #         h[i].append([])

    for i in range(1, umax*nrofcoup+1):
        h.append([])
        for j in range(1, umax*nrofcoup+1):
            h[i-1].append(Hmat(alfa * i / nrofcoup, alfa * j / nrofcoup))

    for i in range(mm):
        temp1.append([])
        temp1[i].append(1 - sum(Q[i]))

    Q = np.matrix(Q)
    h = np.matrix(h)
    b = np.linalg.inv((Q*h)*Q.transpose())*temp1

    gamma = Q.transpose() * b
    temp2 = 0
    temp3 = 0
    for i in range(1, umax*nrofcoup+1):
        # print('temp2=='+str(temp2)+' gamma=='+str(gamma[i-1, 0]))
        temp2 = temp2 + gamma[i-1, 0] * i / nrofcoup
        temp3 = temp3 + gamma[i-1, 0] * np.sinh(alfa * i / nrofcoup)
    kappa = (1 + alfa * temp2) / temp3
    output = []
    output.append(alfa / abs(1 - kappa * math.exp(T2 * alfa)) - Tau)
    output.append(gamma)
    return output

def AlfaScan(lastalfa, stepsize, Q, mm, umax, nrofcoup, T2, Tau):
    '''
    這邊可能有錯 要檢查迴圈
    '''
    alfaReturn = 0
    for alfa in np.arange(lastalfa + stepsize / 10 - stepsize, lastalfa, stepsize / 10):
        alfa = float(round(alfa, 7))
        galfa_output = Galfa(alfa, Q, mm, umax, nrofcoup, T2, Tau)
        alfaReturn = alfa
        if galfa_output[0] <= 0:
            break
    return [alfaReturn, galfa_output[1]]

def SmithWilsonBruteForce(Instrument, DataIn, nrofcoup, CRA, UFRac, alfamin, Tau, T2):
    precision = 6
    Tau = Tau / 10000
    nrofrates = 0
    for i in range(len(DataIn)):
        nrofrates += DataIn[i][0]
    # print('nrofrates=='+str(nrofrates))

    u = []  #len(nrofrates)
    r = []  #len(nrofrates)
    j = 0
    for i in range(len(DataIn)):
        if DataIn[i][0] == 1:
            j = j + 1
            u.append(DataIn[i][1])
            r.append(DataIn[i][2] - CRA / 10000)
    umax = max(u)
    # print('umax==='+str(umax))
    lnUFR = math.log(1+UFRac)
    # print('lnUFR==='+str(lnUFR))

    if Instrument == 'Zero':
        nrofcoup = 1

    q = []
    for i in range(nrofrates):
        q.append([])
        for j in range(umax*nrofcoup):
            q[i].append(0)
    # print('q是'+str(nrofrates)+'X'+str(umax*nrofcoup)+'陣列')
    if Instrument == 'Zero':
        for i in range(nrofrates):
            for j in range(nrofrates):
                if i != j:
                    q[i][j] = 0
                else:
                    q[i][u[i]-1] = math.exp(lnUFR*-1 * u[i]) * (math.pow((1 + r[i]),u[i]))
    elif Instrument == 'Swap' or Instrument == 'Bond':
        for i in range(nrofrates):
            for j in range(u[i]*nrofcoup-1):
                q[i][j] = math.exp(lnUFR*-1 * j / nrofcoup) * r[i] / nrofcoup
            q[i][j] = math.exp(lnUFR*-1 * j / nrofcoup) * (1 + r[i] / nrofcoup)
    galfa_output = Galfa(alfamin, q, nrofrates, umax, nrofcoup, T2, Tau)
    # print(galfa_output[0])
    # time.sleep(50)
    if galfa_output[0] <= 0:
        alfaReturn = alfamin
        gamma = galfa_output[1]
    else:
        stepsize = 0.1
        alfaReturn = 0
        for alfa in np.arange(alfamin+stepsize, 20, stepsize):
            alfa = float(round(alfa, 4))
            alfaReturn = alfa
            if Galfa(alfa, q, nrofrates, umax, nrofcoup, T2, Tau)[0] <= 0:
                break
        for i in range(1, precision):
            alfascanoutput = AlfaScan(alfaReturn, stepsize, q, nrofrates, umax, nrofcoup, T2, Tau)
            alfaReturn = alfascanoutput[0]
            stepsize = stepsize / 10
        gamma = alfascanoutput[1]

    h = []
    for i in range(121+1):
        h.append([])
        for j in range(umax*nrofcoup):
            h[i].append([])

    g = []
    for i in range(121+1):
        g.append([])
        for j in range(umax*nrofcoup):
            g[i].append([])

    for i in range(121+1):
        for j in range(1, umax*nrofcoup+1):
            h[i][j-1] = Hmat(alfaReturn * i, alfaReturn * j / nrofcoup)
            if j / nrofcoup > i:
                g[i][j-1] = (alfaReturn * (1 - math.exp(alfaReturn*-1 * j / nrofcoup) * np.cosh(alfaReturn * i)))
            else:
                g[i][j-1] = (alfaReturn * math.exp(alfaReturn*-1 * i) * np.sinh(alfaReturn * j / nrofcoup))

    tempDiscount = []
    tempintensity = []
    discount = []
    fwintensity = []
    yldintensity = []
    forwardac = []
    zeroac = []
    forwardcc = []
    zerocc = []

    temptempdiscount = (np.matrix(h)*gamma).transpose()
    temptempintensity = (np.matrix(g)*gamma).transpose()
    for i in range(121+1):
        tempDiscount.append(temptempdiscount[0,i])
        tempintensity.append(temptempintensity[0,i])
    temp = 0
    for i in range(1, umax*nrofcoup+1):
        temp = temp + (1 - math.exp(alfaReturn * -1 * i / nrofcoup)) * gamma[i-1, 0]

    yldintensity.append(lnUFR - alfaReturn * temp)
    fwintensity.append(yldintensity[0])
    discount.append(1)
    yldintensity.append(lnUFR - math.log(1 + tempDiscount[1]))
    fwintensity.append(lnUFR - tempintensity[1] / (1 + tempDiscount[1]))
    discount.append(math.exp(lnUFR*-1) * (1 + tempDiscount[1]))
    zeroac.append(0)
    zeroac.append(1 / discount[1] - 1)
    forwardac.append(0)
    forwardac.append(zeroac[1])
    for i in range(2, 121):
        yldintensity.append(lnUFR - math.log(1 + tempDiscount[i]) / i)
        fwintensity.append(lnUFR - tempintensity[i] / (1 + tempDiscount[i]))
        discount.append(math.exp(lnUFR * -1 * i) * (1 + tempDiscount[i]))
        zeroac.append(math.pow(1 / discount[i], (1 / i)) - 1)
        forwardac.append(discount[i - 1] / discount[i] - 1)

    yldintensity.append(0)
    fwintensity.append(0)
    zeroac.append(0)
    forwardac.append(0)
    discount.append(alfaReturn)

    forwardcc.append(0)
    zerocc.append(0)
    for i in range(1, 121):
        forwardcc.append(math.log(1 + forwardac[i]))
        zerocc.append(math.log(1 + zeroac[i]))
    forwardcc.append(0)
    zerocc.append(0)
    # print(len(discount))
    # print(len(yldintensity))
    # print(len(zeroac))
    # print(len(fwintensity))
    # print(len(forwardcc))
    # print(len(forwardac))
    # print(forwardcc)
    #zeroac forwardcc forwardac(少了第一項 index=1)有誤
    # print(yldintensity)
    return [discount, yldintensity, zeroac, fwintensity, forwardcc, forwardac]

def genDataIn(dataFileName, dataDate, needOneList):        #needOneList會是1-10或1 5 10 其中一種
    dataIn = []
    dataFile = open(dataFileName, 'r')
    dataLines = dataFile.readlines()
    for data in dataLines:
        if dataDate == data.split(',')[0]:          #???為啥需要這行
            for i in range(10):
                if i+1 in needOneList:
                    dataIn.append([1, i+1, float(data.split(',')[i+1].replace('\n', ''))])
                else:
                    dataIn.append([0, i+1, float(data.split(',')[i+1].replace('\n', ''))])
    return dataIn

def getDateList(dataFileName):
    dateList = []
    dataFile = open(dataFileName, 'r')    #'r':讀取   'w': 新建檔案寫入(檔案可不存在，若存在則清空)
    dataLines = dataFile.readlines()
    for data in dataLines:
        dateList.append(data.split(',')[0])     # [0]: 使得datelist只有時間
    return dateList

#main

# 從config.ini讀取各參數
config = configparser.ConfigParser()     # 創建對象
config.read('config.ini', encoding='utf-8')
dataFileName = config.get('Parameter', 'DataFileName')
Instrument = config.get('Parameter', 'Instrument')
nrofcoup = float(config.get('Parameter', 'Nrofcoup'))
CRA = float(config.get('Parameter', 'CRA'))
UFRac = float(config.get('Parameter', 'UFRac'))
alfamin = float(config.get('Parameter', 'Alfamin'))
Tau = float(config.get('Parameter', 'Tau'))
T2 = float(config.get('Parameter', 'T2'))
rateType = config.get('Parameter', 'RateType')

needOneBigList = []
for needOne in config.get('Parameter', 'LiquidMaturity').split('|'):
    needOneBigList.append([float(i) for i in needOne.split(',')])
    # needOneBigList:[[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0], [1.0, 5.0, 10.0]]

dateList = getDateList(dataFileName)

dirName = 'output'
if os.path.isdir(dirName) == False:
    os.mkdir(dirName)                 #創建目錄

for k in range(len(needOneBigList)):      # k代表用1-10資料 還是1 5 10資料
    row = 0
    col = 0
    workbook = xlsxwriter.Workbook(dirName+'/output'+str(k)+'_'+rateType+'.xlsx') #k=0代表用1-10, k=1代表用1 5 10
    worksheet = workbook.add_worksheet()
    # for i in range(len(dateList)):
    #     if i == 0:
    #         worksheet.write(row, col, 'Year')
    #     else:
    #         worksheet.write(row, col, str(i))
    #     row = row + 1

    worksheet.write(0, col, 'Year')
    for date in dateList:
        row = 0
        col = col + 1
        print('正在運行 日期=='+date)
        worksheet.write_string(row, col, str(date))     #???  這個感覺跟下面重複
        DataIn = genDataIn(dataFileName, date, needOneBigList[k])
        if rateType == 'forward':
            rateList = SmithWilsonBruteForce(Instrument, DataIn, nrofcoup, CRA, UFRac, alfamin, Tau, T2)[5]
        else:
            rateList = SmithWilsonBruteForce(Instrument, DataIn, nrofcoup, CRA, UFRac, alfamin, Tau, T2)[2]
        row = row + 1
        worksheet.write_string(row, col, str(date))     #???重複了ㄅ
        for i in range(len(rateList)-1):
            # print(rateList[i])
            worksheet.write_string(row, 0, str(i))
            if i == 0:
                continue
            else:
                worksheet.write_string(row, col, str(rateList[i]))
            row = row + 1
    workbook.close()

    # df = pd.read_csv(dirName+'/output'+str(k)+'_'+rateType+'.csv', encoding='utf-8')
    # df2 = df.T
    # df2.to_csv(dirName+'/output'+str(k)+'_'+rateType+'.csv', header=False)

    df = pd.read_excel(dirName+'/output'+str(k)+'_'+rateType+'.xlsx')
    df.set_index('Year',inplace=True)

    name = df.columns

    x = rateType+'_'+str(UFRac)+'_Interpolation_'+str(T2) #儲存圖片的檔名＆圖片的大標名稱
    for i in range(0,len(name),6):  #間隔6個月畫一條線
        plt.style.use('ggplot')
        plt.figure(num = 3,figsize= (20,13))
        plt.title(x)

        plt.xlabel('year',fontsize=30)
        plt.ylabel('forward rate', fontsize=30)
        plt.plot(df[name[i]][0:60].values,label = name[i]) #60年代表畫圖畫到60, 如果今天是收斂到40年可以改成40~
 #       plt.legend(bbox_to_anchor=(1, 0),loc = 3,borderaxespad=0) #看要不要加圖標

    plt.savefig(dirName+'/output'+str(k)+'_'+x+'.png')

# In[13]:

def genDataIn(dataFileName, dataDate, needOneList):        #needOneList會是1-10或1 5 10 其中一種
    dataIn = []
    dataFile = open(dataFileName, 'r')
    dataLines = dataFile.readlines()
    for data in dataLines:
        if dataDate == data.split(',')[0]:          #???為啥需要這行
            for i in range(10):
                if i+1 in needOneList:
                    dataIn.append([1, i+1, float(data.split(',')[i+1].replace('\n', ''))])
                else:
                    dataIn.append([0, i+1, float(data.split(',')[i+1].replace('\n', ''))])
    return dataIn

# In[20]:

DataIn = genDataIn(dataFileName, date, needOneBigList[1])

# In[21]:

DataIn

# In[22]:

DataIn = genDataIn(dataFileName, date, needOneBigList[0])
DataIn


"""Configure and control camera via onvif."""

import datetime
import logging
import site
from enum import Enum

import numpy
from onvif import ONVIFCamera, ONVIFError

from frigate.config import FrigateConfig
from frigate.types import PTZMetricsTypes

logger = logging.getLogger(__name__)

class OnvifCommandEnum(str, Enum):
    """Holds all possible move commands"""

    init = "init"
    move_down = "move_down"
    move_left = "move_left"
    move_right = "move_right"
    move_up = "move_up"
    preset = "preset"
    stop = "stop"
    zoom_in = "zoom_in"
    zoom_out = "zoom_out"

class OnvifController:
    def __init__(
        self, config: FrigateConfig, ptz_metrics: dict[str, PTZMetricsTypes]
    ) -> None:
        self.cams: dict[str, ONVIFCamera] = {}
        self.ptz_metrics = ptz_metrics

        for cam_name, cam in config.cameras.items():
            if not cam.enabled:
                continue

            if cam.onvif.host:
                try:
                    self.cams[cam_name] = {
                        "onvif": ONVIFCamera(
                            cam.onvif.host,
                            cam.onvif.port,
                            cam.onvif.user,
                            cam.onvif.password,
                            wsdl_dir=site.getsitepackages()[0].replace(
                                "dist-packages", "site-packages"
                            )
                            + "/wsdl",
                        ),
                        "init": False,
                        "active": False,
                        "features": [],
                        "presets": {},
                    }
                except ONVIFError as e:
                    logger.error(f"Onvif connection to {cam.name} failed: {e}")

    def _init_onvif(self, camera_name: str) -> bool:
        onvif: ONVIFCamera = self.cams[camera_name]["onvif"]

        # create init services
        media = onvif.create_media_service()

        try:
            profile = media.GetProfiles()[0]
        except ONVIFError as e:
            logger.error(f"Unable to connect to camera: {camera_name}: {e}")
            return False

        ptz = onvif.create_ptz_service()
        request = ptz.create_type("GetConfigurationOptions")
        request.ConfigurationToken = profile.PTZConfiguration.token
        ptz_config = ptz.GetConfigurationOptions(request)
        logger.debug(f"Onvif config for {camera_name}: {ptz_config}")

        fov_space_id = next(
            (
                i
                for i, space in enumerate(
                    ptz_config.Spaces.RelativePanTiltTranslationSpace
                )
                if "TranslationSpaceFov" in space["URI"]
            ),
            None,
        )

        # setup continuous moving request
        move_request = ptz.create_type("ContinuousMove")
        move_request.ProfileToken = profile.token
        self.cams[camera_name]["move_request"] = move_request

        # setup relative moving request for autotracking
        move_request = ptz.create_type("RelativeMove")
        move_request.ProfileToken = profile.token
        if move_request.Translation is None and fov_space_id is not None:
            move_request.Translation = ptz.GetStatus(
                {"ProfileToken": profile.token}
            ).Position
            move_request.Translation.PanTilt.space = ptz_config["Spaces"][
                "RelativePanTiltTranslationSpace"
            ][fov_space_id]["URI"]

        try:
            move_request.Translation.Zoom.space = ptz_config["Spaces"][
                "RelativeZoomTranslationSpace"
            ][0]["URI"]
        except Exception:
            # camera does not support relative zoom
            pass

        if move_request.Speed is None:
            move_request.Speed = ptz.GetStatus({"ProfileToken": profile.token}).Position
        self.cams[camera_name]["relative_move_request"] = move_request

        # setup relative moving request for autotracking
        move_request = ptz.create_type("AbsoluteMove")
        move_request.ProfileToken = profile.token
        self.cams[camera_name]["absolute_move_request"] = move_request

        # status request for autotracking
        status_request = ptz.create_type("GetStatus")
        status_request.ProfileToken = profile.token
        self.cams[camera_name]["status_request"] = status_request

        # setup existing presets
        try:
            presets: list[dict] = ptz.GetPresets({"ProfileToken": profile.token})
        except ONVIFError as e:
            logger.warning(f"Unable to get presets from camera: {camera_name}: {e}")
            presets = []

        for preset in presets:
            self.cams[camera_name]["presets"][preset["Name"].lower()] = preset["token"]

        # get list of supported features
        ptz_config = ptz.GetConfigurationOptions(request)
        supported_features = []

        if ptz_config.Spaces and ptz_config.Spaces.ContinuousPanTiltVelocitySpace:
            supported_features.append("pt")

        if ptz_config.Spaces and ptz_config.Spaces.ContinuousZoomVelocitySpace:
            supported_features.append("zoom")

        if ptz_config.Spaces and ptz_config.Spaces.RelativePanTiltTranslationSpace:
            supported_features.append("pt-r")

        if ptz_config.Spaces and ptz_config.Spaces.RelativeZoomTranslationSpace:
            supported_features.append("zoom-r")

        if fov_space_id is not None:
            supported_features.append("pt-r-fov")
            self.cams[camera_name][
                "relative_fov_range"
            ] = ptz_config.Spaces.RelativePanTiltTranslationSpace[fov_space_id]

        self.cams[camera_name]["relative_fov_supported"] = fov_space_id is not None

        self.cams[camera_name]["features"] = supported_features

        self.cams[camera_name]["init"] = True
        return True

    def _stop(self, camera_name: str) -> None:
        onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
        move_request = self.cams[camera_name]["move_request"]
        onvif.get_service("ptz").Stop(
            {
                "ProfileToken": move_request.ProfileToken,
                "PanTilt": True,
                "Zoom": True,
            }
        )
        self.cams[camera_name]["active"] = False

    def _move(self, camera_name: str, command: OnvifCommandEnum) -> None:
        if self.cams[camera_name]["active"]:
            logger.warning(
                f"{camera_name} is already performing an action, stopping..."
            )
            self._stop(camera_name)

        self.cams[camera_name]["active"] = True
        onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
        move_request = self.cams[camera_name]["move_request"]

        if command == OnvifCommandEnum.move_left:
            move_request.Velocity = {"PanTilt": {"x": -0.5, "y": 0}}
        elif command == OnvifCommandEnum.move_right:
            move_request.Velocity = {"PanTilt": {"x": 0.5, "y": 0}}
        elif command == OnvifCommandEnum.move_up:
            move_request.Velocity = {
                "PanTilt": {
                    "x": 0,
                    "y": 0.5,
                }
            }
        elif command == OnvifCommandEnum.move_down:
            move_request.Velocity = {
                "PanTilt": {
                    "x": 0,
                    "y": -0.5,
                }
            }

        onvif.get_service("ptz").ContinuousMove(move_request)

    def _move_relative(self, camera_name: str, pan, tilt, speed) -> None:
        if not self.cams[camera_name]["relative_fov_supported"]:
            logger.error(f"{camera_name} does not support ONVIF RelativeMove (FOV).")
            return

        logger.debug(f"{camera_name} called RelativeMove: pan: {pan} tilt: {tilt}")

        if self.cams[camera_name]["active"]:
            logger.warning(
                f"{camera_name} is already performing an action, not moving..."
            )
            return

        self.cams[camera_name]["active"] = True
        self.ptz_metrics[camera_name]["ptz_stopped"].clear()
        logger.debug(f"PTZ start time: {datetime.datetime.now().timestamp()}")
        self.ptz_metrics[camera_name][
            "ptz_start_time"
        ].value = datetime.datetime.now().timestamp()
        self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0
        onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
        move_request = self.cams[camera_name]["relative_move_request"]

        # function takes in -1 to 1 for pan and tilt, interpolate to the values of the camera.
        # The onvif spec says this can report as +INF and -INF, so this may need to be modified
        pan = numpy.interp(
            pan,
            [-1, 1],
            [
                self.cams[camera_name]["relative_fov_range"]["XRange"]["Min"],
                self.cams[camera_name]["relative_fov_range"]["XRange"]["Max"],
            ],
        )
        tilt = numpy.interp(
            tilt,
            [-1, 1],
            [
                self.cams[camera_name]["relative_fov_range"]["YRange"]["Min"],
                self.cams[camera_name]["relative_fov_range"]["YRange"]["Max"],
            ],
        )

        move_request.Speed = {
            "PanTilt": {
                "x": speed,
                "y": speed,
            },
            "Zoom": 0,
        }

        move_request.Translation.PanTilt.x = pan
        move_request.Translation.PanTilt.y = tilt
        move_request.Translation.Zoom.x = 0

        onvif.get_service("ptz").RelativeMove(move_request)

        self.cams[camera_name]["active"] = False

    def _move_to_preset(self, camera_name: str, preset: str) -> None:
        if preset not in self.cams[camera_name]["presets"]:
            logger.error(f"{preset} is not a valid preset for {camera_name}")
            return

        self.cams[camera_name]["active"] = True
        self.ptz_metrics[camera_name]["ptz_stopped"].clear()
        move_request = self.cams[camera_name]["move_request"]
        onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
        preset_token = self.cams[camera_name]["presets"][preset]
        onvif.get_service("ptz").GotoPreset(
            {
                "ProfileToken": move_request.ProfileToken,
                "PresetToken": preset_token,
            }
        )
        self.ptz_metrics[camera_name]["ptz_stopped"].set()
        self.cams[camera_name]["active"] = False

    def _zoom(self, camera_name: str, command: OnvifCommandEnum) -> None:
        if self.cams[camera_name]["active"]:
            logger.warning(
                f"{camera_name} is already performing an action, stopping..."
            )
            self._stop(camera_name)

        self.cams[camera_name]["active"] = True
        onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
        move_request = self.cams[camera_name]["move_request"]

        if command == OnvifCommandEnum.zoom_in:
            move_request.Velocity = {"Zoom": {"x": 0.5}}
        elif command == OnvifCommandEnum.zoom_out:
            move_request.Velocity = {"Zoom": {"x": -0.5}}

        onvif.get_service("ptz").ContinuousMove(move_request)

    def handle_command(
        self, camera_name: str, command: OnvifCommandEnum, param: str = ""
    ) -> None:
        if camera_name not in self.cams.keys():
            logger.error(f"Onvif is not setup for {camera_name}")
            return

        if not self.cams[camera_name]["init"]:
            if not self._init_onvif(camera_name):
                return

        if command == OnvifCommandEnum.init:
            # already init
            return
        elif command == OnvifCommandEnum.stop:
            self._stop(camera_name)
        elif command == OnvifCommandEnum.preset:
            self._move_to_preset(camera_name, param)
        elif (
            command == OnvifCommandEnum.zoom_in or command == OnvifCommandEnum.zoom_out
        ):
            self._zoom(camera_name, command)
        else:
            self._move(camera_name, command)

    def get_camera_info(self, camera_name: str) -> dict[str, any]:
        if camera_name not in self.cams.keys():
            logger.error(f"Onvif is not setup for {camera_name}")
            return {}

        if not self.cams[camera_name]["init"]:
            self._init_onvif(camera_name)

        return {
            "name": camera_name,
            "features": self.cams[camera_name]["features"],
            "presets": list(self.cams[camera_name]["presets"].keys()),
        }

    def get_camera_status(self, camera_name: str) -> dict[str, any]:
        if camera_name not in self.cams.keys():
            logger.error(f"Onvif is not setup for {camera_name}")
            return {}

        if not self.cams[camera_name]["init"]:
            self._init_onvif(camera_name)

        onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
        status_request = self.cams[camera_name]["status_request"]
        status = onvif.get_service("ptz").GetStatus(status_request)

        if status.MoveStatus.PanTilt == "IDLE" and status.MoveStatus.Zoom == "IDLE":
            self.cams[camera_name]["active"] = False
            if not self.ptz_metrics[camera_name]["ptz_stopped"].is_set():
                self.ptz_metrics[camera_name]["ptz_stopped"].set()

                logger.debug(f"PTZ stop time: {datetime.datetime.now().timestamp()}")

                self.ptz_metrics[camera_name][
                    "ptz_stop_time"
                ].value = datetime.datetime.now().timestamp()
        else:
            self.cams[camera_name]["active"] = True
            if self.ptz_metrics[camera_name]["ptz_stopped"].is_set():
                self.ptz_metrics[camera_name]["ptz_stopped"].clear()

                logger.debug(f"PTZ start time: {datetime.datetime.now().timestamp()}")

                self.ptz_metrics[camera_name][
                    "ptz_start_time"
                ].value = datetime.datetime.now().timestamp()
                self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0

        return {
            "pan": status.Position.PanTilt.x,
            "tilt": status.Position.PanTilt.y,
            "zoom": status.Position.Zoom.x,
            "pantilt_moving": status.MoveStatus.PanTilt,
            "zoom_moving": status.MoveStatus.Zoom,
        }


from django import forms
from .models import *

class telephoneForm(forms.ModelForm):

    class Meta:
        model = Telephone
        fields = '__all__'
        widgets = {

            "calling_code":forms.Select(),
            "number" : forms.TextInput(attrs={"pattern":"[0-9]+",
                                              "placeholder":"78 666 66 66"})

        }


# -*- coding: utf-8 -*-
from django.template.loader import get_template
from django.shortcuts import render,redirect
from django.http import HttpResponse
from .models import Post,About
from datetime import datetime
# Create your views here.

def homepage(request):
    template = get_template('index.html')
    posts = Post.objects.all()
    posts_list = list()
    for post in posts:
        posts_list.append(post.title)
    now = datetime.now()
    # locals()函数会把当前内存中收到的所有局部变量使用字典类型打包起来
    html = template.render(locals())
    return HttpResponse(html)

def showpost(request,slug):
    template = get_template('post.html')
    try:
        post = Post.objects.get(slug= slug)
        if post != None:
            html = template.render(locals())
            return HttpResponse(html)
    except:
        # 发生例外找不到时候，重定向到首页
        return redirect('/')

def showabout(request):
    template = get_template('about.html')
    try:
        now  = datetime.now()
        abouts = About.objects.all()
        html = template.render(locals())
        return HttpResponse(html)
    except:
        return redirect('/')


#!/usr/bin/env python
# -*- encoding: utf-8

import datetime as dt
import decimal
import json

from common import get_read_only_aws_resource

class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            return float(o)
        return super(DecimalEncoder, self).default(o)

if __name__ == "__main__":
    dynamodb = get_read_only_aws_resource("dynamodb").meta.client
    paginator = dynamodb.get_paginator("scan")

    date_slug = dt.datetime.now().strftime("%Y-%m-%d_%H-%m")
    out_path = f"ingests__{date_slug}.json"

    with open(out_path, "w") as outfile:
        for page in paginator.paginate(TableName="storage-ingests"):
            for item in page["Items"]:
                outfile.write(json.dumps(item, cls=DecimalEncoder) + "\n")
            outfile.flush()

    print(out_path)


import os, shutil, io

from tools import json2arbo, prepare_articles, update_procedure, \
    prepare_amendements, prepare_interventions, reorder_interventions_and_correct_procedure, \
    compute_stats, add_links
from tools.common import debug_file, print_json

def project_header_template(procedure):
    return """

Les données pour: "{long_title}"

Les données mises à disposition dans ces répertoires sont celles utilisées par La Fabrique de la Loi pour visualiser "{long_title}".

Elles ont été constituées par Regards Citoyens à partir de NosDéputés.fr, NosSénateurs.fr et les sites du Sénat et de l'Assemblée nationale. Elles sont réutilisables librement en  sous la licence ODBL.

Le répertoire  procedure/ contient les données brutes au format JSON sur les textes, les interventions et les amendements à chaque étape de la procédure. Le répertoire  viz/ contient les fichiers utilisés par l'application.

""".format(long_title=procedure.get('long_title'), dos_id=procedure['id'])

def dump_success_log(output_dir, log):
    log = log.getvalue()
    logfile = os.path.join(output_dir, "parsing.log")
    with open(logfile, 'w') as f:
        f.write(log)
    textid = output_dir.split('/')[-1]
    api_dir = output_dir.replace('/' + textid, '')
    err_log = os.path.join(api_dir, 'logs', textid)
    if os.path.exists(err_log):
        os.remove(err_log)

def process(dos, OUTPUT_DIR, log=io.StringIO(), skip_already_done=False):
    dos['id'] = dos.get('senat_id', dos.get('assemblee_id'))

    output_dir = os.path.join(OUTPUT_DIR, dos['id'] + '_tmp')
    final_output_dir = os.path.join(OUTPUT_DIR, dos['id'])
    print('     writing to:', dos['id'] + '_tmp')

    if skip_already_done and os.path.exists(final_output_dir):
        print(' - already done')
        return

    shutil.rmtree(output_dir, ignore_errors=True)

    debug_file(dos, 'debug_before_add_links.json')
    dos = add_links.process(dos)

    # add texte.json and write all the text files tree
    debug_file(dos, 'debug_before_json2arbo.json')
    dos = json2arbo.process(dos, output_dir + '/procedure')

    print(' - process article versions')
    json2arbo.mkdirs(os.path.join(output_dir, 'viz'))
    debug_file(dos, 'debug_before_prepare_articles.json')
    articles_etapes = prepare_articles.process(dos)
    print_json(articles_etapes, os.path.join(output_dir, 'viz', 'articles_etapes.json'))

    procedure = update_procedure.process(dos, articles_etapes)

    print(' - process amendements & interventions')
    procedure = prepare_amendements.process(output_dir, procedure)

    print(' - re-order interventions and correct procedure dates')
    procedure = reorder_interventions_and_correct_procedure.process(output_dir, procedure)

    print(' - prepare interventions.json')
    prepare_interventions.process(output_dir, procedure)

    print(' - compute stats')
    debug_file(dos, 'debug_before_stats.json')
    procedure['stats'] = compute_stats.process(output_dir, procedure)

    # remove intermediate data
    for step in procedure['steps']:
        for key in 'articles_completed', 'articles', 'texte.json':
            try:
                step.pop(key)
            except KeyError:
                pass

    # avoid duplicate titles
    if " de loi organique" in procedure['long_title']:
        procedure['short_title'] += " (texte organique)"

    # AN doslegs have no short_titles
    if 'short_title' not in procedure:
        procedure['short_title'] = procedure['long_title']

    print_json(procedure, os.path.join(output_dir, 'viz', 'procedure.json'))

    with open(os.path.join(output_dir, 'HEADER.html'), 'w') as f:
        f.write(project_header_template(procedure))

    shutil.rmtree(final_output_dir, ignore_errors=True)
    os.rename(output_dir, final_output_dir)

    print('  FINISHED -', dos['id'])

    dump_success_log(final_output_dir, log)


from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from cart.models import Cart
from products.models import Product

from .serializers import CartSerializer

class CartAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, *args, **kwargs):
        cart_obj = Cart.objects.get_existing_or_new(request)[0]
        serializer = CartSerializer(cart_obj)
        return Response(serializer.data)

    def post(self, request, *args, **kwargs):
        product_id = request.POST.get("id")
        product_obj = Product.objects.get(id=product_id)
        cart_obj = Cart.objects.get_existing_or_new(request)[0]
        if request.POST.get('type') == "remove":
            cart_obj.products.remove(product_obj)
            cart_obj.save()
        else:
            cart_obj.products.add(product_obj)
            cart_obj.save()
        serializer = CartSerializer(cart_obj)
        return Response(serializer.data)


# SYS
from sys import argv
from os.path import join, dirname, realpath
from os import getenv

# MODULES
from server import WSGI

server_rc = realpath(join(dirname(__file__), 'server/server.rc'))
statics_dir = realpath(join(dirname(__file__), 'server/statics'))
host = '127.0.0.1'

if "--dev" in argv:
    port = 8000
    environment = 'development'
    mongodb_uri = "mongodb://localhost:27017/icwa_waves"
    debug = True
else:
    port = 8000
    environment = 'production'
    mongodb_uri = getenv("MONGODB_URI")
    debug = False

class App (object):

  def __init__ (self):
     self.app = WSGI({
        "mongodb_uri": mongodb_uri,
        "statics_dir": statics_dir,
        "server_rc": server_rc,
        "port": port,
        "debug": debug,
     })

  def __call__ (self, *args, **kwargs):
    return self.app(*args, **kwargs)

app = App()

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple(host, port, app, use_debugger=debug, use_reloader=debug)


import pkgutil
from pathlib import Path

__all__ = (
    "build_editable",
    "__version__",
)

__version__ = "0.1"

_TEMPLATE = pkgutil.get_data(__package__, "install_hook.py").decode("utf-8")

def build_editable(location, expose=None, hide=None):
    """Generate files that can be added to a wheel to expose packages from a directory.

    By default, every package (directory with __init__.py) in the supplied
    location will be exposed on sys.path by the generated wheel.

    Optional arguments:

    expose: A list of packages to include in the generated wheel
            (overrides the default behaviour).
    hide: A list of sub-packages of exposed packages that will be
          invisible in the generated wheel.

    Returns: a list of (name, content) pairs, specifying files that should
    be added to the generated wheel. Callers are responsible for building a
    valid wheel containing these files.
    """

    location = Path(location)

    if expose is None:
        expose = [pkg.parent.name for pkg in location.glob("*/__init__.py")]
    if hide is None:
        hide = []

    for pkg in expose:
        code = _TEMPLATE
        for of, to in {
            '""  # location of replacement': str(location),
            '""  # excludes': hide,
        }.items():
            code = code.replace(of, repr(to))

        yield "{}.py".format(pkg), code


'''
Created on Sep 18, 2014

@author: Seyed Iman Mirzaei
'''

#import numpy as np
import numpy as np
import scipy.optimize as opt
print('JJObject v1.0.0')

class junction():
    version='1.0.0'
    def __init__(self, Tc, T, Area, RnUnit,
                 CjUnit, Cground):

    #def __init__(self, Tc=1.23, T=20e-3, Area=4e-13, RnUnit=1300e-12,
    #            CjUnit=50e-3, Cground=30e-18):

        # Defining physical constants
        self.ec_ = 1.602176565e-19  # elementary charge (C)
        self.c_ = 299792458  # speed of light (m.s^-1)
        self.eps0_ = 8.854187817e-12  # electric constant (F.m^-1)
        self.mu0_ = 4*np.pi*1e-7  # magnetic constant (N.A^-2)
        self.h_ = 6.62606957e-34  # planck constant (J.s)
        self.hbar_ = self.h_/(2*np.pi)  # reduced planck constant (J.s)
        self.kb_ = 1.3806488e-23  # Boltzman coefficient (J.K^-1)
        self.Phi0_ = self.h_/(2*self.ec_)  # magnetic flux quanta (Wb)

        # Defining junction constants from parameters passed to the object
        self.Tc = Tc  # critical temperature in kelvin
        self.T = T  # junction temperature in kelvin
        self.Area = Area  # junction area (m^2)
        self.RnUnit = RnUnit  # normal state resistance of the junction per unit area (ohm)
        self.CjUnit = CjUnit  # junction capacitance per unit area (Farads)
        self.Cground = Cground

        # Calculating junction properties
        self.update()

    def update(self, Tc=None, T=None, Area=None, RnUnit=None, CjUnit=None, Cground=None):
        if not Tc is None :
            self.Tc = Tc
        if not T is None :
            self.T = T
        if not Area is None :
            self.Area = Area
        if not RnUnit is None :
            self.RnUnit = RnUnit
        if not CjUnit is None :
            self.CjUnit = CjUnit
        if not Cground is None :
            self.Cground = Cground

        self.Delta0 = self.calcDelta0(self.Tc)
        self.Delta = self.calcDelta(self.T, self.Tc, self.Delta0)
        self.Rn = self.calcRn(self.Area, self.RnUnit)
        self.Cj = self.calcCj(self.Area, self.CjUnit)
        self.Ic = self.calcIc(self.Rn,self.Delta,self.T)
        self.Ej = self.calcEj(self.Ic)
        self.Ec = self.calcEc(self.Cj)
        # in the following case, the junction current is assumed to be a thousand
        # times smaller than the critical current
        self.Lj = self.calcLj_lowCurr(self.Ic, self.Ic*1e-3)
        self.F0 = self.calcF0(self.Lj, self.Cj)

    def calcDelta0(self,Tc):
        ''' This function calculates the zero kelvin gap value based on BCS theory'''
        return 1.764*self.kb_*Tc  # gap value in Joules

    def calcDelta(self,T,Tc,d0):
        ''' This function calculates the finite-temperature gap based on a simplified equation that is more valid around 0 and Tc'''
        return d0*np.tanh((np.pi/1.764)*np.sqrt(0.95333*((Tc/T)-1)))  # gap value in Joules

    def calcRn(self,Area,RnUnit):
        return RnUnit/Area

    def calcCj(self,Area,CjUnit):
        return CjUnit*Area

    def calcIc(self,Rn,Delta,T):
        '''This function calculates the junction critical current based on Ambegaokar-Baratoff relation [Ambegaokar1963]
        '''
        return (np.pi*Delta/(2*self.ec_*Rn))*np.tanh(Delta/(2*self.kb_*T))

    def calcEj(self,Ic):
        return self.h_*Ic/(4*self.ec_*np.pi)

    def calcEc(self,Cj):
        return self.ec_**2/(2*Cj)

    def calcLj_lowCurr(self,Ic,I):
        return self.Phi0_/(2*np.pi*Ic*np.sqrt(1-(I/Ic)**2))

    def calcF0(self,Lj,Cj):
        return 1/(2*np.pi*np.sqrt(Lj*Cj))

    def junctionInfo(self,PrintInfo=True):
        self.update()
        txt = ('\n\nJunction Information: \n' +
              '\n=================================================================' +
              '\n* F0 = ' + '%0.2f' % (self.F0/1e9) + ' GHz   Plasma frequency ' +
              '\n* Delta('+str(self.T)+'K) = ' + '%0.2f' % (self.Delta/(self.h_*1e9)) + ' GHz   Gap value'
              '\n* ic(' + str(self.T) + 'K) = ' + '%0.3f' % (self.Ic*1e-6/self.Area) + ' uA/um^2   Critical current density'
              '\n* Ic(' + str(self.T) + 'K) = ' + '%0.2f' % (self.Ic*1e9) + ' nA  Critical current'
              '\n*----------------------------------------------------------------'
              '\n* Cj = ' + '%0.3f' % (self.Cj*1e15) + ' fF'
              '\n* Lj = ' + '%0.3f' % (self.Lj*1e9) + ' nH'
              '\n* Rjn = ' + '%0.3f' % (self.Rn/1e3) + ' kOhm'
              '\n*----------------------------------------------------------------'
              '\n* Ej = ' + '%0.3f' % (self.Ej/(self.h_*1e9)) + ' GHz'
              '\n* Ec = ' + '%0.3f' % (self.Ec/(self.h_*1e9)) + ' GHz'
              '\n* Ej/Ec = ' + '%0.3f' % (self.Ej/self.Ec) +
              '\n================================================================= \n\n')

        if PrintInfo is True:
            print(txt)

        return txt

class array():
    def __init__(self, junction, Cs, N):
        self.h_ = 6.62606957e-34  # planck constant (J.s)
        self.kb_ = 1.3806488e-23  # Boltzman coefficient (J.K^-1)

        self.j = junction
        self.Cs = Cs
        self.N = N

        self.update()

    def update(self, junction = None, N = None, Cs = None):

        if not junction is None :
            self.j = junction
        if not N is None :
            self.N = N
        if not Cs is None :
            self.Cs = Cs

        self.slipRate = self.calcSlipRate(self.j.Ej,self.j.Ec,self.N)
        self.unloadedModes = self.calcModesUnloaded(self.j.F0, np.arange(1,np.floor(self.N/2)), self.N, self.j.Cj, self.j.Cground)
        self.loadedModes = self.calcModesLoaded(self.j.F0, np.arange(1,np.floor(self.N/2)), self.N, self.j.Cj, self.j.Cground,self.Cs,self.j.Ec,self.j.Ej)
        self.thermalPop = self.calcThermalPop(self.slipRate, self.j.T)

    def calcSlipRate(self,Ej,Ec,N):
        return ((1/self.h_)*N*16
                *np.sqrt((Ej*Ec/np.pi))
                *(Ej/(2*Ec))**0.25
                *np.exp(-np.sqrt(8*Ej/Ec)))

    def calcModesUnloaded(self,f0,n,N,Cj,C0):
        return f0*np.sqrt((1-np.cos(np.pi*n/N))/(1-np.cos(np.pi*n/N)+C0/(2*Cj)))

    def calcModesLoaded(self,f0,n_vec,N,C_j,C_0,C_s,E_c,E_j):
        #eq_even = lambda omega_l: -1020.0*np.sqrt(2)*C_s*omega_l*np.sqrt(E_c/(E_j*(-np.cos(np.pi*n/N) + 1)*(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))) - np.tan(np.pi*n*omega_l/(2*f0*np.sqrt((-np.cos(np.pi*n/N) + 1)/(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))))
        #eq_odd = lambda omega_l: -np.tan(np.pi*n*omega_l/(2*f0*np.sqrt((-np.cos(np.pi*n/N) + 1)/(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1)))) + 0.000490196078431373*np.sqrt(2)/(C_s*omega_l*np.sqrt(E_c/(E_j*(-np.cos(np.pi*n/N) + 1)*(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))))

        sol = np.array([])
        eps = 1e2

        n_vec = np.int_(n_vec)
        for n in n_vec:

            if n%2==0: # even numbers
                #eq = lambda w_l_num: eq_even_num(nn,N_num,C0_num,Cj_num,Cs_num,w0_num,Ec_num,Ej_num,w_l_num)
                eq = lambda omega_l: -1020.0*np.sqrt(2)*C_s*omega_l*np.sqrt(E_c/(E_j*(-np.cos(np.pi*n/N) + 1)*(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))) - np.tan(np.pi*n*omega_l/(2*f0*np.sqrt((-np.cos(np.pi*n/N) + 1)/(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))))
                sol = np.append(sol,opt.ridder(eq,self.unloadedModes[n-1]*(1-1/n)+eps,self.unloadedModes[n-1]-eps))
            else:
                #eq = lambda w_l_num: eq_odd_num(nn,N_num,C0_num,Cj_num,Cs_num,w0_num,Ec_num,Ej_num,w_l_num)
                eq = lambda omega_l: -np.tan(np.pi*n*omega_l/(2*f0*np.sqrt((-np.cos(np.pi*n/N) + 1)/(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1)))) + 0.000490196078431373*np.sqrt(2)/(C_s*omega_l*np.sqrt(E_c/(E_j*(-np.cos(np.pi*n/N) + 1)*(C_0/(2*C_j) - np.cos(np.pi*n/N) + 1))))
                if n==1:
                    sol = np.append(sol,opt.ridder(eq,eps,self.unloadedModes[n-1]-eps))
                else:
                    sol = np.append(sol,opt.ridder(eq,self.unloadedModes[n-1]*(1-2/n)+eps,self.unloadedModes[n-1]-eps))

        return sol

    def calcThermalPop(self,slipRate,T):
        return np.exp(-2*slipRate*self.h_/(self.kb_*T))/(1 + np.exp(-2*slipRate*self.h_/(self.kb_*T)))*100

    def arrayInfo(self,PrintInfo=True):
        self.update()
        txt =('\n\nArray Information: \n' +
              '\n=================================================================' +
              '\n* Cs = ' + '%0.3f' % (self.Cs*1e15) + 'fF' +
              '\n* Cj/C0 = ' + '%0.3f' % (self.j.Cj/self.j.Cground) +
              '\n* Lj_array = ' + '%0.3f' % (1e9*self.N*self.j.Lj) + ' nH' +
              '\n* Ej_array = ' + '%0.3f' % (1e-9*self.j.Ej/(self.h_*self.N)) + ' GHz' +
              '\n* First unloaded mode = ' + '%0.3f' % (1e-9*self.unloadedModes[0]) + ' GHz    (K=1)' +
              '\n* First loaded mode = ' + '%0.3f' % (1e-9*self.loadedModes[0]) + ' GHz    (K=1)' +
              '\n* Phase slipRate = ' + '%0.2e' % (self.slipRate) + ' Hz' +
              '\n* Qubit thermal population = ' + '%0.2f' % (self.thermalPop) + ' %' +
              '\n================================================================= \n\n'
              )
        if PrintInfo is True:
            print(txt)

        return txt