import numpy as np, matplotlib.pyplot as plt, konstanter as k, regning as regn

def read_data(filename):
    with open(filename,'r') as file:
        lines = np.array([line.split() for line in file.readlines()])
    data = np.array([np.zeros(3) for line in lines])
    brytning_lister = [0 for line in lines]

    for i in range(len(lines)):
        data[i][0] = float(lines[i][0])
        data[i][1] = float(lines[i][1])

        line_data = [float(x) for x in lines[i]]
        data[i][2] = sum(line_data[2:])/len(line_data[2:])
        brytning_lister[i] = line_data[2:]

    data = np.array(data)
    data = np.transpose(data)
    return data, brytning_lister

def gen_coeff():
    # finner molfraksjon som funksjon av brytningsindex
    # returnerer koeffisienter til lineærregresjonen og kovariansmatrisen
    data, brytning_lister = read_data('tuva_sin_kalib.txt')

    V_iso = []
    V_mb = []
    brytning = []

    # gjør litt fancy greier fordi det ikke er like mange målinger
    # ved hver konsentrasjon
    for i in range(len(brytning_lister)):
        for j in range(len(brytning_lister[i])):
            V_iso.append(data[0][i])
            V_mb.append(data[1][i])
            brytning.append(brytning_lister[i][j])

    V_iso = np.array(V_iso)
    V_mb = np.array(V_mb)
    brytning = np.array(brytning)

    # Finner molfraksjon og standardavvik i hvert punkt for isopropanol
    x_iso, s_x_iso = regn.x_iso(V_iso=V_iso, V_mb=V_mb, usikkerhet=True)

    #########  Lineærregresjon ##########
    coeff, cov = np.polyfit(brytning, x_iso, 1, cov=True)

    ######## plotter lineærregresjonen for å se at den er brukbar
    x_fit = np.linspace(min(brytning), max(brytning), 3)
    y_fit = coeff[0] * x_fit + coeff[1]

    upper = (coeff[0] + (np.sqrt(cov[0][0]))) * x_fit + (coeff[1] + (np.sqrt(cov[1][1])))
    lower = (coeff[0] - (np.sqrt(cov[0][0]))) * x_fit + (coeff[1] - (np.sqrt(cov[1][1])))
    plt.fill_between(x_fit, lower, upper, color='black', alpha=0.2)

    plt.plot(x_fit, y_fit, color='black')
    plt.errorbar(brytning, x_iso, yerr=s_x_iso, xerr=k.s_brytning, color='black', linestyle='', capsize=2)

    plt.ylabel(r'$x_{iso}$ / [-]')
    plt.xlabel('Brytningsindeks / [-]')
    plt.savefig('kalibreringskurve_2')

    plt.show()

    return coeff, cov

def x_iso_fra_brytning(brytning):
    coeff, cov = gen_coeff()

    x_iso = coeff[0] * brytning + coeff[1]

    x_iso_matrise = np.vstack([(brytning ** (len(coeff) - 1 - i)) for i in range(len(coeff))])

    s_x_iso = np.dot(x_iso_matrise.T, np.dot(cov, x_iso_matrise))

    return x_iso, np.sqrt(np.diag(s_x_iso))

def x_iso_fra_brytning2(brytning):
    # Tar inn en brytningsindex og returnerer tilhørende molfraksjon av isopropanol
    # sammen med standardavvik
    coeff,cov = gen_coeff()
    x_iso = (brytning - coeff[1])/coeff[0]

    # standardavvik i koeffisientene, hvor
    # x_iso = (brytning - b)/a

    s_a = np.sqrt(cov[0][0])
    s_b = np.sqrt(cov[1][1])

    print('s_a :',s_a)
    print('s_b :', s_b)

    #standardavvik i x_iso
    s_x_iso = np.sqrt(((-brytning/(coeff[0]**2)) * s_a)**2 + ((1/coeff[0])*k.s_brytning)**2 + ((-1/coeff[0])*s_b)**2)

    print('s_iso :', s_x_iso)

    return x_iso, s_x_iso

def gen_coeff2():
    # finner molfraksjon som funksjon av brytningsindex
    # returnerer koeffisienter til lineærregresjonen og kovariansmatrisen
    data, brytning_lister = read_data('kalibreringsdata.txt')

    V_iso = []
    V_mb = []
    brytning = []

    # gjør litt fancy greier fordi det ikke er like mange målinger
    # ved hver konsentrasjon
    for i in range(len(brytning_lister)):
        for j in range(len(brytning_lister[i])):
            V_iso.append(data[0][i])
            V_mb.append(data[1][i])
            brytning.append(brytning_lister[i][j])

    V_iso = np.array(V_iso)
    V_mb = np.array(V_mb)
    brytning = np.array(brytning)

    #V_iso = data[0]
    #V_mb = data[1]
    #brytning = data[2]

    # Finner molfraksjon og standardavvik i hvert punkt for isopropanol
    x_iso, s_x_iso = regn.x_iso(V_iso=V_iso,V_mb=V_mb, usikkerhet=True)

    #########  Lineærregresjon ##########
    coeff, cov = np.polyfit(x_iso,brytning,1,cov = True)

    ######## plotter lineærregresjonen for å se at den er brukbar
    # x_fit = np.linspace(min(x_iso),max(x_iso),3)
    # y_fit = coeff[0]*x_fit + coeff[1]
    #
    # upper = (coeff[0] + np.sqrt(cov[0][0]))*x_fit + (coeff[1] + np.sqrt(cov[1][1]))
    # lower = (coeff[0] - np.sqrt(cov[0][0])) * x_fit + (coeff[1] - np.sqrt(cov[1][1]))
    #
    # plt.plot(x_fit, y_fit, color='black')
    # plt.errorbar(x_iso, brytning, xerr=s_x_iso, yerr=k.s_brytning, color='black', linestyle='', capsize = 2)
    # plt.fill_between(x_fit, lower, upper, color='black', alpha = 0.2)
    # plt.xlabel(r'$x_{iso}$ / [-]')
    # plt.ylabel('Brytningsindeks / [-]')
    # plt.savefig('kalibreringskurve')
    # plt.show()

    return coeff,cov

def x_iso_2(brytning):
    coeff, cov = gen_coeff2()

    x_iso = coeff[0]*brytning + coeff[1]

    s_a = np.sqrt(cov[0][0])
    s_b = np.sqrt(cov[1][1])

    #print('s_a :', s_a)
    #print('s_b :', s_b)

    s_x_iso = np.sqrt((coeff[0] * k.s_brytning)**2 + (brytning * s_a)**2 + s_b**2)
    #print('s_iso_2 : ', s_x_iso)

    return x_iso, s_x_iso
