# ===============================================================================
# File: 05.py
# Language: Python 3
# Aims:
#	Create a control panel.
#	Plot the target Boltzmann curve.
#	Plot the fit.
#	Plot the first power Boltzmann curve.

# ===============================================================================
# Figure

# Import modules
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

matplotlib.use('TkAgg')
import matplotlib as mpl
import numpy as np
import sys
if sys.version_info[0] < 3:
    import Tkinter as tk
else:
    import tkinter as tk
import matplotlib.backends.tkagg as tkagg
from matplotlib.backends.backend_agg import FigureCanvasAgg

from pylab import *  # Required for figure

# Create a figure
fig = figure("Curves", figsize=(4.5, 3), dpi=72)

# Show the figure
plt.show()

# ===============================================================================
# Define Fit class

# Import modules
import numpy as np
from scipy.optimize import curve_fit

import cv2
import os

import matplotlib.pyplot as plt

from qrcode import QRColoredCode
from qrcode.util import ColorSet

from qrdecode.main import QRProcessor
from qrdecode.utils.biostrip import ColorLines, Biostrip, np
from qrdecode.utils.image import ImageProcessor
from pyctorama import TPSColorProcessor

if __name__ == '__main__':
    FILE_PATH = os.path.dirname(os.path.abspath(__file__))

    c = ColorSet()

    qr1 = QRColoredCode(color_set=c)
    qr1.add_data("AliciaMorales12345")

    im1 = qr1.make_image()

    qr2 = QRColoredCode(color_set=c)
    qr2.add_data("AliciaMorales78910")

    im2 = qr2.make_image()

    biostrip = Biostrip(os.path.join(FILE_PATH, "../", "scripts/images/2018-06-06-163719.jpg"),
                        threshold="simple",
                        output_size=300,
                        output_border=30)

    print(biostrip.scanner.image_processor.patterns.get("mean").find("position").centers)

    codes = biostrip.scanner.codes
    f = mpl.Figure.figure()

    assert isinstance(codes[0], ImageProcessor)

    f.subplot(3, 5, 1)
    f.imshow(im1.get_image())
    f.title("Original Image")

    f.subplot(3, 5, 2)
    f.imshow(codes[0].image[:, :, ::-1])
    f.title("Position Trimmed Image")

    processor1 = QRProcessor(codes[0],
                             code_border=biostrip.output_border,
                             code_offset=biostrip.output_offset,
                             code_size=biostrip.output_size
                             )
    f.subplot(3, 5, 3)
    f.title("Threshold image for analysis")
    plt.imshow(processor1.image_processor.threshold(processor1.threshold).copy(),
               interpolation="none",
               cmap="gray")

    processor1.correct_alignment()
    # Or alternatively:
    # processor.correct_fourth_corner()

    processor1.sample()

    plt.subplot(3, 5, 4)
    plt.title("Alignment corrected Image")
    plt.imshow(processor1.image[:, :, ::-1])

    plt.subplot(3, 5, 5)
    plt.title("Colored extracted Image.")
    plt.imshow(processor1.matrix, interpolation="none", cmap="gray")
    plt.imshow(cv2.cvtColor(processor1.colors, cv2.COLOR_BGR2RGB))
    src_rgb, dst_rgb = processor1.colors_extraction(qr1)

    tps = TPSColorProcessor(src_rgb, dst_rgb)
    img_corrected = tps.transform(biostrip.sliced)
    data = processor1.decode()

    assert isinstance(codes[1], ImageProcessor)

    plt.subplot(3, 5, 6)
    plt.imshow(im2.get_image())
    plt.title("Original Image")

    plt.subplot(3, 5, 7)
    plt.imshow(codes[1].image[:, :, ::-1])
    plt.title("Position Trimmed Image")

    processor2 = QRProcessor(codes[1],
                             code_border=biostrip.output_border,
                             code_offset=biostrip.output_offset,
                             code_size=biostrip.output_size
                             )
    plt.subplot(3, 5, 8)
    plt.title("Threshold image for analysis")
    plt.imshow(processor2.image_processor.threshold(processor2.threshold).copy(),
               interpolation="none",
               cmap="gray")

    processor2.correct_alignment()
    # Or alternatively:
    # processor.correct_fourth_corner()

    processor2.sample()

    plt.subplot(3, 5, 9)
    plt.title("Alignment corrected Image")
    plt.imshow(processor2.image[:, :, ::-1])

    plt.subplot(3, 5, 10)
    plt.title("Colored extracted Image.")
    plt.imshow(processor2.matrix, interpolation="none", cmap="gray")
    plt.imshow(cv2.cvtColor(processor2.colors, cv2.COLOR_BGR2RGB))
    src_rgb, dst_rgb = processor2.colors_extraction(qr2)
    #
    tps = TPSColorProcessor(src_rgb, dst_rgb)
    img_corrected = tps.transform(biostrip.sliced)

    data = processor2.decode()

    plt.show()

    # if datas[0] == "AliciaMorales78910":
    #     (h, w) = img.shape[:2]
    #     center = (w / 2, h / 2)
    #
    #     # rotate the image by 180 degrees
    #     M = cv2.getRotationMatrix2D(center, 180, 1.0)
    #     img_corrected= cv2.warpAffine(img_corrected, M, (w, h))
    #     plt.imshow(img_corrected)

    lines = ColorLines(img_corrected, model="gaussian")
    lines.mean()
    lines.solve()
    print(lines.result.fit_report())
    x = np.arange(len(lines.profile))

    plt.subplot(3, 5, 11)
    plt.plot(x, lines.profile, 'bo')
    plt.plot(x, lines.result.init_fit, 'k--')
    plt.plot(x, lines.result.best_fit, 'r-')

    plt.subplot(3, 5, 12)
    plt.imshow(biostrip.image[:, :, ::-1])
    plt.title("Original Image")

    plt.show()

