import os, sys
import numpy
import math

try:
    from pyspatialite import dbapi2 as db
    import sqlite3
except:
    import sqlite3

try:
    from osgeo import ogr
except:
    import osr
try:
    from osgeo import osr
except:
    import ogr

import matplotlib.pyplot as plt
from matplotlib.pylab import *
from matplotlib.font_manager import FontProperties
import csv

def DamBreakHyrograph(VolumeInvaso,AltezzaDiga,LarghezzaSezioneRottura,NomeDiga):
    """
    Evaluation of the dambreak hydrograph:
    -------------------------------------
    VolumeInvaso            : Reservoir volume in millions of cubic meters
    AltezzaDiga             : height of the dam in meters
    LarghezzaSezioneRottura : Width of the breach in meters
    NomeDiga                : name of the dam
    """
    # form Mmc to mc
    VolumeInvaso=VolumeInvaso*10**6

    # reservoir volume curve : V=k*h^alfa
    # ====================================
    alfa=1.5
    kinvaso=VolumeInvaso/(AltezzaDiga**alfa)

    Vcurr=VolumeInvaso
    Vfin=VolumeInvaso*0.001

    # flow calculation for instant collapse
	# ====================================
	# gravity acceleration
    G=9.81

    Yc=4.0/9.0*AltezzaDiga

    Qmax=8.0/27.0*LarghezzaSezioneRottura*AltezzaDiga*(G*AltezzaDiga)**0.5

    Vmax=Qmax/(Yc*LarghezzaSezioneRottura)

    # Total theoretical energy height in the hypothesis of permanent motion
    Htot=Yc+Vmax**2/2./G
    # volume
    Vol_Htot=kinvaso*Htot**alfa

    dt=60.0
    Idrogramma=[]
    Time=[]
    T=0.0
    Time.append(T)
    Idrogramma.append(Qmax)

    while Vcurr>=Vol_Htot:
        dv=-Qmax*dt
        # update volume
        Vcurr+=dv
        if Vcurr<0.0:
            while Vcurr<0.0:
                dt=dt/2.0
                Vcurr+=-dv
                dv=-Qmax*dt
                Vcurr+=dv
        Idrogramma.append(Qmax)
        T+=dt
        Time.append(T)

    alfa_1=1.0/alfa
    # water depth
    hcurr=math.pow((Vcurr/kinvaso),alfa_1)

    hmin=0.5

    while hcurr>hmin:
        # critical depth for rectangular section
        Yc=2.0/3.0*hcurr
        # critical speed for rectangular section
        Uc=math.sqrt(G*Yc)
        # critical flow rate for rectangular section
        Qc=Uc*Yc*LarghezzaSezioneRottura
        # volume released in time unit
        dv=-Qc*dt
        # update the volume at the end of dt
        Vcurr+=dv
        if Vcurr<0.0:
            while Vcurr<0.0:
                dt=dt/2.0
                Vcurr+=-dv
                dv=-Qc*dt
                Vcurr+=dv
        Idrogramma.append(Qc)
        T+=dt
        Time.append(T)
        # update the water depth at the end of dt
        hcurr=math.pow((Vcurr/kinvaso),alfa_1)

    # calculate hydrogram volume
    Vol=0.0
    nn=len(Idrogramma)
    for i in range(1,nn):
        Vol+=(Time[i]-Time[i-1])*(Idrogramma[i]+Idrogramma[i-1])/2.0

    grafico=0
    if grafico>0:
        try:
            plt.subplot(1, 1, 1)

            plt.plot(Time,Idrogramma)

            plt.grid(True)
            txt='Dam: %s  - Dam-Break discharge' % NomeDiga
            plt.title(txt)
            plt.xlabel('Time (sec)')
            plt.ylabel('discharge (cm/s)')
            plt.legend()
            plt.show()
        except:
            pass

    return Idrogramma, Time

def ValoreInX(x,n,datix,datiy):

    nx=len(datix)
    ny=len(datiy)
    if nx==ny:
        if x>datix[0]:
            if x<datix[-1]:
                for i in range(1,nx):
                    if datix[i]>=x:
                        y=datiy[i-1]+(datiy[i]-datiy[i-1])*(x-datix[i-1])/(datix[i]-datix[i-1])
                        break
            else:
                y=datiy[-1]
        else:
            y=datiy[0]
    else:
        sys.exit()

    return y