# Start definition
class Fit:

    # Constructor
    def __init__(self):
        self.x0 = 0.0
        self.k = -10.0
        self.power = 1
        self.x = range(-100, 101)
        self.y = []
        self.y2 = []

    # Fit x, y to func with initial parameters in p
    def fit(self, x, y, p):
        # Fit curve
        popt, pcov = curve_fit(self.func, x, y, p0=p)

        # Generate the fit curve
        self.x0 = popt[0]
        self.k = popt[1]
        self.y = self.func(self.x, self.x0, self.k)

        # Generate first power Boltzmann curve
        power = self.power
        self.power = 1
        self.y2 = self.func(self.x, self.x0, self.k)
        self.power = power

    # Target function
    def func(self, x, x0, k):
        return (1 / (1 + np.exp((x - x0) / k))) ** self.power


# Instantiation
fit = Fit()


# ===============================================================================
# Define Boltzmann class to hold the target curve
class Boltzmann:
    # Constructor
    def __init__(self, x0=0.0, k=-10.0):
        self.x0 = x0
        self.k = k
        self.x = range(-100, 101)
        self.y = []

    # Set x and y coordinates
    def set(self):
        # self.y = [self.func(i, self.x0, self.k) for i in self.x]
        self.y = [self.func(i, self.x0, self.k) for i in self.x]

    # Boltzmann function
    def func(self, x, x0, k):
        return 1.0 / (1.0 + np.exp((x - x0) / k))


# Instantiation
bm = Boltzmann()

# ===============================================================================
# Import tkinter
from tkinter import *
from tkinter.ttk import *


# ===============================================================================
# Define the ControlPanel class
class ControlPanel:
    # Constructor
    # master is the parent widget, which is the root widget here.
    def __init__(self, master):
        # Create a frame
        frameMain = Frame(master, width=100, height=100, padding=5)
        frameMain.grid()
        rowNumber = 0

        # ===================
        # Target Curve
        rowNumber += 1
        # Create a label
        Label(frameMain, text="Target Curve").grid(row=rowNumber,
                                                   column=1,
                                                   columnspan=2)

        # ===================
        # Vhalf
        rowNumber += 1
        # Create a label
        Label(frameMain, text="Vhalf").grid(row=rowNumber, column=1)

        # Create a vhalf entry
        self.etyVhalf = Entry(frameMain)
        self.etyVhalf.insert(0, "0")
        self.etyVhalf.grid(row=rowNumber, column=2)

        # ===================
        # k
        rowNumber += 1
        # Create a label
        Label(frameMain, text="k").grid(row=rowNumber, column=1)

        # Create a k entry
        self.etyK = Entry(frameMain)
        self.etyK.insert(0, "-10")
        self.etyK.grid(row=rowNumber, column=2)

        # ===================
        # Boltzmann Curve
        rowNumber += 1
        # Create a label
        Label(frameMain, text="Fit Curve: Boltzmann to the X power").grid(
            row=rowNumber,
            column=1,
            columnspan=2)

        # =======================================================================
        # Power
        rowNumber += 2
        # Create a label
        Label(frameMain, text="Power (X: 1~4)").grid(
            row=rowNumber,
            column=1)

        # Create a power entry
        self.etyPower = Entry(frameMain)
        self.etyPower.insert(0, "1")
        self.etyPower.grid(row=rowNumber, column=2)

        # ===================
        # Vhalf
        rowNumber += 1
        # Create a label
        Label(frameMain, text="Vhalf").grid(row=rowNumber, column=1)

        # Create a vhalf entry
        self.etyVhalf2 = Entry(frameMain)
        self.etyVhalf2.grid(row=rowNumber, column=2)

        # ===================
        # k
        rowNumber += 1
        # Create a label
        Label(frameMain, text="k").grid(row=rowNumber, column=1)

        # Create a k entry
        self.etyK2 = Entry(frameMain)
        self.etyK2.grid(row=rowNumber, column=2)


        rowNumber += 1
        Label(frameMain, text="Fitting method").grid(row=rowNumber, column=1)
        combo = Combobox(frameMain)
        combo["values"] = ["Gaussian", "Loretzian", "Pseudo-Voigt", "Pearson7"]
        combo.set("Gaussian")
        combo.grid(row=rowNumber, column=2)

        # ===================
        # Plot
        rowNumber += 1
        # Create a plot button
        btnPlot = Button(frameMain,
                         text="Plot",
                         command=self.cbPlot,
                         width=5)
        btnPlot.grid(row=rowNumber, column=1)

        # ===================
        # Quit
        rowNumber += 1
        # Create a "Quit" button
        btnQuit = Button(frameMain,
                         text="Quit",
                         command=frameMain.quit,
                         width=5)
        btnQuit.grid(row=rowNumber, column=1)


        # ====================
        rowNumber += 1


    # Methods
    def cbPlot(self):
        fig.clear()

        # x0
        x0 = float(self.etyVhalf.get())
        bm.x0 = x0

        # k
        k = float(self.etyK.get())
        if (abs(k) < 1):
            if (k > 0):
                k = 1
            else:
                k = -1
            self.etyK.delete(0, END)
            self.etyK.insert(0, str(k))
        bm.k = k

        # Power
        power = int(self.etyPower.get())
        if power < 1 or power > 4:
            power = 1
            self.etyPower.delete(0, END)
            self.etyPower.insert(0, str(power))
            print("Power is set to 1.")

        fit.power = power

        # Set x and y
        bm.set()

        # Fit
        fit.fit(bm.x, bm.y, [bm.x0, bm.k])

        # Plot
        plot(bm.x, bm.y, "b-", linewidth=1)
        plot(fit.x, fit.y, "r--", linewidth=2)
        plot(fit.x, fit.y2, "g:", linewidth=2)
        axis([-100, 100, -0.1, 1.1])
        legend(["Target", "Fit", "Boltzmann"], loc="best", frameon=False,
               labelspacing=0)

        # Update GUI
        self.etyVhalf2.delete(0, END)
        self.etyVhalf2.insert(0, str(fit.x0))
        self.etyK2.delete(0, END)
        self.etyK2.insert(0, str(fit.k))

        # Print information
        print("")
        print("=== Result ===")
        print("Power = ", fit.power)
        print("Vhalf = ", fit.x0)
        print("k = ", fit.k)
        print("")

def draw_figure(canvas, figure, loc=(0, 0)):
    """ Draw a matplotlib figure onto a Tk canvas

    loc: location of top-left corner of figure on canvas in pixels.
    Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
    """
    figure_canvas_agg = FigureCanvasAgg(figure)
    figure_canvas_agg.draw()
    figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
    figure_w, figure_h = int(figure_w), int(figure_h)
    photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)

    # Position: convert from top-left anchor to center anchor
    canvas.create_image(loc[0] + figure_w / 2, loc[1] + figure_h / 2, image=photo)

    # Unfortunately, there's no accessor for the pointer to the native renderer
    tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)

    # Return a handle which contains a reference to the photo object
    # which must be kept live or else the picture disappears
    return photo


# ===============================================================================
# Create the root widget
rootWidget = Tk()
rootWidget.title("Curve Fitting")
rootWidget.geometry("+500+10")


# ===============================================================================
# Instantiate App class
controlPanel = ControlPanel(rootWidget)
canvas = Canvas(rootWidget)

# Generate some example data
X = np.linspace(0, 2 * np.pi, 50)
Y = np.sin(X)

# Create the figure we desire to add to an existing canvas
fig = mpl.figure.Figure(figsize=(2, 1))
ax = fig.add_axes([0, 0, 1, 1])
ax.plot(X, Y)

# Keep this handle alive, or else figure will disappear
fig_x, fig_y = 100, 100
fig_photo = draw_figure(canvas, fig)
fig_w, fig_h = fig_photo.width(), fig_photo.height()

# Add more elements to the canvas, potentially on top of the figure
canvas.create_line(200, 50, fig_x + fig_w / 2, fig_y + fig_h / 2)
canvas.create_text(200, 50, text="Zero-crossing", anchor="s")
canvas.grid(column=1)

# ===============================================================================
# Enter the event loop
rootWidget.mainloop()

# ===============================================================================