def RunCinemat(mydb_path_user,DamID,PathFiles,grafico2):

    """
    Run Cinemat model for routing flood downstream
    """

    pythonver_log=sys.version
    ppp=pythonver_log.split(' ')
    py_ver=ppp[0][0:1]

    NotErr=bool('True')
    errMsg='OK'

    PathFiles=os.path.realpath(PathFiles)

    if not os.path.exists(PathFiles):
        errMsg = "Non ci sono dati per la diga num =%s \nEffettuare prima il calcolo delle sezioni a valle !" % (DamID)
        NotErr= bool()
        return NotErr, errMsg

    FileOutput=PathFiles+os.sep+'Q_H_max.csv'

    # =========================================
    # apertura del database sqlite
    # =========================================

    conn = sqlite3.connect(mydb_path_user, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
   # import extention
    conn.enable_load_extension(True)
    conn.execute('SELECT load_extension("mod_spatialite")')

    # creating a Cursor
    cur = conn.cursor()

    NomeTabella='DAMS'

    sql='SELECT ResVolMcm,Height_m,BreachWidth,Name FROM %s WHERE DamID=%d' % (NomeTabella,DamID)
    cur.execute(sql)
    DatiDiga=cur.fetchone()

    if DatiDiga!=None:

        VolumeInvaso=float(DatiDiga[0])

        # height of the dam
        AltezzaDiga = float(DatiDiga[1])

        # Width of the breach
        LarghezzaSezioneRottura = float(DatiDiga[2])

        # Name of the dam
        NomeDiga=DatiDiga[3]

    else:
        errMsg = 'In the table= %s there is no data for the dam num =%s \nPerform data saving first !' % (NomeTabellaTest,DamID)
        NotErr= bool()
        return NotErr, errMsg

    # reading the data for the calculation
    NomeTabellaMatrice='MatriceAexp'

    sql='SELECT '
    sql+=' PixDist'
    sql+=', progr_lungo_fiume'
    sql+=', distanza_fiume'
    sql+=', distanza_linea_retta'
    sql+=', pend'
    sql+=', ka'
    sql+=', ma'
    sql+=', kq'
    sql+=', mq'
    sql+=', kcel'
    sql+=', mcel'
    sql+=' FROM %s' % NomeTabellaMatrice
    sql+=' WHERE DamID=%d' % (DamID)
    sql+=' ORDER BY PixDist;'
    cur.execute(sql)
    MatriceDati=cur.fetchall()

    ListaTratti=[]
    Progressive_fiume=[]
    Distanze_fiume=[]
    # coefficient matrix: ka;ma;kq;mq;kcel;mcel for every reach
    MatriceCoeff=[]
    for row in MatriceDati:
        ListaTratti.append(int(row[0]))
        Progressive_fiume.append(float(row[1]))
        Distanze_fiume.append(float(row[2]))
        MatriceCoeff.append(row[3:])

    # Calculation of the flood hydrograph
    # ................................

    Idrogramma, T = DamBreakHyrograph(VolumeInvaso,AltezzaDiga,LarghezzaSezioneRottura,NomeDiga)

    # the first input corresponds to the hydrograph at the initial section
    Qin=numpy.array(Idrogramma,dtype =numpy.float)
    maxQ=Qin.max()

    nn=len(MatriceCoeff)
    x=0.0

    numtime=len(Qin)

    # lists of times, distances and the envelope of the maximum flow along the river
    TQX=[]
    XQX=[]
    QX=[]
    # Water depth
    HX=[]
    # water width
    BX=[]
    # water velocity
    VX=[]

    TrattoX=[]
    PendX=[]
    TQX.append(T[0])
    XQX.append(0.0)
    Progr_fiume=[]
    Progr_fiume.append(0.0)

    QX.append(Qin[0])
    TrattoX.append(0)
    PendX.append(MatriceCoeff[0][1])

    # coeff. and exponent of the formula monomer of the flow Q=kq*h^mq
    kq=float(MatriceCoeff[0][4])
    mq=float(MatriceCoeff[0][5])
    invmq=1.0/mq
    h_0=math.pow(Qin[0]/kq,invmq)
    HX.append(h_0)

    # water width = ka*ma*h^(ma-1)
    # ----------------------------------------------------
    ka=float(MatriceCoeff[0][2])
    ma=float(MatriceCoeff[0][3])

    # water width calculation
    mb=ma-1.0
    b_0=ka*ma*math.pow(h_0,mb)
    BX.append(b_0)

    # velocity
    # ------------------
    A_0=ka*math.pow(h_0,ma)
    if A_0>0:
        V_0=Qin[0]/A_0
    else:
        V_0=0.0

    VX.append(V_0)

    # settings for the chart
    #============================
    fontP = FontProperties()
    fontP.set_size('small')

    Progr_fiume=[]
    Progr_fiume.append(0.0)

    # cycle along the river reaches
    # -------------------------------
    for i in range(nn):

        # abscissa and instants of the initial point of the previous reach
        xini=XQX[-1]
        xini_fiume=Progr_fiume[-1]

        tini=TQX[-1]
        # abscissa from the beginning of reach
        x=0.0
        xfronte=0.0

        # kx and mx coefficients of geometric and hydraulic reach parameters
        tratto=ListaTratti[i]

        # distance in a straight line: used for calculating the propagation
        distanza=float(MatriceCoeff[i][0])

        # reading distance on the river
        Progr_curr=Progressive_fiume[i]
        Parz_curr=Distanze_fiume[i]
        # relationship between the distance along the river axis and the distance in a straight line
        Amplificazione=Parz_curr/distanza

        # slope
        pend=float(MatriceCoeff[i][1])
        # coeff. and exponent of the area monomular formula
        ka=float(MatriceCoeff[i][2])
        ma=float(MatriceCoeff[i][3])
        # coeff. and exponent of the monomer flow rate formula Q=kq*h^mq
        kq=float(MatriceCoeff[i][4])
        mq=float(MatriceCoeff[i][5])
        # coeff. and exponent of the monomy formula of celerity
        kcel=float(MatriceCoeff[i][6])
        mcel=float(MatriceCoeff[i][7])

        # parameters for calculating the water width B=dA/dh
        # ----------------------------------------------------------------
        # B=ka*ma*h^(m-1) = kb*h^mb
        kb=ka*ma
        mb=ma-1.0

        # calculation of the  Dt
        # ......................
        t=T[0]
        tau=T[0]
        # calculate the corresponding water depth
        invmq=1.0/mq

        for k in range(1,numtime):

            dtau=float(T[k]-T[k-1])

            Qtau=Qin[k-1]
            Qtau1=Qin[k]

            h_tau=math.pow(Qtau/kq,invmq)
            h_tau1=math.pow(Qtau1/kq,invmq)
            #  dQ/dA for tau cost = celerity
            Qprimo=kcel*math.pow(h_tau,mcel)
            # dQ/dA^2 for tau cost = derivative of celerity
            mcel2=mcel-1.0
            Qseconda=kcel*mcel*math.pow(h_tau,mcel2)
            # computation A(tau)
            Atau=ka*math.pow(h_tau,ma)
            Atau1=ka*math.pow(h_tau1,ma)
            # computation dA/dtau
            dA=(Atau1-Atau)/dtau
            # Vfronte calculation that on dry riverbed (ie Qo = 0) is Vfronte = Qtau / Atau
            Vfronte=Qtau/Atau
            # then dt calculation
            numerat=Qseconda*dA*x/Qprimo-Qprimo
            denom=-Qprimo+Vfronte
            dt=numerat/denom*dtau
            # dx calculation
            dx=dt*Vfronte

            # check the distance traveled
            if (x+dx)>distanza:
                # calculates the times to reach the end of the section
                dx1=distanza-x
                dt1=dx1/Vfronte
                dtau1=denom/numerat*dt1
                t1=t+dt1
                tau1=tau+dtau1
                # flow at instant tau1
                Q1=ValoreInX(tau1,numtime,T,Qin)
                h_1=math.pow(Q1/kq,invmq)
                # calculating the water width
                b_1=kb*math.pow(h_1,mb)

                # calculating celerity
                c1=kcel*math.pow(h_1,mcel)

                # calculates the hydrograph leaving the reach
                Tout=[]
                Qout=[]
                Tout.append(t1)
                Qout.append(Q1)

                # inserts a new point in the TQX curve
                TQX.append(t1)
                x1=xini+distanza
                XQX.append(x1)

                # progressive distance upgrade along the river axis
                x1_fiume=xini_fiume+distanza*Amplificazione
                Progr_fiume.append(x1_fiume)

                # save the results in the respective lists
                QX.append(Q1)
                HX.append(h_1)
                BX.append(b_1)
                A_1=ka*math.pow(h_1,ma)
                if A_1>0:
                    V_1=Q1/A_1
                else:
                    V_1=0.0
                VX.append(V_1)

                TrattoX.append(tratto)
                PendX.append(pend)

                # look for the next point in tau1 in the hydrograph
                for kk in range(1,numtime):
                    if T[kk]>tau1:
                        k1=kk
                        break
                for kk in range(k1,numtime):
                    dttau=T[kk]-tau1
                    tau1=tau1+dttau
                    Q2=ValoreInX(tau1,numtime,T,Qin)
                    h_2=math.pow(Q2/kq,invmq)
                    # calculating celerity
                    c2=kcel*math.pow(h_2,mcel)
                    # time difference of the two points of the hydrograph at the end of the section
                    dtt=(1.0/c2-1.0/c1)*dx1+dttau

                    t1=t1+dtt
                    Q1=Q2*1.0
                    c1=c2*1.0
                    Tout.append(t1)
                    Qout.append(Q1)
                break

            else:
                # case in which the end of reach was not reached
                x=x+dx
                tau=tau+dtau
                t=t+dt
                # inserts a new point in the TQX curve
                TQX.append(t)
                x1=xini+x
                XQX.append(x1)
                # progressive distance upgrade along the river axis
                x1_fiume=xini_fiume+x*Amplificazione
                Progr_fiume.append(x1_fiume)

                Q1=ValoreInX(tau,numtime,T,Qin)
                h_1=math.pow(Q1/kq,invmq)
                # water width calculation
                b_1=kb*math.pow(h_1,mb)

                # save results
                QX.append(Q1)
                HX.append(h_1)
                BX.append(b_1)
                A_1=ka*math.pow(h_1,ma)
                if A_1>0:
                    V_1=Q1/A_1
                else:
                    V_1=0.0
                VX.append(V_1)

                TrattoX.append(tratto)
                PendX.append(pend)

                # -----------------------------------
                # check if the hydrograph has ended
                # -----------------------------------
                if k> (numtime-2):

                    # calculates the times to reach the end of reach
                    dx1=distanza-x
                    dt1=dx1/Vfronte
                    dtau1=denom/numerat*dt1
                    t1=t+dt1
                    tau1=tau+dtau1
                    # flow at instant tau1
                    Q1=ValoreInX(tau1,numtime,T,Qin)
                    h_1=math.pow(Q1/kq,invmq)
                    # water width calculation
                    b_1=kb*math.pow(h_1,mb)

                    # calculate the celerity
                    c1=kcel*math.pow(h_1,mcel)

                    # calculates the hydrograph leaving the reach
                    Tout=[]
                    Qout=[]
                    Tout.append(t1)
                    Qout.append(Q1)

                    # inserts a new point in the TQX curve
                    TQX.append(t1)
                    x1=xini+distanza
                    XQX.append(x1)

                    # progressive distance upgrade along the river axis
                    x1_fiume=xini_fiume+distanza*Amplificazione
                    Progr_fiume.append(x1_fiume)

                    # save the results in the respective lists
                    QX.append(Q1)
                    HX.append(h_1)
                    BX.append(b_1)
                    A_1=ka*math.pow(h_1,ma)
                    if A_1>0:
                        V_1=Q1/A_1
                    else:
                        V_1=0.0
                    VX.append(V_1)

                    TrattoX.append(tratto)
                    PendX.append(pend)

                    # extends the length of the hydrograph
                    Tout.append(t1+1000.0)
                    Qout.append(Q1)

        grafico1=0
        if grafico1>0:
            # praph
            fig1 = figure()

            ax1 = fig1.add_subplot(111)
            ax1.plot(T,Qin,'-')
            ax1.plot(Tout,Qout,'-')
            nomi=[]
            nomi.append('Inflow')
            nomi.append('Outflow')

            PosizoneLegenda = 'best'
            leg = ax1.legend((nomi),
                       PosizoneLegenda, shadow=True,prop = fontP)
            ax1.grid(True)
            ax1.set_ylabel('Q (mc/s)')
            ax1.set_xlabel('Time (hrs)')
            Titolo='CINE method applied to reach %d distance pixel n: %d  x=%.3f' % (i+1,tratto,x)

            suptitle(Titolo, fontsize=16,fontstyle='italic')

            show()

        #assigns the outgoing idrograph to input to the next reach
        Qin=numpy.array(Qout,dtype =numpy.float)
        T=numpy.array(Tout,dtype =numpy.float)
        numtime=len(T)

    if grafico2>0:

        # graph QX
        fig1 = figure()

        ax1 = fig1.add_subplot(111)
        ax1.plot(Progr_fiume,QX,'o-')
        nomi=[]
        nomi.append('QX')

        ax2 = ax1.twinx()
        ax2.plot(Progr_fiume,HX,'-r')

        nomi2=[]
        nomi2.append('HX')

        PosizoneLegenda = 'upper left'
        leg = ax1.legend((nomi), loc=PosizoneLegenda,
         shadow=True,prop = fontP)
        PosizoneLegenda2 = 'upper right'
        leg2 = ax2.legend((nomi2), loc=PosizoneLegenda2,
         shadow=True,prop = fontP)

        ax1.grid(True)
        ax1.set_ylabel('Qmax (mc/s)')
        ax2.set_ylabel('Hmax (m)')
        ax1.set_xlabel('x (m)')
        Titolo='Peak flow rate reduction with distance'

        suptitle(Titolo, fontsize=16,fontstyle='italic')

        show()

    # save the results
    # --------------------------

    NomeTabella='Q_H_max'

    # cancel any previous data
    # ---------------------------------
    sql='DELETE FROM %s WHERE DamID=%d' % (NomeTabella,DamID)
    cur.execute(sql)
    conn.commit()

    fout=open(FileOutput,'w')
    txt='PixDist;Progr_fiume;Progr_retta;pend;Qmax;Hmax;Bmax;Vmax;Time\n'
    fout.write(txt)
    n2=len(QX)
    for i in range(n2):
        # salva i risultati
        txt='%d;%.2f;%.2f;%s;%.2f;%.2f;%.2f;%.2f;%.2f\n' %(TrattoX[i],Progr_fiume[i],XQX[i],PendX[i],QX[i],HX[i],BX[i],VX[i],TQX[i])
        fout.write(txt)

        sql='INSERT INTO %s (DamID' % (NomeTabella)
        sql_value=') VALUES (%d' % DamID
        sql_value+=',%d,%.2f,%.2f,%s,%.2f,%.2f,%.2f,%.2f,%.2f' %(TrattoX[i],Progr_fiume[i],XQX[i],PendX[i],QX[i],HX[i],BX[i],VX[i],TQX[i])
        sql+=', PixDist'
        sql+=', Progr_fiume'
        sql+=', Progr_retta'
        sql+=', pend'
        sql+=', Qmax'
        sql+=', Hmax'
        sql+=', Bmax'
        sql+=', Vmax'
        sql+=', Time'
        sql+='%s' % sql_value
        sql+=');'
        cur.execute(sql)

    fout.close()

    conn.commit()

    # Close communication with the database
    cur.close()
    conn.close()

    return NotErr, errMsg

if __name__ == '__main__':

    mydb_path_user='..'+ os.sep+'db'+os.sep+'USER_GeoDB.sqlite'

    # San Giuliano
    DamID=449
    PathFiles='..'+ os.sep+ str(DamID)

    grafico2=1
    NotErr, errMsg= RunCinemat(mydb_path_user,DamID,PathFiles,grafico2)

    print(NotErr,errMsg)


import datetime

import matplotlib.dates as md
import netCDF4
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

'''Some helper routines to fix data'''

def fillTimeAndPowerVector(inptTime, spacing, inptPwr):
    outputTime = []
    outputPwr = []
    totDataPntsAdded = 0
    # Need to fill in gaps in the time vectors. Normally, time stamps are 15min (900 sec) apart, if this is not the case
    # missing time stamps need to be added.
    # This assumes good data in the first and last time stamp, and it does not check for equal spacing otherwise.
    for idx, ts in enumerate(inptTime):
        # Skip first entry, just use as is
        if idx == 0:
            outputTime.append(ts)
            outputPwr.append(inptPwr[idx])
        # Skip last entry, just use as is
        elif idx == inptTime.size - 1:
            outputTime.append(ts)
            outputPwr.append(inptPwr[idx])
        # Check if there is a gap greater 900 sec. If so, see how many multiples of 900 fit this gap and add these time
        # stamps.
        elif inptTime[idx + 1] - ts > spacing:
            outputTime.append(ts)
            outputPwr.append(inptPwr[idx])
            dy = inptPwr[idx + 1] - inptPwr[idx]
            dx = inptTime[idx + 1] - ts
            slope = dy / dx
            delta = int(np.floor((inptTime[idx + 1] - ts) / spacing))
            for i in range(1, delta):
                outputTime.append(inptTime[idx] + i * spacing)
                outputPwr.append(inptPwr[idx] + slope * i * spacing)
                totDataPntsAdded = totDataPntsAdded + 1
        # All other cases, just take what's already there.
        else:
            outputTime.append(ts)
            outputPwr.append(inptPwr[idx])

    print('Total data points added : ', totDataPntsAdded)
    return outputTime, outputPwr

'''LoadData helps with repeatedly loading data from well formed netCDF files, i.e., such files with a 'value' 'time'
    tuple.
    THIS ROUTINE IGNORES SCALING AT THIS POINT
'''

def loadData(filePath):
    nc = netCDF4.Dataset(filePath)
    value = np.asarray(nc.variables['value'])
    time = np.asarray(nc.variables['time'])

    # simplify
    value = value[0, :]
    time = time[0, :]
    timeStamps = [datetime.datetime.fromtimestamp(ts) for ts in time]

    startDate = datetime.datetime.fromtimestamp(time[0])
    endDate = datetime.datetime.fromtimestamp(time[-1])

    return value, time, timeStamps, startDate, endDate

# import time series

# St. Mary's total load
stmLoadP, stmTime, stmTimeStamps, stmStart, stmEnd = loadData(
    '../../GBSProjects/StMary/InputData/TimeSeriesData/RawData/StMaryTotalizerLoadkW.nc')

# Mt. Village's total load
mtvLoadP, mtvTime, mtvTimeStamps, mtvStart, mtvEnd = loadData(
    '../../GBSProjects/StMary/InputData/TimeSeriesData/RawData/MountainVillageTotalizerLoadkW.nc')
print('Patching Mt. Village Data...') #Needs to be patched first to yield useable master timestamp vector
mtvTime, mtvLoadP = np.array(fillTimeAndPowerVector(np.array(mtvTime[:]), 900, mtvLoadP))

# Clip Mt. Village TS to one year length using most recent year
mtvTime = mtvTime[-35040:]
mtvLoadP = mtvLoadP[-35040:]
mtvStart = datetime.datetime.fromtimestamp(mtvTime[0])
print('Mt. Village data set clipped to: Start date : ', mtvStart, ' End date: ', mtvEnd)
stmDt = stmTime[1:] - stmTime[:-1]
mtvDt = mtvTime[1:] - mtvTime[:-1]

# Adjust length of stmLoadP and stmTime vectors based on available data in Mt. Village TS
stmLoadP = stmLoadP[stmTime > mtvTime[-35040] - 1]
stmTime = stmTime[stmTime > mtvTime[-35040] - 1]
stmStart = datetime.datetime.fromtimestamp(stmTime[0])
print('St. Mary\'s data set clipped to: Start date : ', stmStart, ' End date: ', stmEnd)

print('Patching St. Mary\'s Data...')
stmTime, stmLoadP = np.array(fillTimeAndPowerVector(stmTime, 900, stmLoadP))
# Need to fix time stamps vector
stmTimeStamps = [datetime.datetime.fromtimestamp(ts) for ts in stmTime]

# Calculate total combined load
print('Calculating total combined load...')
totLoadP = stmLoadP + mtvLoadP
print('Peak load :', np.nanmax(totLoadP), ' kW')
print('Average combined load: ', np.nanmean(totLoadP), ' kW')
print('Standard deviation from average: ', np.nanstd(totLoadP), ' kW')

# Wind power data set
wtgP, wtgTime, wtgTimeStamps, wtgStart, wtgEnd = loadData(
    '../../GBSProjects/StMary/InputData/TimeSeriesData/RawWindData/Calc50mPowerDW52.nc')

# fix any data gaps
print('Patching wind power data...')
wtgTime, wtgP = np.array(fillTimeAndPowerVector(wtgTime, 600, wtgP))
wtgStart = datetime.datetime.fromtimestamp(wtgTime[0])
wtgEnd = datetime.datetime.fromtimestamp(wtgTime[-1])
print('Wind power data range: Start date: ', wtgStart, ' to ', wtgEnd )

# Need to reorganize the time series so that it starts where the load time-series start
# Entries 0 through 11520 need to appended at the end
wtgTime1 = wtgTime[:11521]
wtgTime2 = wtgTime[11521:]
wtgTime = np.append(wtgTime1, wtgTime2, axis=0)
wtgP1 = wtgP[:11521]
wtgP2 = wtgP[11521:]
wtgP = np.append(wtgP1, wtgP2, axis=0)
# Cut down to one year length
wtgP = wtgP[:52560]
wtgPSize10 = wtgP.size

# need to resample the time series to fit with the load data
# get actual time stamps
wtgTimeStamps = [datetime.datetime.fromtimestamp(ts) for ts in wtgTime[:52560]]
wtgSeries = pd.Series(wtgP, index=wtgTimeStamps)
wtgShrt = wtgSeries.resample('15T').mean()

wtgP = np.array(wtgShrt)
# Fill NaN with 0
wtgP[np.isnan(wtgP)] = 0
print('Wind power data set re-sampled from 10 to 15 min intervals. Size changed from: ', wtgPSize10, 'to ', wtgP.size)

genP = totLoadP - wtgP
genDP = (genP[1:] - genP[:-1]) / (15 * 60)

plt.figure(figsize=(8, 5.5))

# Matplotlib formated timestamps for plotting routines.
mpTimeStamps = md.date2num(stmTimeStamps)

# discretize the profile
# Generator fleet as per 'Saint-Marys-REF8' project file
gen1PMax = 499 #Cummins QSX15G9
gen2PMax = 611 #Caterpillar 3508
gen3PMax = 908 #Caterpillar 3512
MOL = 0#.15 # NOTE THE FILE SUPPLIED BY AVEC STATES 0% (!!!)
print('Assuming: ', 100*MOL, '% MOL')
# Minimum spinning reserve kept in the system
# NOTE: this is a brute force approach, but with data available better estimates are not possible.
srcPMin = 0.15*totLoadP
srcP = np.zeros(np.shape(wtgP)) + srcPMin

#print('Assuming base SRC : ', srcPMin, 'kW')

# isolate the data where there is more wind power than demand.
curtailedP = np.copy(genP)
curtailedP[genP > MOL * gen1PMax] = np.nan
curtailedP = -(curtailedP - MOL * gen1PMax)

# Adjust SRC calculation to either be base SRC or actual wind power contribution to total load, which ever is greater.
srcP[srcP < (wtgP - curtailedP)] = wtgP[srcP < (wtgP - curtailedP)] - curtailedP[srcP < (wtgP - curtailedP)]
srcP = srcP*0

# Curtailment condition
genP[genP < MOL*gen1PMax] = MOL*gen1PMax # Normal approach with MOL
print('Max SRC : ', np.max(srcP), ' kW')
print('Min SRC : ', np.min(srcP), ' kW')
# Find which generator is online based on generator loading
def dieselSchedule(genP, MOL, srcP, gen1PMax, gen2PMax, gen3PMax):
    genPAvail = np.zeros(np.shape(genP))
    chngCnt = 0
    runHours = 0

    for i, p in enumerate(genP):
        if p <= MOL*gen1PMax and srcP[i] == 0:
            genPAvail[i] = 0
        elif p < (gen1PMax - srcP[i]):
            genPAvail[i] = gen1PMax
            runHours = runHours + 15/60
        elif p < (gen2PMax - srcP[i]):
            genPAvail[i] = gen2PMax
            runHours = runHours + 15/60
        elif p < (gen3PMax - srcP[i]):
            genPAvail[i] = gen3PMax
            runHours = runHours + 15/60
        elif p < (gen1PMax + gen2PMax - srcP[i]):
            #print('Required: ', p + srcP[i], ' Available : ', gen1PMax + gen2PMax)
            genPAvail[i] = gen1PMax + gen2PMax
            runHours = runHours + 2*15/60
        elif p < (gen1PMax + gen3PMax - srcP[i]):
            #print('1 and 3, Load: ', p, ' SRC: ', srcP[i], 'wtgP : ', wtgP[i])
            genPAvail[i] = gen1PMax + gen3PMax
            runHours = runHours + 2*15/60
        elif p < (gen2PMax + gen3PMax - srcP[i]):
            print('2 and 3, Load: ', p, ' SRC: ', srcP[i])
            genPAvail[i] = gen2PMax + gen3PMax
            runHours = runHours + 2*15/60
        elif p < (gen1PMax + gen2PMax + gen3PMax - srcP[i]):
            print('Load: ', p, ' SRC: ', srcP[i])
            genPAvail[i] = gen1PMax + gen2PMax + gen3PMax
            runHours = runHours + 3*15/60
        else:
            genPAvail[i] = -100

        if chngCnt > 0 and (genPAvail[i]-genPAvail[i-1] < 0):
            genPAvail[i] = genPAvail[i-1]

        # Prevent excessive down transitions
        if i > 3:
            if (genPAvail[i] - genPAvail[i-1]) < 0:
                chngCnt = 4
            elif chngCnt > 0:
                chngCnt = chngCnt - 1
            else:
                chngCnt = 0

    return genPAvail, runHours

genPAvail, runHours = dieselSchedule(genP, MOL, srcP, gen1PMax, gen2PMax, gen3PMax)

print('Number of diesel configuration changes : ', np.sum(np.abs(np.sign(np.diff(genPAvail)))))
print('Total generator run-hours: ', runHours, ' h')
# Capacity factor
nonZeroGenPAvail = np.copy(genPAvail)
nonZeroGenPAvail[genPAvail == 0] = np.nan
print('Diesel capacity factor :', np.nanmean(genP/nonZeroGenPAvail))
print('Diesel kWh total: ', np.nansum(genP)/4)

print('Wind power curtailed :', np.nansum(curtailedP)/4, ' kWh')
print('Wind power available :', np.nansum(wtgP)/4, ' kWh')

essEMaxRng = [ 20, 50, 75, 100, 125, 150, 175, 200, 250, 300, 400, 500, 750, 1000, 1500, 2000, 2500, 3500, 5000]#, 10000, 15000, 17500, 20000, 25000, 35000, 50000, 75000, 100000]
wpRec = np.zeros(np.shape(essEMaxRng))
essPMax = 5000
runHoursArray = np.zeros(np.shape(essEMaxRng))
dieselEReductionArray = np.zeros(np.shape(essEMaxRng))
essPMaxAct = np.zeros(np.shape(essEMaxRng))
configChngTot = np.zeros(np.shape(essEMaxRng))
genCapFac = np.zeros(np.shape(essEMaxRng))

for j, essEMax in enumerate(essEMaxRng):
    essEIn = np.zeros(np.shape(curtailedP))
    essEIn[0] = 0#curtailedP[0] / 4
    essEOut = np.zeros(np.shape(curtailedP))
    essEOut[0] = 0
    essP = np.zeros(np.shape(curtailedP))
    essETot = 0 #0.5*essEMax
    SOC = np.zeros(np.shape(curtailedP))
    recoveredWtgE = 0
    expendedWtgE = 0
    genPNew = np.copy(genP)

    for idx, wp in enumerate(curtailedP[1:]):
        SOC[idx] = essETot/essEMax
        # if there is some curtailed power, try put it in the battery at max possible rate
        if wp > 0 and essETot < essEMax:
            essEOut[idx] = 0   # charging, so the cummulative discharing counter is reset
            dE = (essEMax - essETot)     # remaining energy capacity until battery full

            # if the available energy fits, and the available power does not exceed max battery power, just charge
            if wp/4 < dE and wp < essPMax:
                essEIn[idx] = essEIn[idx-1] + (wp/4)
                essP[idx] = -wp
                recoveredWtgE = recoveredWtgE + (wp/4)
                essETot = essETot + (wp/4)
            # if the available energy fits, but we are limited by the max battery power, charge at max battery power
            elif wp/4 < dE and wp >= essPMax:
                essEIn[idx] = essEIn[idx-1] + essPMax
                essP[idx] = -essPMax
                recoveredWtgE = recoveredWtgE + essPMax/4
                essETot = essETot + essPMax/4
            # if the available energy does not fit, but the available power is fine
            elif wp/4 >= dE and 4*dE < essPMax:
                essEIn[idx] = essEIn[idx - 1] + dE
                essP[idx] = -dE*4
                recoveredWtgE = recoveredWtgE + dE
                essETot = essETot + dE
            # if the available energy does not fit, and the available power exceeds battery power
            else:
                essEIn[idx] = essEIn[idx - 1] + essPMax/4
                essP[idx] = -essPMax
                recoveredWtgE = recoveredWtgE + essPMax/4
                essETot = essETot + essPMax/4
        # if there is no excess wind power, try discharge the battery at maximum power possible
        elif np.isnan(wp) and essETot > 10:
            essEIn[idx] = 0  # discharging, so the cummulative charging counter is reset
            # if the requested power exceeds battteru power and requested energy is available, discharge at max power
            if genP[idx] > essPMax and essETot > essPMax/4:
                essETot = essETot - (essPMax/4)
                essEOut[idx] = essEOut[idx - 1] + (essPMax/4)
                essP[idx] = essPMax
                expendedWtgE = expendedWtgE + (essPMax/4)
                genPNew[idx] = genPNew[idx] - essPMax
            # if the requested power and requested energy are available, discharge at the requested power
            elif genP[idx] < essPMax and essETot > genP[idx]/4:
                essETot = essETot - (genP[idx]/4)
                essP[idx] = genP[idx]
                essEOut[idx] = essEOut[idx - 1] + (genP[idx] / 4)
                expendedWtgE = expendedWtgE + (genP[idx]/4)
                genPNew[idx] = genPNew[idx] - genP[idx]
            # if the requested power exceeds the max battery power, and the requested energy is not available, discharge at constant rate to 0 kWh
            elif (genP[idx] > essPMax and essETot < essPMax/4) or (genP[idx] < essPMax and essETot < genP[idx]/4):
                essEOut[idx] = essEOut[idx - 1] + essETot
                essP[idx] = 4*essETot
                expendedWtgE = expendedWtgE + essETot
                essETot = 0
            # if the requested power is available, but the
            #else:

            #if totLoadP[idx] - wtgP[idx] - essP[idx] - genPNew[idx] < 0:
               # print('Power balance :', totLoadP[idx] - wtgP[idx] - essP[idx] - genPNew[idx], totLoadP[idx], wtgP[idx], genPNew[idx])
    print('*****************************************')
    print('GBS Energy capacity [kWh]: ', essEMax)
    print('ESS cummulative kWh in: ', recoveredWtgE)
    print('ESS cummulative kWh out: ', expendedWtgE)
    wpRec[j] = recoveredWtgE

    # Re-calc diesel power
    genPReduced = np.copy(genP)
    essPOut = np.copy(essP)
    essPOut[essP < 0] = 0
    essPIn = np.copy(essP)
    essPIn[essP > 0] = 0
    essPIn = -essPIn
    genPReduced = genP - essPOut
    genPAvailRed, runHoursRed = dieselSchedule(genPReduced, MOL, srcP, gen1PMax, gen2PMax, gen3PMax)
    #print('Net ESS power: ', np.sum(essP)/4)
    #plt.plot(mpTimeStamps, essP)#totLoadP - wtgP - genP + curtailedP)
    #plt.plot(mpTimeStamps, genPReduced)
    print('Reduced diesel kWh: ', np.nansum(genP - genPReduced)/4)
    dieselEReductionArray[j] = np.nansum(genP - genPReduced)/4
    print('Reduced diesel run hours: ', runHoursRed)
    runHoursArray[j] = runHoursRed
    print('Number of diesel configuration changes : ', np.sum(np.abs(np.sign(np.diff(genPAvailRed)))))
    configChngTot[j] = np.sum(np.abs(np.sign(np.diff(genPAvailRed))))
    nonZeroGenPAvailRed = np.copy(genPAvailRed)
    nonZeroGenPAvailRed[genPAvailRed == 0] = np.nan
    print('Diesel capacity factor : ', np.nanmean(genPReduced/nonZeroGenPAvailRed))
    genCapFac[j] = np.nanmean(genPReduced/nonZeroGenPAvailRed)
    print('Max. ESS P: ', np.nanmax(np.abs(essP)))
    essPMaxAct[j] = np.nanmax(np.abs(essP))
    #print('Net power balance :', np.nansum(totLoadP - wtgP - essP - genPReduced))

print((np.nansum(curtailedP) / 4) / (np.nansum(wtgP) / 4), (np.nansum(wtgP) / 4))
'''#plt.plot(mpTimeStamps, genP)
#plt.plot(mpTimeStamps, genPAvail)
#plt.plot(mpTimeStamps, curtailedP)
plt.plot(mpTimeStamps, genP)
plt.plot(mpTimeStamps, essP)
plt.plot(essEMaxRng, wpRec/1000)
plt.show()

curtailedP[np.isnan(curtailedP)] = 0
curtailedPD = (curtailedP[1:] - curtailedP[:-1])/4

sns.distplot(curtailedPD)'''

# create total load time series for the powerhouse
# if gen1P.size == gen2P.size:
#     genP = gen1P + gen2P
#     print('Same size')
# else:
#     print('Size mismatch gen1P and gen2P.')
#
# if genP.size == gen3P.size:
#     genP = genP + gen3P
#
# # produce distribution of differences
# dt = time1[1:] - time1[:-1]
# meanDt = np.mean(dt)
# dGenP = genP[1:] - genP[:-1]

# sns.distplot(dt)

plt.subplots_adjust(bottom=0.2)
plt.xticks(rotation=25)
ax = plt.gca()
xfmt = md.DateFormatter('%Y-%m-%d')
ax.xaxis.set_major_formatter(xfmt)
#plt.plot(mpTimeStamps, mtvLoadP)
#plt.plot(mpTimeStamps, stmLoadP)
#plt.plot(mpTimeStamps, totLoadP)
#plt.plot(mpTimeStamps, wtgP)
#plt.plot(mpTimeStamps, genP)
#plt.hist(genP, bins='auto')
#plt.savefig('StMarysAndMtVillageLoad.eps', format = 'eps')
#plt.plot(mpTimeStamps, genP, label='GenP')
#plt.plot(mpTimeStamps, curtailedP, label= 'CurtailedP')
#plt.legend(loc='upper right')
#plt.xlabel('GBS Energy Capacity [kWh]')
#plt.ylabel('Total diesel fleet configuration changes [#/yr]')

# SRC Figure
plt.xlabel('Time')
plt.ylabel('Spinning reserve capacity required [kW]')
plt.plot(mpTimeStamps, srcP + genP)
#plt.plot(mpTimeStamps, totLoadP)
plt.plot(mpTimeStamps, genPAvail)

#plt.semilogx(essEMaxRng, wpRec/1000)
'''
vals, base = np.histogram(genDP, bins=150)
cumDist = np.cumsum(vals)
plt.plot(base[:-1], cumDist/genP.size)'''
#plt.savefig('baselineSRC.eps', format = 'eps')
plt.show()

'''
# Demo plot of loss of wind farm scenario
time = np.array(range(120))
wtgP = np.zeros(time.size, 'int')
wtgP[:15] = 100
essP = np.zeros(time.size, 'int')
x = 100 - 2 * np.array(range(15))
essP[15:30] = x
essP[30:40] = essP[29]
essP[40:60] = essP[39] - 35
x1 = essP[59] - (essP[59]/15)*np.array(range(15))
essP[60:75] = x1
genP = np.zeros(time.size, 'int')
genP[:15] = 120
genP[15:30] = 120 - x + 100
genP[30:60] = genP[29]
genP[60:75] = genP[59] - x1 + essP[59]
genP[75:] = genP[74]

plt.figure(figsize=(8, 5.5))
plt.plot(time, wtgP)
plt.plot(time, essP)
plt.plot(time, genP)
plt.savefig('lossOfWindDemo.eps', format = 'eps')
plt.show()'''

'''Helper routines go down here'''


def selection(x):
    n = len(x)
    for i in range(0, n-1):
        ordenado = True
        for j in range(i+1, n):
            if x[i]>x[j]:
                x[i], x[j] = x[j], x[i]
            print('''{} - {} - {}'''.format(i, j, x))
            ordenado = False
        if ordenado:
            return x
    return x
x = [1, 7, 8, 9, 13, 26]
print(selection(x))


import copy
from collections import OrderedDict

import pytest
import scipy
from neuraxle.hyperparams.distributions import (Boolean, Choice,
                                                FixedHyperparameter,
                                                HyperparameterDistribution,
                                                LogNormal, LogUniform, Normal,
                                                PriorityChoice, Quantized,
                                                RandInt, Uniform)
from neuraxle.hyperparams.scipy_distributions import Gaussian, Poisson
from neuraxle.hyperparams.space import (FlatDict, HyperparameterSamples,
                                        HyperparameterSpace, RecursiveDict)

HYPERPARAMS_FLAT_AND_DICT_PAIRS = [(
    # Pair 1:
    {
        "a__learning_rate": 7
    },
    {
        "a": {
            "learning_rate": 7
        }
    }),
    # Pair 2:
    ({
        "b__a__learning_rate": 7,
        "b__learning_rate": 9
    },
    {
        "b": {
            "a": {
                "learning_rate": 7
            },
            "learning_rate": 9
        }
    }),
]

@pytest.mark.parametrize("class_to_test", [RecursiveDict, HyperparameterSamples])
@pytest.mark.parametrize("flat,expected_dic", HYPERPARAMS_FLAT_AND_DICT_PAIRS)
def test_flat_to_dict_hyperparams(flat: dict, expected_dic: dict, class_to_test):
    from_flat_dic = class_to_test(flat)
    from_nested_dic = class_to_test(expected_dic)

    assert from_flat_dic == from_nested_dic
    assert from_flat_dic.to_flat_dict() == flat
    assert from_nested_dic.to_flat_dict() == flat
    assert from_nested_dic.to_nested_dict() == expected_dic
    assert from_flat_dic.to_nested_dict() == expected_dic

HYPE_SPACE = HyperparameterSpace(OrderedDict({
    "a__b__c": PriorityChoice([0, 1, False, "Test"]),
    "a__b__q__c": Quantized(Uniform(-10, 10)),
    "a__b__q__q": Quantized(Uniform(-10, 10)),
    "a__c": Choice([0, 1, False, "Test"]),
    "a__e__q__c": Choice([0, 1, False, "Test"]),
    "a__test": Boolean(),
    "d__param": RandInt(-10, 10),
    "d__u": Uniform(-10, 10),
    "e__alpha": Normal(0.0, 1.0),
    "e__f__g": LogNormal(0.0, 2.0),
    "e__other": LogUniform(0.001, 10),
    "p__could_also_be_as_fixed": FixedHyperparameter("also hey"),
    "scipy__gaussian": Gaussian(-1, 1),
    "scipy__poisson": Poisson(1.0, 2.0),
    "scipy__scipy__gaussian": scipy.stats.randint(0, 10)
}))

def test_hyperparams_space_rvs_outputs_samples():
    space = copy.deepcopy(HYPE_SPACE)

    samples = space.rvs()

    assert isinstance(samples, HyperparameterSamples)
    assert len(samples) == len(space)
    for k, v in samples.iter_flat():
        assert k in space
        assert not isinstance(v, HyperparameterDistribution)

@pytest.mark.parametrize("hd", list(HYPE_SPACE.to_flat_dict().values()))
def test_hyperparams_space_rvs_outputs_in_range(hd: HyperparameterDistribution):
    for _ in range(20):

        sample = hd.rvs()

        assert sample in hd

def test_wildcards():
    EXPECTED_WILDCARDS = [
        "*b__c",
        "*b*c",
        "*q",
        "a__c",
        "*e*c",
        "*test",
        "*param",
        "*u",
        "*alpha",
        "*g",
        "*other",
        "*could_also_be_as_fixed",
        "scipy__gaussian",
        "*poisson",
        "*scipy__gaussian",
    ]

    wildcards: FlatDict = HYPE_SPACE.to_wildcards()

    for wc, ewc in zip(wildcards.keys(), EXPECTED_WILDCARDS):
        assert wc == ewc, f"{wc} != {ewc}, but should be equal as expected."
    for wv, ewv in zip(wildcards.values(), HYPE_SPACE.to_flat_dict().values()):
        assert wv == ewv, f"{str(wv)} != {str(ewv)}, but should remain the same."


import turtle                                   #Importamos módulo que nos permite dibujar.
import time                                     #Podremos manipular las velocidades a las que se ejecuta el programa
import random                                   #Lo utilizaremos entre otras para poder ir moviendo la manzana.

posponer = 0.1                                  #Creamos variable que nos servirá para variar la velocidad entre frames de ejecución.

#Marcador
score = 0
high_score = 0

#Configuración de la ventana
wn = turtle.Screen()                            #Dibujamos la pantalla del juego.
wn.title("Juego de Snake")
wn.bgcolor("black")
wn.setup(width=600, height=600)
wn.tracer(0)                                    #(I) Parace que hace que los movimientos sean más fluidos

#Cabeza serpiente
cabeza = turtle.Turtle()                        #Dibujamos el cuadrado dentro del espacio Turtle
cabeza.speed(0)                                 #Comienza parado
cabeza.shape("square")
cabeza.color("white")
cabeza.penup()                                  #Evitamos que vaya dejando rastro al ir dibujando.
cabeza.goto(0,0)                                #Turtle dibuja desde el centro como si fueran ejes xy normales.
cabeza.direction = "stop"

#Comida
comida = turtle.Turtle()
comida.speed(0)
comida.shape("circle")
comida.color("red")
comida.penup()
comida.goto(0,100)

#Segmentos - Cuerpo serpiente
segmentos = []

#Texto
texto = turtle.Turtle()
texto.speed(0)                                  #Para que cuando abramos la pantalla ya aparezca dibujado.
texto.color("white")
texto.penup()                                   #Escondemos trazo del pincel.
texto.hideturtle()                              #Escondemos pincel.
texto.goto(0,260)
texto.write("Score: 0     High Score: 0", align="center", font=("Courier", 24, "normal"))

#Funciones
def arriba():
    cabeza.direction = "up"

def abajo():
    cabeza.direction = "down"

def derecha():
    cabeza.direction = "right"

def izquierda():
    cabeza.direction = "left"

def mov():
    if cabeza.direction == "up":
        y = cabeza.ycor()                       #Obtenemos la cordenada y de la posición de nuestra cabeza
        cabeza.sety(y + 20)                     #Le añadimos 20px.

    if cabeza.direction == "down":
        y = cabeza.ycor()
        cabeza.sety(y - 20)

    if cabeza.direction == "left":
        x = cabeza.xcor()
        cabeza.setx(x - 20)

    if cabeza.direction == "right":
        x = cabeza.xcor()
        cabeza.setx(x + 20)

#Teclado
wn.listen()
wn.onkeypress(arriba, "Up")
wn.onkeypress(abajo, "Down")
wn.onkeypress(izquierda, "Left")
wn.onkeypress(derecha, "Right")

while True:                                     #Bucle principal normal en la mayoría de juegos.
    wn.update()

    #Colisiones bordes.
    if cabeza.xcor() > 280 or cabeza.xcor() < -280 or cabeza.ycor() > 280 or cabeza.ycor() < -280:
        time.sleep(1)
        cabeza.goto(0,0)
        cabeza.direction = "stop"

        #Reseteamos el cuerpo de la serpiente.
        [i.hideturtle() for i in segmentos]
        segmentos.clear()
        posponer = 0.1

        #Resetear marcador
        score = 0
        texto.clear()
        texto.write("Score: {}     High Score: {}".format(score, high_score), align="center", font=("Courier", 24, "normal"))

    #Colisiones comida-serpiente.
    if cabeza.distance(comida) < 20:
        #Mover la comida a posición random
        x = random.randint(-280,280)
        y = random.randint(-280,280)
        comida.goto(x,y)
        posponer *= 0.99

        #Cuerpo de la serpiente
        nuevo_segmento = turtle.Turtle()
        nuevo_segmento.speed(0)
        nuevo_segmento.shape("square")
        nuevo_segmento.color("grey")
        nuevo_segmento.penup()
        segmentos.append(nuevo_segmento)

        #Aumentar marcador
        score += 1

        if score > high_score:
            high_score = score

        texto.clear()
        texto.write("Score: {}     High Score: {}".format(score, high_score), align="center", font=("Courier", 24, "normal"))

    #Mover el cuerpo de la serpiente.
    totalSeg = len(segmentos)
    for index in range(totalSeg -1, 0, -1):
        x = segmentos[index - 1].xcor()
        y = segmentos[index - 1].ycor()
        segmentos[index].goto(x,y)

    if totalSeg > 0:
        x = cabeza.xcor()
        y = cabeza.ycor()
        segmentos[0].goto(x,y)

    mov()

    #Colisiones con el cuerpo
    for segmento in segmentos:
        if segmento.distance(cabeza) < 20:
            time.sleep(1)
            cabeza.goto(0,0)
            cabeza.direction = "stop"

            #Reseteamos el cuerpo de la serpiente.
            [i.hideturtle() for i in segmentos]
            segmentos.clear()
            posponer = 0.1

             #Resetear marcador
            score = 0
            texto.clear()
            texto.write("Score: {}     High Score: {}".format(score, high_score), align="center", font=("Courier", 24, "normal"))

    time.sleep(posponer)                        #Establecemos un periodo de descanso entre repetición y repetición de la función mov.


import os
from operator import attrgetter
from distutils.dir_util import mkpath

import pandas as pd
import matplotlib.pyplot as plt

try:
    from __init__ import ROOT
except ImportError:
    ROOT = None

from misc import matching
from misc import utils

plt.ion()
plt.rc("axes.spines", top=False, right=False)

GIW_EVENT_MAPPING = {1: 1, 2: 4, 3: 2, 4: 5, 5: 12}
GIW_EVENT_MAPPING_REV = {v: k for k, v in GIW_EVENT_MAPPING.items()}

##
# ELC
def elc(evt_gt, evt_pr, ws, data_gt, data_pr, fpath_elc_mat, **kwargs):
    """Interface to matching framework"""
    # TODO: simply parse copnfusion matrix from ELCCM.m
    events_match = elc_tp_matching(evt_gt, evt_pr, ws)
    events_match = events_match.query("match==True")

    # load ELC alignment
    if not os.path.exists(fpath_elc_mat):
        print("Run ELCCM.m")
        _, fname = os.path.split(fpath_elc_mat)
        prepare_for_elc_mat(data_gt.copy(), data_pr.copy(), fname)

    data_mat_gt, data_mat_pr, data_mat = parse_elc_mat(fpath_elc_mat)
    matcher_elc_mat = get_elc_mat_events(data_mat, [["ref_aligned"], ["test_aligned"]])
    events_err = matcher_elc_mat.events
    events_err = events_err.query("match==True")

    events = pd.concat((events_match, events_err))
    events.reset_index(drop=True, inplace=True)

    output = events[["gt", "pr"]].values

    return output, events

def elc_tp_matching(evt_gt, evt_pr, ws, eps=1e-5):
    """Transition point matching for ELC visualization"""
    _ws = ws / 2 + eps

    # ELC transition points and matches
    accum_tp = []
    ag = attrgetter("st", "et", "evt")
    _evt_pr = evt_pr["evt"].values
    # find matching transition points
    for evt in evt_gt.itertuples():
        st, et, _evt = ag(evt)
        pr_onset = evt_pr.query("st>@st-@_ws and st<@st+@_ws and evt==@_evt")
        pr_offset = evt_pr.query("et>@et-@_ws and et<@et+@_ws and evt==@_evt")
        pr_onset = pr_onset.index.values[0] if len(pr_onset) else -1
        pr_offset = pr_offset.index.values[0] if len(pr_offset) else -1
        if -1 not in [pr_onset, pr_offset]:
            match = _evt_pr[pr_onset] == _evt_pr[pr_offset]
        else:
            match = False
        accum_tp.append([pr_onset, pr_offset, match])
    tp_onset, tp_offset, mask_match = zip(*accum_tp)

    # gather data
    gt_idx = list(range(len(evt_gt)))
    tp_onset = zip(gt_idx, tp_onset)
    match_tp_onset = list(filter(lambda x: -1 not in x, tp_onset))
    tp_offset = zip(gt_idx, tp_offset)
    match_tp_offset = list(filter(lambda x: -1 not in x, tp_offset))

    match_tp_onset = pd.DataFrame(match_tp_onset, columns=["gt_idx", "pr_idx"])
    match_tp_onset["onset"] = True
    match_tp_offset = pd.DataFrame(match_tp_offset, columns=["gt_idx", "pr_idx"])
    match_tp_offset["offset"] = True
    events_match_tp = pd.merge(
        match_tp_onset, match_tp_offset, on=["gt_idx", "pr_idx"], how="outer"
    )
    events_match_tp.fillna(False, inplace=True)

    gt_idx_match = [_m for _f, _m in zip(mask_match, gt_idx) if _f]
    events_match_tp["match"] = events_match_tp["gt_idx"].isin(gt_idx_match)

    gt_idx = events_match_tp["gt_idx"]
    pr_idx = events_match_tp["pr_idx"]
    events_match_tp["gt"] = evt_gt.loc[gt_idx, "evt"].values
    events_match_tp["pr"] = evt_pr.loc[pr_idx, "evt"].values

    return events_match_tp

# mat functions
def prepare_for_elc_mat(data_gt, data_pr, name, sdir):
    """Prepares data to be imported to matlab"""

    data_gt.replace({"evt": GIW_EVENT_MAPPING_REV}, inplace=True)
    data_pr.replace({"evt": GIW_EVENT_MAPPING_REV}, inplace=True)
    data_gt["evt-test"] = data_pr["evt"]
    data_gt.to_csv(os.path.join(sdir, f"{name}-data.csv"), index=False)
    for d, tag in zip([data_gt, data_pr], ["gt", "pr"]):
        evt = utils.aggr_events_df(d["evt"])
        # get timestamps for event bounds; adjust for matlab's 1-based indexing
        evt["st"] = d.iloc[evt["s"].values]["t"].values
        evt["et"] = d.iloc[evt["e"].values - 1]["t"].values
        evt["s"] += 1
        evt.to_csv(os.path.join(sdir, f"{name}-{tag}.csv"), index=False)

def parse_elc_mat(fpath):
    data = utils.loadmat(fpath)

    labels_ref = utils.get_nested_item(data, ["label_ref", "Labels"])
    labels_test = utils.get_nested_item(data, ["label_test", "Labels"])

    timestamps = utils.get_nested_item(data, ["label_test", "T"])
    data_gt = pd.DataFrame.from_dict({"t": timestamps, "evt": labels_ref})
    data_pr = pd.DataFrame.from_dict({"t": timestamps, "evt": labels_test})

    return data_gt, data_pr, data

def get_elc_mat_events(data_mat, sources):
    _source_gt, _source_pr = sources
    labels_ref = utils.get_nested_item(data_mat, _source_gt)
    labels_test = utils.get_nested_item(data_mat, _source_pr)
    timestamps = utils.get_nested_item(data_mat, ["label_test", "T"])
    _gt = pd.DataFrame.from_dict({"t": timestamps, "evt": labels_ref})
    _pr = pd.DataFrame.from_dict({"t": timestamps, "evt": labels_test})
    _gt.replace({"evt": GIW_EVENT_MAPPING}, inplace=True)
    _pr.replace({"evt": GIW_EVENT_MAPPING}, inplace=True)
    matcher = matching.EventMatcher(gt=_gt, pr=_pr, mode="raw")
    match_mask = matcher.events["gt"] == matcher.events["pr"]
    matcher.events["match"] = ~match_mask
    return matcher

# plotting functions
def plot_elc(events, matcher, matcher_mat, plot_transitions=False, xlim=None):
    kwargs = {
        "wspace": 0,
        "hspace": 0.3,
        "top": 0.9,
        "bottom": 0,
        "left": 0.2,
        "right": 0.98,
    }

    fig, ax = plt.subplots(figsize=(3.2, 1.2 * 1.5))
    ax = [None, ax]
    fig.subplots_adjust(**kwargs)
    if xlim is not None:
        ax[1].set_xlim(*xlim)

    # scarf plots
    scarf_plot = utils.ScarfPlot(ax[1])
    scarf_plot.plot(matcher.evt_gt, label="Ground\ntruth")
    scarf_plot.plot(matcher.evt_pr, label="Prediction")

    # match plots
    plot_kwargs = {"ax": ax[1], "ids": (0, 1), "enum": True, "exclude_null": True}
    utils.match_plot(events, matcher.evt_gt, matcher.evt_pr, plot_kwargs=plot_kwargs)

    # plot aligned events
    scarf_plot.plot(matcher_mat.evt_gt, label="Aligned\nground\ntruth")
    scarf_plot.plot(matcher_mat.evt_pr, label="Aligned\nprediction")
    plot_kwargs = {
        "ax": ax[1],
        "ids": (2, 3),
        "enum": False,
        "linestyle": "--",
        "match-point": "mid",
    }
    utils.match_plot(matcher_mat.events, plot_kwargs=plot_kwargs)

    # visualize transition points
    if plot_transitions:
        match_plot_kwargs = {
            "ids": (0, 1),
            "enum": False,
            "exclude_null": True,
            "linestyle": "--",
            "linewidth": 0.75,
            "color": "green",
            "match-point": "s",
        }
        plot_kwargs.update(match_plot_kwargs)
        utils.match_plot(
            events,
            matcher.evt_gt,
            matcher.evt_pr,
            col_match="onset",
            plot_kwargs=plot_kwargs,
        )

        match_plot_kwargs = {
            "enum": False,
            "exclude_null": True,
            "linestyle": "--",
            "linewidth": 0.75,
            "color": "red",
            "match-point": "e",
        }
        plot_kwargs.update(match_plot_kwargs)
        utils.match_plot(
            events,
            matcher.evt_gt,
            matcher.evt_pr,
            col_match="offset",
            plot_kwargs=plot_kwargs,
        )

    return fig

##
def main():
    root = os.path.join(ROOT, "assets")
    dataset = "elc"
    sdir = os.path.join(ROOT, "results", "match-plots", "elc")
    mkpath(sdir)

    jobs = [
        {"fname": "elc", "plot_transitions": True},
        {"fname": "elc_fail", "plot_transitions": False},
        {"fname": "elc_fail_timing", "plot_transitions": False},
    ]

    for job in jobs:
        fname = job["fname"]
        data_gt = pd.read_csv(os.path.join(root, dataset, f"{fname}.csv"))
        data_pr = pd.read_csv(os.path.join(root, f"{dataset}_test", f"{fname}.csv"))

        event_matcher = matching.EventMatcher(gt=data_gt, pr=data_pr, mode="raw")
        events = elc_tp_matching(
            event_matcher.evt_gt, event_matcher.evt_pr, ws=10 / 300.0
        )

        # analyse data from matlab implementation
        fpath_elc_mat = os.path.join(
            root, f"{dataset}_test", "elccm", f"{fname}-{fname}_test.mat"
        )
        if not os.path.exists(fpath_elc_mat):
            print(f"Run ELCCM.m for {fname} and copy to {fpath_elc_mat}")
            prepare_for_elc_mat(data_gt, data_pr, fname, sdir)
            continue

        data_mat_gt, data_mat_pr, data_mat = parse_elc_mat(fpath_elc_mat)
        matcher_mat = get_elc_mat_events(data_mat, [["ref_aligned"], ["test_aligned"]])

        # plot
        _e = utils.idx2timestamp(event_matcher.evt_gt.iloc[-1]["e"] + 1, data_gt)
        plot_transitions = job.get("plot_transitions", False)
        fig = plot_elc(
            events,
            event_matcher,
            matcher_mat,
            plot_transitions=plot_transitions,
            xlim=(data_gt["t"].min(), _e),
        )
        fig.savefig(os.path.join(sdir, f"{fname}.eps"))
        fig.savefig(os.path.join(sdir, f"{fname}.png"))

if __name__ == "__main__":
    main()

# debug ELCCM.m output
# fig, ax = plt.subplots(figsize=(3.2, 2.4))
# ax = [None, ax]
# fig.subplots_adjust(**kwargs)
# _e = utils.idx2timestamp(event_matcher.evt_gt.iloc[-1]['e'] + 1, data_gt)
# ax[1].set_xlim(data_gt['t'].min(), _e)
#
# # scarf plots
# scarf_plot = utils.ScarfPlot(ax[1])
# scarf_plot.plot(event_matcher.evt_gt, label='Ground\ntruth')
# scarf_plot.plot(event_matcher.evt_pr, label='Test')
# sources = [
#     [['label_ref', 'Labels'], ['label_test', 'Labels']],
#     # [['ref_fu'], ['test_fu']],
#     [['ref_aligned'], ['test_aligned']],
# ]
# for source in sources:
#     matcher_elc = get_elc_mat_events(data_mat, source)
#     _source_gt, _source_pr = source
#     scarf_plot.plot(matcher_elc.evt_gt, label='-'.join(_source_gt))
#     scarf_plot.plot(matcher_elc.evt_pr, label='-'.join(_source_pr))


from smog.abstract.command import CommandBase
from smog.logger import Logger

class Run(CommandBase):

    command = "run"
    description = "Run the selected module"
    aliases = ("execute", "start")

    def init_arguments(self):
        self.parser.add_argument(
            "-t",
            "--threads",
            type=int,
            default=5,
            help="Number of threads to run",
            metavar="<value>",
        )

        self.parser.add_argument(
            "-d", "--debug-threads", action="store_true", help="Debug threads"
        )

    def execute(self):
        if self.shell.selected_module is None:
            return Logger.warn("No module selected.")

        self.shell.selected_module(
            self.database, self.arguments.threads, self.arguments.debug_threads
        )._execute()

        Logger.success("Done.")


import os
import glob
import pandas as pd

def merge(root):
    os.chdir(root + '/Data/Appended/')
    fileList=glob.glob('*csv')
    dfList=[]
    colnames=pd.read_csv(fileList[0]).columns[2:]
    print(colnames)
    for filename in fileList:
        df=pd.read_csv(filename)
        df = df[colnames]
        dfList.append(df)
    concatDf=pd.concat(dfList,axis=0)#this concatenates vertically (axis=0)
    concatDf.columns=colnames
    concatDf.to_csv(root + '/Data/Final/merge.csv',index=None)


from command import Command
from serial_device import SerialDevice
from command_history import CommandHistory

#  ["started", "stopped", "paused", "playing"]

class CommandProcessors:
    class Resolver:
        def __init__(self, logger) -> None:
            self.logger = logger

        def resolve(self, command: Command):
            if command.event_name == "started":
                return CommandProcessors.Base(command, self.logger)
            elif command.event_name == "stopped":
                return CommandProcessors.Base(command, self.logger)
            elif command.event_name == "paused":
                return CommandProcessors.Paused(command, self.logger)
            elif command.event_name == "playing":
                return CommandProcessors.Playing(command, self.logger)
            else:
                self.logger.info(f"No command resolver for command: {command}")

    class Base:
        def __init__(self, command: Command, logger) -> None:
            self.command = command
            self.logger = logger

        def should_process_immediately(self) -> bool:
            return True

        def should_process_now(self) -> bool:
            return self.command.processed_at is None

        def process(self, serial_device: SerialDevice, command_history: CommandHistory):
            responses = []
            with serial_device:
                for translated_command in self.command.translated_commands():
                    serial_device.send_command(translated_command)
                    response = serial_device.receive_response()
                    responses.append(response)

            self.command.mark_processed(responses)

    class Paused(Base):
        POSTPONE_THRESHOLD_IN_MINUTES = 4

        def should_process_immediately(self) -> bool:
            return False

        def should_process_now(self) -> bool:
            return (
                super().should_process_now()
                and self.command.received_minutes_ago()
                > self.POSTPONE_THRESHOLD_IN_MINUTES
            )

    class Playing(Base):
        def process(self, serial_device: SerialDevice, command_history: CommandHistory):
            if self.__should_process(command_history):
                return super().process(serial_device, command_history)
            else:
                self.command.mark_processed([])

        def __should_process(self, command_history: CommandHistory) -> bool:
            previous_command = command_history.get_previous_command()

            if previous_command is None:
                return True

            if (
                previous_command.event_name != "paused"
                and previous_command.event_name != "stopped"
            ):
                return False

            if (
                previous_command.event_name == "paused"
                and previous_command.processed_at is None
            ):
                return False

            return True


# -*- coding: utf-8 -*-
# @Author  : qiaohezhe
# @github : https://github.com/fengduqianhe
# @Date    :  8/31/2022
# version： Python 3.7.8
# @File : one_class_homo.py
# @Software: PyCharm
# @Institution: SMU

import numpy as np
import scipy.io as sio
import scipy.sparse as sp
from collections import Counter
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from matplotlib.ticker import MaxNLocator

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['figure.dpi'] = 300  # 图片像素
plt.rcParams['figure.figsize'] = (8.5, 7.5)
dataset_name = 'Facebook'

data = sio.loadmat('./data/{}.mat'.format(dataset_name))

label = data['Label'] if ('Label' in data) else data['gnd']

attr = data['Attributes'] if ('Attributes' in data) else data['X']
network = data['Network'] if ('Network' in data) else data['A']

attr = attr.toarray()
adj_matrix = network.toarray()

label = label[:, 0]
print(Counter(label))

plt.rcParams['figure.figsize'] = (8.5, 7.5)
from matplotlib.backends.backend_pdf import PdfPages

import matplotlib.pyplot as plt
import matplotlib.mlab as mlab

def draw_homo(message_all, i):
    with PdfPages('pdf/BlogCatalog.pdf') as pdf:
        mu_0 = np.mean(message_all[0])  # 计算均值
        print('The mean of normal node is {}'.format(mu_0))
        sigma_0 = np.std(message_all[0])
        print('The std of normal node is {}'.format(sigma_0))
        mu_1 = np.mean(message_all[1])  # 计算均值
        print('The mean of abnormal node is {}'.format(mu_1))
        sigma_1 = np.std(message_all[1])
        print('The std of abnormal node is {}'.format(sigma_1))
        n, bins, patches = plt.hist(message_all, bins=30, normed=1, label=['Normal', 'Abnormal'])
        y_0 = mlab.normpdf(bins, mu_0, sigma_0)  # 拟合一条最佳正态分布曲线y
        y_1 = mlab.normpdf(bins, mu_1, sigma_1)  # 拟合一条最佳正态分布曲线y
        # plt.plot(bins, y_0, 'g--', linewidth=3.5)  # 绘制y的曲线
        # plt.plot(bins, y_1, 'r--', linewidth=3.5)  # 绘制y的曲线
        plt.plot(bins, y_0, color='steelblue', linestyle='--', linewidth=7.5)  # 绘制y的曲线
        plt.plot(bins, y_1, color='darkorange', linestyle='--', linewidth=7.5)  # 绘制y的曲线
        plt.ylim(0, 100)
        plt.xlabel('Homophily', fontsize=25)
        plt.ylabel('Number of Samples', size=25)
        plt.yticks(fontsize=30)
        plt.xticks(fontsize=30)
        plt.gca().yaxis.set_major_locator(MaxNLocator(integer=True))
        plt.legend(loc='upper left', fontsize=30)
        plt.title('{}_{}'.format(dataset_name, i), fontsize=25)
        plt.title('Facebook', fontsize=25)
        plt.show()
        # plt.savefig('fb_homo.png', dpi=500, bbox_inches='tight')

def cmpute_home(adj_matrix, anomaly_ids):
    row = adj_matrix.shape[0]
    col = adj_matrix.shape[1]
    num_ano_list = []

    for i in range(row):
        node_index = np.argwhere(adj_matrix[i, :] == 1)[:, 0]
        num_ano = len(set(node_index) & set(anomaly_ids))
        if len(node_index) != 0:
            num_ano_list.append(num_ano / len(node_index))
        else:
            num_ano_list.append(-1)
    return np.array(num_ano_list)

normal_node_index = np.where(label == 0)[0]
abnormal_node_index = np.where(label == 1)[0]

homo1 = cmpute_home(adj_matrix, normal_node_index)
homo2 = cmpute_home(adj_matrix, abnormal_node_index)
normal_node_degree = homo1[normal_node_index]
abnormal_node_degree = homo2[abnormal_node_index]

data = [normal_node_degree, abnormal_node_degree]
fig, ax = plt.subplots()
ax.boxplot(data)
# plt.ylim(0, 1)
ax.set_xticklabels(["normal", "abnormal"])  # 设置x轴刻度标签
plt.title('Node Homophily')
plt.show()

draw_homo(data, 0)


from helper.input import read_input_simple
import sys

def parse_input(input):
    sensors = []
    for line in read_input_simple(15, input):
        line = line.split(' ')
        x1 = int(line[2][2:-1])
        y1 = int(line[3][2:-1])
        x2 = int(line[8][2:-1])
        y2 = int(line[9][2:])
        sensors.append((x1, y1, x2, y2))
    return sensors

def part1(input):
    sensors = parse_input(input)
    # y_cutoff = 10
    y_cutoff = 2000000
    ranges = list()
    for sx, sy, bx, by in sensors:
        # print(f'Sensor at ({sx}, {sy}) with beacon at ({bx}, {by})')
        d_sb = abs(sx - bx) + abs(sy - by)
        d_sl = abs(sy - y_cutoff)
        diff = d_sb - d_sl
        if diff >= 0:
            ranges.append([sx - diff, sx + diff])

    ranges.sort()
    merged_ranges = [ranges[0]]
    for i in range(1, len(ranges)):
        if ranges[i][0] > merged_ranges[-1][1] + 1:
            merged_ranges.append(ranges[i])
        else:
            merged_ranges[-1][1] = max(ranges[i][1], merged_ranges[-1][1])
    return sum([x2 - x1 for x1, x2 in merged_ranges])

def part2(input):
    sensors = parse_input(input)
    # bound = 20
    bound = 4000000
    ranges = []
    for y_cutoff in range(bound + 1):
        if y_cutoff % 100000 == 0:
            print(f'{100.0 * y_cutoff / bound}%')
        cant_contain = list()
        for sx, sy, bx, by in sensors:
            # print(f'Sensor at ({sx}, {sy}) with beacon at ({bx}, {by})')
            d_sb = abs(sx - bx) + abs(sy - by)
            d_sl = abs(sy - y_cutoff)
            diff = d_sb - d_sl
            if diff >= 0:
                cant_contain.append([max(sx - diff, 0), min(sx + diff, bound)])
        cant_contain.sort()
        merged_ranges = [cant_contain[0]]
        for i in range(1, len(cant_contain)):
            if cant_contain[i][0] > merged_ranges[-1][1] + 1:
                merged_ranges.append(cant_contain[i])
            else:
                merged_ranges[-1][1] = max(cant_contain[i][1], merged_ranges[-1][1])
        ranges.append(merged_ranges)
    for y, rang in enumerate(ranges):
        if rang != [[0, bound]]:
            x = rang[0][1] + 1
            return y + x * 4000000

if __name__ == '__main__':
    _, part, input = sys.argv
    if part == '1':
        print(part1(input))
    elif part == '2':
        print(part2(input))
    else:
        print('Part must be one of 1 or 2')


from sqlalchemy.orm import Session

from app import models, schemas

def create_prescription(db: Session, prescription: schemas.PrescriptionCreate):
    db_prescription = models.Prescription(username=prescription.username, doctor=prescription.doctor,
                                          medicines=prescription.medicines,
                                          date_time=prescription.date_time, done=prescription.done)
    db.add(db_prescription)
    db.commit()
    db.refresh(db_prescription)
    return db_prescription

def read_user_prescription(db: Session, username: str, only_pending: bool):
    if only_pending:
        return db.query(models.Prescription).filter(models.Prescription.username == username).filter(
            models.Prescription.done == False).all()

    return db.query(models.Prescription).filter(models.Prescription.username == username).all()


# Author: Jose G Perez
# Version 1.0
# Last Modified: January 31, 2018
import cv2
import numpy as np
import pylab as plt
from multiprocessing.pool import ThreadPool
from timeit import default_timer as timer

from util_sift import load_sift
from util_matching import match_details
INDIVIDUAL = False

np.random.seed(1)
np.set_printoptions(threshold=np.nan, linewidth=1000)
# Default: contrast 0.04, edge 10, sigma 1.6
# Experiment: 0.08, 30, 2
# CT: The larger the threshold, the less features are produced by the detector
# ET: The larger the threshold, the more features that are retained
SIFT = cv2.xfeatures2d.SIFT_create(contrastThreshold=0.05, edgeThreshold=100, sigma=2)

RADIUS = 50
RADIUS_SQUARED = RADIUS ** 2
SCALE_THRESHOLD = 3
DISTANCE_THRESHOLD = 200
RESPONSE_THRESHOLD = 0.01

DISTANCE_RATIO = 0.95
RANSAC_REPROJ_TRESHHOLD = 10 # The higher the threshold, the lower the inliers
RANSAC_MAX_ITERS = 2000
RANSAC_CONFIDENCE = 0.99

FIGURE_IDX = 0
def imshow(im, title=''):
    global FIGURE_IDX
    plt.figure(FIGURE_IDX)
    plt.axis('off')
    plt.tick_params(axis='both',
                    left='off', top='off', right='off', bottom='off',
                    labelleft='off', labeltop='off', labelright='off', labelbottom='off')
    plt.title(title)
    plt.imshow(im)

    FIGURE_IDX += 1

def gallery(array, ncols=3):
    # Grayscale
    if len(array.shape) == 3:
        nindex, height, width = array.shape
        nrows = nindex//ncols
        result = (array.reshape(nrows, ncols, height, width)
                  .swapaxes(1,2)
                  .reshape(height*nrows, width*ncols))
        return result
    # Color
    else:
        nindex, height, width, intensity = array.shape
        nrows = nindex//ncols
        # want result.shape = (height*nrows, width*ncols, intensity)
        result = (array.reshape(nrows, ncols, height, width, intensity)
                  .swapaxes(1,2)
                  .reshape(height*nrows, width*ncols, intensity))
        return result

s_im, s_label, s_kp, s_des = load_sift('S_BB_V1_SIFT.npz')
pw_im, pw_label, pw_kp, pw_des = load_sift('PW_BB_V1_SIFT.npz')

time_start = timer()
# ==== MATCHING SYSTEM BEGIN
if INDIVIDUAL:
    # im1 = s_im[np.where(s_label == 67)[0][0]]
    # im2 = pw_im[np.where(pw_label == 145)[0][0]]

    # im1 = s_im[np.where(s_label == 6)[0][0]]
    # im2 = pw_im[np.where(pw_label == 8)[0][0]]

    im1 = s_im[np.where(s_label == 33)[0][0]]
    im2 = pw_im[np.where(pw_label == 68)[0][0]]

    kp1, des1 = SIFT.detectAndCompute(im1, None)
    kp2, des2 = SIFT.detectAndCompute(im2, None)

    m, matches, stat_s_diff, stat_d_dist, stat_resp_kp1, stat_resp_kp2, stat_rad  = match_details(kp1, des1, kp2, des2)
    print("\t[ScaleD] Max %.3f, Min %.3f, Avg %.3f" % (max(stat_s_diff) , min(stat_s_diff), sum(stat_s_diff) / len(stat_s_diff)))
    print("\t[DescD] Max %.3f, Min %.3f, Avg %.3f" % (max(stat_d_dist),  min(stat_d_dist), sum(stat_d_dist) / len(stat_d_dist)))
    print("\t[RespKp1] Max %.3f, Min %.3f, Avg %.3f" % (max(stat_resp_kp1), min(stat_resp_kp1), sum(stat_resp_kp1) / len(stat_resp_kp1)))
    print("\t[RespKp2] Max %.3f, Min %.3f, Avg %.3f" % (max(stat_resp_kp2), min(stat_resp_kp2), sum(stat_resp_kp2) / len(stat_resp_kp2)))
    print("\t[Rad] Max %.3f, Min %.3f, Avg %.3f" % (max(stat_rad), min(stat_rad), sum(stat_rad) / len(stat_rad)))

    # For comparison, the selected SIFT keypoints
    # BF = cv2.BFMatcher(normType=cv2.NORM_L2, crossCheck=True)
    # bf_matches = BF.match(des1, des2)
    # im_bf_matches = cv2.drawMatches(im1,kp1,im2,kp2,bf_matches,None,flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
    # metric_bf = len(bf_matches)/ (len(kp1) + len(kp2) - len(bf_matches))
    BF = cv2.BFMatcher(normType=cv2.NORM_L2)
    bf_matches = BF.knnMatch(des1, des2, k=2)

    # For comparison, RANSAC
    good_matches = [m[0] for m in bf_matches if len(m) == 2 and m[0].distance < m[1].distance * DISTANCE_RATIO]
    src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches])
    dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches])
    H, mask = cv2.findHomography(src_pts, dst_pts, method=cv2.RANSAC, ransacReprojThreshold=RANSAC_REPROJ_TRESHHOLD, maxIters=RANSAC_MAX_ITERS, confidence=RANSAC_CONFIDENCE)
    matchesMask = mask.ravel().tolist()
    drawParameters = dict(matchColor=None, singlePointColor=None, matchesMask=matchesMask,
                          flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)

    # Draw the lines between the 2 images connecting matches
    im_ransac = cv2.drawMatches(im1, kp1, im2, kp2, good_matches, None, **drawParameters)

    # Images
    im1_kp = cv2.drawKeypoints(im1, kp1, None, None, cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
    im2_kp = cv2.drawKeypoints(im2, kp2, None, None, cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
    im_matches = cv2.drawMatches(im1,kp1,im2,kp2,matches,None,flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)

    metric = len(matches) / (len(kp1) + len(kp2) - len(matches))
    print("Metric", metric)
    print("Inliers", mask.sum())

    plt.gray()
    imshow(im1_kp, 'S Keypoint Data')
    imshow(im2_kp, 'PW Keypoint Data')
    #imshow(im_bf_matches, 'BF Matches')
    imshow(im_matches, 'New Matching')
    imshow(im_ransac, 'RANSAC')
else:
    # im2 = pw_im[np.where(pw_label == 68)[0][0]]
    pw_idx = 33
    im2 = pw_im[pw_idx]
    (kp2, des2) = (pw_kp[pw_idx], pw_des[pw_idx])

    all_matches = []
    all_im = []
    all_kp = []
    all_des = []

    cidx = 0
    for sidx in range(25, 40):
        # if sidx == 32:
        #     print('=[P]', end='')
        print("S", (sidx + 1), "CIDX", cidx)

        (im1, kp1, des1) = (s_im[sidx], s_kp[sidx], s_des[sidx])
        m, matches, stat_s_diff, stat_d_dist, stat_resp_kp1, stat_resp_kp2, stat_rad = match_details(kp1, des1, kp2,
                                                                                des2)
        # print("\t[ScaleD] Max %.3f, Min %.3f, Avg %.3f" % (
        # max(stat_s_diff), min(stat_s_diff), sum(stat_s_diff) / len(stat_s_diff)))
        # print("\t[DescD] Max %.3f, Min %.3f, Avg %.3f" % (
        # max(stat_d_dist), min(stat_d_dist), sum(stat_d_dist) / len(stat_d_dist)))
        # print("\t[RespKp1] Max %.3f, Min %.3f, Avg %.3f" % (
        # max(stat_resp_kp1), min(stat_resp_kp1), sum(stat_resp_kp1) / len(stat_resp_kp1)))
        # print("\t[RespKp2] Max %.3f, Min %.3f, Avg %.3f" % (
        # max(stat_resp_kp2), min(stat_resp_kp2), sum(stat_resp_kp2) / len(stat_resp_kp2)))
        # print("\t[Rad] Max %.3f, Min %.3f, Avg %.3f" % (max(stat_rad), min(stat_rad), sum(stat_rad) / len(stat_rad)))

        metric = len(matches) / (len(kp1) + len(kp2) - len(matches))

        print("\tMetric w/PW %s is %.3f, Matches %d" % (pw_idx, metric, len(matches)))

        all_matches.append(matches)
        all_im.append(im1)
        all_kp.append(kp1)
        all_des.append(des1)
        cidx += 1

    # index = 7
    # imshow(cv2.drawMatches(all_im[index], all_kp[index], im2, kp2, all_matches[index], None, flags=2), '[P]SW33')
    # index = 8
    # imshow(cv2.drawMatches(all_im[index], all_kp[index], im2, kp2, all_matches[index], None, flags=2), 'SW34')

duration = timer() - time_start
print("Program took %.3fs" % duration)


#Front-end (GUI)
import Tkinter
#import chat_back
from chat_back import chatbot

def ai(x,reply):
    if '@' in x:
        f=open('qlist.txt','a')
        writeup="Email: "+reply+'\n'
        f.write(writeup)
        f.close
        w="Okay I 'll respond back! Tell me if you have any other queries besides this"

    else:
        w="Sorry! I need to think over it. Please leave your email address and I'll respond back"
        f=open('qlist.txt','a')
        writeup="Query: "+x+'\n'
        f.write(writeup)
        f.close

    return w

#class to create window
class myapp(Tkinter.Tk):
    def __init__(self,parent): #self : current object ; parent : widget to act as parent of current object
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        #self.canvas = Canvas(self.Tk, width=500, height=500)
        #self.canvas.pack()
        self.initialize()

    def initialize(self):

        top1=Tkinter.Label(self,text="THE JMI CPP",fg="red",font=("Times New Roman",18,"bold"),bg="yellow")
        top1.grid(row=0,column=0)
        top2=Tkinter.Label(self,text="Ask anything about JMI",fg="black",font=("Times New Roman",14))
        top2.grid(row=1,column=0)
        top3=Tkinter.Label(self)
        top3.grid(row=2,pady=8)
        self.entryvar1=Tkinter.StringVar() #binding entry widget to StringVar() instance and set and get entry through that variable
        entry1=Tkinter.Entry(self,textvariable=self.entryvar1,bd=3,width=32)
        entry1.bind("<Return>", self.OnClick)
        entry1.grid(row=4)

        label1=Tkinter.Label(self,text="Enter your query",fg="black",font=("Times New Roman",14,"bold"),bg="grey")
        label1.grid(row=3)

        scroll=Tkinter.Scrollbar(self)
        scroll.grid(row=12,column=1,rowspan=10)

        self.entryvar2=Tkinter.StringVar()
        self.output=Tkinter.Text(self,wrap=Tkinter.WORD,height=33,width=167,yscrollcommand=scroll.set,bg="grey") #for text window
        self.output.grid(row=12)

        scroll.config(command=self.output.yview_pickplace("end")) #to place the cursor at the endline

        self.output.insert(Tkinter.INSERT, "Hello!")
        self.output.insert(Tkinter.INSERT, "\nIt's nice to see you here!")
        self.output.insert(Tkinter.INSERT, "\nHow may I help you?")

        btn1=Tkinter.Button(self,text="Get Answer",command=self.OnClick)
        btn1.grid(row=5)

        #self.geometry("1080x800+200+200")

    def OnClick(self):
        inp=self.entryvar1.get()
        if (chatbot(inp)=="Sorry! I need to think over it. Please leave your email address and I'll respond back"):
            self.output.insert(Tkinter.END,"\n>>>User  : "+inp)
            x=self.entryvar1.get()
            self.output.insert(Tkinter.END,"\n   JMICPP : "+ai(x,inp))
        else:
            self.output.insert(Tkinter.END,"\n>>>User  : "+inp)
            self.output.insert(Tkinter.END, "\n  JMICPP : "+chatbot(inp))
            self.entryvar1.set("")
            self.output.yview(Tkinter.END)
    def OnPressEnter(self,event):
        inp=self.entryvar1.get()
        if (chatbot(inp)=="Sorry! I need to think over it. Please leave your email address and I'll respond back"):
            self.output.insert(Tkinter.END,"\n>>>User  : "+inp)
            x=self.entryvar1.get()
            self.output.insert(Tkinter.END,"\n   JMICPP : "+ai(x,inp))
        else:
            self.output.insert(Tkinter.END,"\n>>>User  : "+inp)
            self.output.insert(Tkinter.END,"\n   JMICPP : "+chatbot(inp))
            self.entryvar1.set("")
            self.output.yview(Tkinter.END)

if __name__ =="__main__":
    app = myapp(None)
    app.title('JMI Chatterbot')
    app.geometry('850x600')
    #app['bg'] = 'grey'

    app.mainloop() # show window


# hexaspace computer graphics 09
# cube array myOrtho myLookAt
##################################################
import glfw
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np

# draw cube glVertex3f quads*4 / 4각형 6개로 육면제 그리기
def drawUnitCube():
    glBegin(GL_QUADS)
    glVertex3f( 0.5, 0.5,-0.5)
    glVertex3f(-0.5, 0.5,-0.5)
    glVertex3f(-0.5, 0.5, 0.5)
    glVertex3f( 0.5, 0.5, 0.5)

    glVertex3f( 0.5,-0.5, 0.5)
    glVertex3f(-0.5,-0.5, 0.5)
    glVertex3f(-0.5,-0.5,-0.5)
    glVertex3f( 0.5,-0.5,-0.5)

    glVertex3f( 0.5, 0.5, 0.5)
    glVertex3f(-0.5, 0.5, 0.5)
    glVertex3f(-0.5,-0.5, 0.5)
    glVertex3f( 0.5,-0.5, 0.5)

    glVertex3f( 0.5,-0.5,-0.5)
    glVertex3f(-0.5,-0.5,-0.5)
    glVertex3f(-0.5, 0.5,-0.5)
    glVertex3f( 0.5, 0.5,-0.5)

    glVertex3f(-0.5, 0.5, 0.5)
    glVertex3f(-0.5, 0.5,-0.5)
    glVertex3f(-0.5,-0.5,-0.5)
    glVertex3f(-0.5,-0.5, 0.5)

    glVertex3f( 0.5, 0.5,-0.5)
    glVertex3f( 0.5, 0.5, 0.5)
    glVertex3f( 0.5,-0.5, 0.5)
    glVertex3f( 0.5,-0.5,-0.5)
    glEnd()

# glTranslatef와 glScalef 를 push pop사이에 함으로써 현재행렬에 영향을 주지 않는다.
def drawCubeArray():
    for i in range(5):
        for j in range(5):
            for k in range(5):
                glPushMatrix()
                glTranslatef(i,j,-k-1)
                glScalef(.5,.5,.5)
                drawUnitCube()
                glPopMatrix()

# xyz축
def drawFrame():
    glBegin(GL_LINES)
    glColor3ub(255, 0, 0)
    glVertex3fv(np.array([0.,0.,0.]))
    glVertex3fv(np.array([1.,0.,0.]))
    glColor3ub(0, 255, 0)
    glVertex3fv(np.array([0.,0.,0.]))
    glVertex3fv(np.array([0.,1.,0.]))
    glColor3ub(0, 0, 255)
    glVertex3fv(np.array([0.,0.,0]))
    glVertex3fv(np.array([0.,0.,1.]))
    glEnd()

def render():
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glEnable(GL_DEPTH_TEST)
    glPolygonMode( GL_FRONT_AND_BACK, GL_LINE )
    glLoadIdentity()

    myOrtho(-5,5,-5,5,-8,8)
    myLookAt(np.array([5,3,5]), np.array([1,1,-1]),np.array([0,1,0]))

    #Above two lines must behaves exactly same as the below two lines
    #위의 두 함수는 아래 두개와 같은 작동을 한다
    #glOrtho(-5,5,-5,5,-8,8)
    #gluLookAt(5,3,5 , 1,1,-1, 0,1,0)

    # xyz축 그리기
    drawFrame()
    # 흰색 큐브배열 그리기
    glColor3ub(255, 255, 255)
    drawCubeArray()

# glOrtho와 같은 작동을 하게 하는 함수
def myOrtho(left, right, bottom, top, near, far):                           # scale      translate
    moth=np.array([[2/(right-left),0,0,-(right+left)/(right-left)],         # lr평균 0 0 평균차이
                  [0, 2/(top-bottom),0,-(top+bottom)/(top-bottom)],         # 0 tb평균 0 평균차이
                  [0, 0, -2 / (far - near), -(far + near) / (far - near)],  # 0 0 fn평균 평균차이
                  [0, 0, 0, 1]])
    moth=moth.T   #glMultMatrixf는 .T역행렬을 해줘야한다.
    # 현재 행렬에 moth를 곱한다.
    glMultMatrixf(moth)

# eye point, look-at point, up vector --->> 카메라의 coordinates 백터 uvw, origin point(=eye)
def myLookAt(eye, at, up):
    w=(eye-at)/np.sqrt(np.dot(eye-at,eye-at))   # w는 물체->카메라위치의 unit 백터이다
    u=np.cross(up,w)/np.sqrt(np.dot(np.cross(up,w),np.cross(up,w))) #u는 w와 up을 외적(수직한 백터)의 unit
    v=np.cross(w,u) # v는 wu의 외적하여 unit함
    mlook=np.identity(4)    # global frame(object space)->local(world space)로 변환하는 matrix를 만드는 방법은
                            # m=[U,V,W,ORIGIN P]이다.
                            # 반대인 view space<-world space는 m^(-1) = [u1,u2,u3,-U`p1]이다.
                                                                    #  [v1,v2,v3,-V`p2]
                                                                    #  [w1,w2,w3,-W`p3]
    mlook[0, 0:3] = u
    mlook[1, 0:3] = v
    mlook[2, 0:3] = w
    mlook[0, 3] = -np.dot(u,eye)
    mlook[1, 3] = -np.dot(v, eye)
    mlook[2, 3] = -np.dot(w, eye)
    mlook=mlook.T   #glMultMatrixf는 .T역행렬을 해줘야한다.
    # 현재 행렬에 mlook을 곱한다.
    glMultMatrixf(mlook)

def main():
    if not glfw.init():
        return
    window = glfw.create_window(480,480,'cube array myOrtho myLookAt', None,None)
    if not window:
        glfw.terminate()
        return
    glfw.make_context_current(window)

    while not glfw.window_should_close(window):
        glfw.poll_events()
        render()
        glfw.swap_buffers(window)

    glfw.terminate()

if __name__ == "__main__":
    main()


import numpy as np
import math, glob, os

def get_class_weight(d):
    white_list_formats = {'png', 'jpg', 'jpeg', 'bmp'}
    class_number = dict()
    dirs = sorted([o for o in os.listdir(d) if os.path.isdir(os.path.join(d, o))])
    k = 0
    for class_name in dirs:
        class_number[k] = 0
        iglob_iter = glob.iglob(os.path.join(d, class_name, '*.*'))
        for i in iglob_iter:
            _, ext = os.path.splitext(i)
            if ext[1:] in white_list_formats:
                class_number[k] += 1
        k += 1

    total = np.sum(list(class_number.values()))
    max_samples = np.max(list(class_number.values()))
    mu = 1. / (total / float(max_samples))
    keys = class_number.keys()
    class_weight = dict()
    for key in keys:
        score = math.log(mu * total / float(class_number[key]))
        class_weight[key] = score if score > 1. else 1.

    return class_weight


from django.db import models

class NistControl(models.Model):
    objects = models.Manager()

    control_type = models.CharField(max_length=20)
    baseline = models.CharField(max_length=50, null=True)
    status = models.CharField(max_length=50, null=True)
    # this may just be the statement.description
    number = models.CharField(max_length=20, db_index=True)
    family = models.CharField(max_length=255)
    title = models.CharField(max_length=250, null=True)
    revision = models.IntegerField()
    privacy_impact = models.CharField(max_length=5, null=True, blank=True)
    priority = models.CharField(max_length=20, choices=(('P0', 'P0'), ('P1', 'P1'), ('P2', 'P2'), ('P3', 'P3')), null=True, blank=True)
    related = models.ManyToManyField('NistRelated', related_name='related_controls')
    parent_control = models.ForeignKey('self', related_name='enhancements', null=True, on_delete=models.SET_NULL)

    def __str__(self):
        return str(self.get_family()) + ': ' + str(self.get_number())

    def get_number(self):
        return self.number

    def get_family(self):
        return self.family

    def get_title(self):
        return self.title

class NistReference(models.Model):
    objects = models.Manager()

    href = models.TextField( null=True)
    text = models.TextField(null=True)
    short_name = models.TextField(null=True)
    control = models.ForeignKey(NistControl, related_name='references', on_delete=models.CASCADE)

class NistRelated(models.Model):
    objects = models.Manager()

    text = models.CharField(max_length=20)

class NistSupplementGuidanceRelatedItems(models.Model):
    objects = models.Manager()

    related = models.CharField(max_length=50)

class NistDiscussion(models.Model):
    objects = models.Manager()

    description = models.TextField()
    control = models.OneToOneField(NistControl, related_name='discussion', on_delete=models.CASCADE)

class NistSupplementalGuidance(models.Model):
    objects = models.Manager()

    description = models.TextField()
    control = models.OneToOneField(NistControl, related_name='supplemental_guidance', on_delete=models.CASCADE)
    related = models.ManyToManyField(NistSupplementGuidanceRelatedItems, related_name='related_items')

class NistStatement(models.Model):
    objects = models.Manager()

    number = models.CharField(max_length=25, null=True, db_index=True)
    description = models.TextField(null=True)
    statement = models \
        .ForeignKey('self', related_name='statements', null=True, on_delete=models.CASCADE)
    control = models \
        .OneToOneField(NistControl, related_name='statement', null=True, on_delete=models.CASCADE)
    parent_control = models \
        .ForeignKey(NistControl, related_name='nested_statements', null=True, on_delete=models.CASCADE)

    def __str__(self):
        return self.number

    def statement_control(self):
        if self.control:
            return self.control
        else:
            return self.parent_control

    def get_revision(self):
        if self.control:
            return self.control.revision
        else:
            return self.parent_control.revision

class ControlCorrelationIdentifier(models.Model):
    objects = models.Manager()

    name = models.TextField(null=True, unique=True)
    contributor = models.CharField(max_length=255, null=True, blank=True)
    status = models.CharField(max_length=255, null=True, blank=True)
    type = models.CharField(max_length=255, null=True, blank=True)
    parameter = models.CharField(max_length=255, null=True, blank=True)
    publishdate = models.DateField(null=True, blank=True)
    note = models.TextField(null=True, blank=True)
    definition = models.TextField(null=True, blank=True)
    location = models.TextField(null=True, blank=True)
    statements = models.ManyToManyField(NistStatement, related_name='ccis')
    implementation_guidance = models.TextField(null=True, blank=True)
    assessment_procedures = models.TextField(null=True, blank=True)
    controls = models.ManyToManyField(NistControl, related_name='ccis', )

    def get_number(self):
        return int(str(self.name).replace('CCI-', ''))


'''
< Face Recognition >

- 참고
블로그 : https://wiserloner.tistory.com/1198
유튜브 : https://www.youtube.com/watch?v=sz25xxF_AVE&t=28s

face recognition : https://velog.io/@wbsl0427/facerecognition%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EC%96%BC%EA%B5%B4%EC%9D%B8%EC%8B%9D-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EB%A7%8C%EB%93%A4%EA%B8%B0

pip install face-recognition
pip install cmake
pip isntall dlib
'''

import cv2
import numpy as np
import face_recognition

imgElon = face_recognition.load_image_file('./img_file/musk.jpg')
imgElon = cv2.cvtColor(imgElon, cv2.COLOR_BGR2RGB)

imgTest = face_recognition.load_image_file('./img_file/musk_test.jpg')
imgTest = cv2.cvtColor(imgTest, cv2.COLOR_BGR2RGB)

# 얼굴 찾기

# 원본 이미지 얼굴 bbox 좌표값
faceLoc = face_recognition.face_locations(imgElon)[0]

# 이를 encoding하여 특정 128개의 값으로 만들어내는 것.
encodeElon = face_recognition.face_encodings(imgElon)[0]
# print(encodeElon)
# bbox 그리기
cv2.rectangle(imgElon, (faceLoc[3], faceLoc[0]), (faceLoc[1], faceLoc[2]),(255,0,255), 2)

# Test이미지
faceLocTest = face_recognition.face_locations(imgTest)[0]
encodeTest = face_recognition.face_encodings(imgTest)[0]
cv2.rectangle(imgTest, (faceLocTest[3], faceLocTest[0]), (faceLocTest[1], faceLocTest[2]),(255,0,255), 2)

# 원본 이미지와 테스트 이미지가 비슷한지 테스트
results = face_recognition.compare_faces([encodeElon], encodeTest)
# print(results)

faceDis = face_recognition.face_distance([encodeElon], encodeTest)
print(results, faceDis)

# bbox위에 글자 입력
cv2.putText(imgTest, f'{results} {round(faceDis[0],2)}', (50, 50), cv2.FONT_HERSHEY_COMPLEX,1,(0,0,255),2)

cv2.imshow('Elon Musk', imgElon)
cv2.imshow('Elon Test', imgTest)
cv2.waitKey(0)


# --------------------------------------------------------
# Deep Iterative Matching Network
# Licensed under The Apache-2.0 License [see LICENSE for details]
# Written by Gu Wang, Yi Li
# --------------------------------------------------------
from __future__ import print_function, division
import sys
import os

cur_dir = os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(cur_dir, "../../external/mxnet/mxnet_v00_origin"))
"""
zoom image with zoom factor
"""

import cv2
import mxnet as mx
import numpy as np

class ZoomImageWithFactorOperator(mx.operator.CustomOp):
    def __init__(self, height, width, pixel_means, high_light_center):
        super(ZoomImageWithFactorOperator, self).__init__()
        self.height = height
        self.width = width
        self.pixel_means = pixel_means
        self.high_light_center = high_light_center
        self.center_map = None
        self.spot_radius = 5

    def forward(self, is_train, req, in_data, out_data, aux):
        ctx = in_data[0].context
        if type(self.pixel_means) == np.ndarray:
            self.pixel_means = mx.nd.array(self.pixel_means, ctx=ctx)
        batch_size = in_data[0].shape[0]
        if self.center_map is None:
            self.center_map = np.zeros([batch_size, 3, self.height, self.width])
            spot_start_x = int(np.floor(float(self.width) / 2 - self.spot_radius))
            spot_end_x = int(np.ceil(float(self.width) / 2 + self.spot_radius))
            spot_start_y = int(np.floor(float(self.height) / 2 - self.spot_radius))
            spot_end_y = int(np.ceil(float(self.height) / 2 + self.spot_radius))
            self.center_map[:, 0, spot_start_y:spot_end_y, spot_start_x:spot_end_x] = 255.0  # red center
            self.center_map = mx.ndarray.array(self.center_map, ctx=ctx)
        image_real_array = in_data[1] + self.pixel_means
        image_rendered_array = in_data[2] + self.pixel_means

        grid_array = mx.ndarray.zeros([batch_size, 2, self.height, self.width], ctx=ctx)
        zoom_factor = in_data[0].asnumpy()
        for batch_idx in range(batch_size):
            wx, wy, tx, ty = zoom_factor[batch_idx]
            affine_matrix = mx.ndarray.array([[wx, 0, tx], [0, wy, ty]], ctx=ctx).reshape((1, 6))
            a = mx.ndarray.GridGenerator(
                data=affine_matrix, transform_type="affine", target_shape=(self.height, self.width)
            )
            grid_array[batch_idx] = a[0]

        zoom_image_rendered_array = mx.nd.BilinearSampler(image_rendered_array, grid_array)
        zoom_image_real_array = mx.nd.BilinearSampler(image_real_array, grid_array)
        if self.high_light_center:
            zoom_image_rendered_array = mx.ndarray.maximum(zoom_image_rendered_array, self.center_map)
        zoom_image_real_array -= self.pixel_means
        zoom_image_rendered_array -= self.pixel_means

        self.assign(out_data[0], req[0], zoom_image_real_array)
        self.assign(out_data[1], req[1], zoom_image_rendered_array)

    def backward(self, req, out_grad, in_data, out_data, in_grad, aux):
        self.assign(in_grad[0], req[0], 0)
        self.assign(in_grad[1], req[1], 0)
        self.assign(in_grad[2], req[2], 0)

@mx.operator.register("ZoomImageWithFactor")
class ZoomImageWithFactorProp(mx.operator.CustomOpProp):
    def __init__(self, width=640, height=480, pixel_means="[0 0 0]", high_light_center="False"):
        super(ZoomImageWithFactorProp, self).__init__(True)
        self.height = int(height)
        self.width = int(width)
        self.pixel_means = np.fromstring(pixel_means[1:-1], dtype=np.float32, sep=" ").reshape(3)
        self.pixel_means = self.pixel_means[::-1]
        self.pixel_means = self.pixel_means[np.newaxis, :, np.newaxis, np.newaxis]
        self.hight_light_center = high_light_center.lower() == "true"

    def list_arguments(self):
        input_list = ["zoom_factor", "image_observed", "image_rendered"]
        return input_list

    def list_outputs(self):
        output_list = ["zoom_image_observed", "zoom_image_rendered"]
        return output_list

    def infer_shape(self, in_shape):
        out_shape = [in_shape[1], in_shape[2]]

        return in_shape, out_shape, []

    def infer_type(self, in_type):
        dtype = in_type[0]
        input_type = [dtype, dtype, dtype]
        output_type = [dtype, dtype]
        return input_type, output_type, []

    def create_operator(self, ctx, shapes, dtypes):
        return ZoomImageWithFactorOperator(self.height, self.width, self.pixel_means, self.hight_light_center)

if __name__ == "__main__":
    # configs
    thresh = 1e-3
    step = 1e-4
    ctx = mx.gpu(0)
    K = np.array([[1066.778, 0, 312.9869], [0, 1067.487, 241.3109], [0, 0, 1]])
    height = 480
    width = 640
    b_zoom_se3 = False
    batch_size = 8
    img_idx = ["005_{:06}".format(x) for x in [1, 11, 21, 31, 41, 51, 61, 71]]
    sub_idx1 = ["0" for x in range(batch_size)]
    sub_idx2 = ["5" for x in range(batch_size)]
    pixel_means = 120

    # initialize layer
    image_real = mx.sym.Variable("image_real")
    image_rendered = mx.sym.Variable("image_rendered")
    zoom_factor = mx.sym.Variable("zoom_factor")
    proj2d = mx.sym.Custom(
        zoom_factor=zoom_factor,
        image_real=image_real,
        image_rendered=image_rendered,
        pixel_means=np.array([pixel_means, pixel_means, pixel_means]).flatten(),
        name="updater",
        op_type="ZoomImageWithFactor",
        height=height,
        width=width,
        high_light_center=True,
    )
    v_zoom_factor = np.ones((batch_size, 4)) * 0.8
    v_zoom_factor[:, 1] = v_zoom_factor[:, 0]
    v_zoom_factor[:, 2:] = 0.0
    v_image_real = np.zeros((batch_size, 3, height, width), dtype=np.float32)
    v_image_rendered = np.zeros((batch_size, 3, height, width), dtype=np.float32)
    for idx in range(batch_size):
        v_image_rendered[idx] = cv2.imread(
            os.path.join(
                cur_dir, "../../data/render_v5/data/rendered/0003/{}_{}-color.png".format(img_idx[idx], sub_idx1[idx])
            ),
            cv2.IMREAD_COLOR,
        ).transpose([2, 0, 1])
        v_image_real[idx] = cv2.imread(
            os.path.join(
                cur_dir, "../../data/render_v5/data/rendered/0003/{}_{}-color.png".format(img_idx[idx], sub_idx2[idx])
            ),
            cv2.IMREAD_COLOR,
        ).transpose([2, 0, 1])

    exe1 = proj2d.simple_bind(
        ctx=ctx, zoom_factor=v_zoom_factor.shape, image_real=v_image_real.shape, image_rendered=v_image_rendered.shape
    )

    # forward
    exe1.arg_dict["zoom_factor"][:] = mx.nd.array(v_zoom_factor, ctx=ctx)
    exe1.arg_dict["image_real"][:] = mx.nd.array(v_image_real - pixel_means, ctx=ctx)
    exe1.arg_dict["image_rendered"][:] = mx.nd.array(v_image_rendered - pixel_means, ctx=ctx)
    import time
    import matplotlib.pyplot as plt

    t = time.time()
    exe1.forward(is_train=True)
    zoom_image_real = exe1.outputs[0].asnumpy().astype(np.uint8)
    zoom_image_rendered = exe1.outputs[1].asnumpy().astype(np.uint8)
    print("using time: {:.2f}s".format(time.time() - t))

    for batch_idx in range(batch_size):
        im_real = v_image_real[batch_idx].transpose([1, 2, 0]).astype(np.uint8)
        im_rendered = v_image_rendered[batch_idx].transpose([1, 2, 0]).astype(np.uint8)
        z_im_real = zoom_image_real[batch_idx].transpose([1, 2, 0]) + pixel_means
        z_im_rendered = zoom_image_rendered[batch_idx].transpose([1, 2, 0]) + pixel_means
        fig = plt.figure()
        tmp = fig.add_subplot(2, 2, 1)
        tmp.set_title("image_real")
        plt.axis("off")
        plt.imshow(im_real)

        tmp = fig.add_subplot(2, 2, 2)
        tmp.set_title("image_imagine")
        plt.axis("off")
        plt.imshow(im_rendered)

        tmp = fig.add_subplot(2, 2, 3)
        tmp.set_title("image_real after zoom")
        plt.axis("off")
        plt.imshow(z_im_real)

        tmp = fig.add_subplot(2, 2, 4)
        tmp.set_title("image_imagine after zoom")
        plt.axis("off")
        plt.imshow(z_im_rendered)
        plt.show()


import streamlit as st
import gestion_BBD as bdd
import utils as ut

db = bdd.connexion(st.secrets["textkey"], "michelin-4180a")

ajout, dico_entree = ut.construction_form_sidebar()

if ajout:
    if (dico_entree['Name']!= ""):
        bdd.ecriture_data(db, 'Indicateur', dico_entree)
    else :
        st.sidebar.error("Please enter the name of an indicator !")

st.markdown('# Macro indicator research glossary')

st.write('''We are doing more and more studies to understand and visualize the different economic macro indicators.
However, in order to avoid doing the same research several times and facing unnecessary problems this small application
has been set up to list everything that has been done on this subject.''')

form_recherche = st.form("Recherche form")
texte_a_chercher = form_recherche.text_input("What do you want")
recherche = form_recherche.form_submit_button("Rechercher")

if not recherche:
    bdd.lecture_data(db, "Indicateur")

if recherche:
    docs = db.collection(u'Indicateur').where(u'Name', u'==', texte_a_chercher).stream()

    for doc in docs:
        bdd.presentation_indicateur(db, doc.to_dict())

    if(texte_a_chercher == ""):
        bdd.lecture_data(db, "Indicateur")