#!/usr/bin/env python3
"""
author: Kamil Cukrowski, 2016

"""

from tkinter import *
import tkinter
import tkinter.ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import numpy as np
import inference
import fuzzy_sets
from matplotlib import cm
import random


class guiOnButtonDefuzyfikacja(tkinter.Tk):
	def __init__(self, parent):
		self.parent = parent
	
	def show(self, frame):
		self.frame = frame
		self.parent.resetFrame(frame)
		##
		interfere_functext = self.convertInferenceFuncToText(self.parent.ruleset.inference_func)
		##
		f = Frame(frame)
		f.place(in_=frame, anchor="c", relx=.5, rely=.5)
		##
		l=tkinter.Label(f, text='Deffuzifikacja aktualna: '+interfere_functext, justify=LEFT)
		l.grid(row=0,column=0,columnspan=3)
		##
		tkinter.Label(f, text='Ustaw deffuzifikacje: ').grid(row=1,column=0)
		list=['COG','LOM','MOM','FOM']
		inference_func = StringVar()
		inference_func.set(interfere_functext)
		tkinter.OptionMenu(f,inference_func,*list).grid(row=1,column=1)
		##
		b=tkinter.Button(f, text="Ustaw", command= lambda: self.OnButtonRegulyOdswierz(inference_func.get()))
		b.grid(row=1,column=2)
	
	def OnButtonRegulyOdswierz(self,text):
		self.parent.ruleset.inference_func = self.convertTextToInferenceFunc(text)
		self.show(self.frame)
		
	def convertTextToInferenceFunc(self, text):
		if ( text == 'COG' ):
			return inference.cog
		if ( text == 'MOM' ):
			return inference.mom
		if ( text == 'LOM' ):
			return inference.lom
		if ( text == 'FOM' ):
			return inference.fom
		raise ValueError('unexistent inference function : '+text)
	
	def convertInferenceFuncToText(self, inference_func):
		if ( inference_func == inference.cog ):
			return 'COG'
		if ( inference_func == inference.mom ):
			return 'MOM'
		if ( inference_func == inference.lom ):
			return 'LOM'
		if ( inference_func == inference.fom ):
			return 'FOM'
		raise ValueError('unexistent inference function : '+inference_func)

class guiOnButtonReguly(tkinter.Tk):
	def __init__(self, parent):
		self.parent = parent
		
	def show(self, frame):
		self.parent.resetFrame(frame)
		self.frame = frame
		##
		frame.update()
		top = Frame(frame, width = frame.winfo_width(), height = frame.winfo_height()-50)
		top.pack(side=TOP, fill=BOTH, expand=True)
		b1 = Frame(frame, width = frame.winfo_width(), height = 20)
		b1.pack(side=BOTTOM, fill=X)
		b1.pack_propagate(False)
		b2 = Frame(frame, width = frame.winfo_width(), height = 20)
		b2.pack(side=BOTTOM, fill=X)
		b2.pack_propagate(False)
		##
		tkinter.Label(top, text='Reguły:').pack()
		view = Frame(top)
		view.pack(fill=BOTH, expand=True)
		S = Scrollbar(view)
		T = Text(view, width=1000)
		S.pack(side=RIGHT, fill=BOTH, expand=True)
		T.pack(side=LEFT, fill=BOTH, expand=True)
		S.config(command=T.yview)
		T.config(yscrollcommand=S.set)
		self.T = T
		self.refreshText()
		##
		tkinter.Label(b1, text='Usuń regułę numer: ').pack(side=LEFT)
		e = StringVar(); e.set('1');
		tkinter.Entry(b1, textvariable=e).pack(side=LEFT)
		tkinter.Button(b1, text='usuń', command= lambda: self.onButtonUsunRegule(e)).pack(side=LEFT)
		##
		tkinter.Label(b2, text='Jeśli prędkość jest ').pack(side=LEFT)
		v1 = self.addOptionMenu(b2, self.parent.dom_predkosc);
		tkinter.Label(b2, text=' i widocznosc jest ').pack(side=LEFT)
		v2 = self.addOptionMenu(b2, self.parent.dom_widocznosc);
		tkinter.Label(b2, text=' i przyczepnosc jest ').pack(side=LEFT)
		v3 = self.addOptionMenu(b2, self.parent.dom_przyczepnosc);
		tkinter.Label(b2, text=' to ryzyko jest ').pack(side=LEFT)
		v4 = self.addOptionMenu(b2, self.parent.dom_ryzyko);
		tkinter.Button(b2, text='dodaj regułę',command= lambda: self.onButtonDodajRegule(v1.get(),v2.get(),v3.get(),v4.get())).pack(side=LEFT)
		
	def refreshText(self):
		self.T.delete("1.0", END)
		self.T.insert(END, self.rulesString())
		
	def addOptionMenu(self, frame, domain):
		list_=[]
		for fuzzyset in domain.fuzzy_sets:
			list_.append(fuzzyset.name)
		var = StringVar()
		var.set(list_[0])
		tkinter.OptionMenu(frame,var,*list_).pack(side=LEFT)
		return var
		
	def onButtonDodajRegule(self, predkosc, widocznosc, przyczepnosc, ryzyko):
		self.parent.ruleset.add_rule_in_order([predkosc,widocznosc,przyczepnosc,ryzyko])
		self.refreshText()
		
	def onButtonUsunRegule(self, entry):
		num = int(entry.get());
		if num < 1 | num > len(ruleset.rules):
			return
		del self.parent.ruleset.rules[num-1]
		self.refreshText()
		
	def rulesString(self):
		ruleset = self.parent.ruleset
		string=""
		for j in range(len(ruleset.rules)):
			rule = ruleset.rules[j]
			string += '%2d'%(j+1)+". Jeśli "
			
			for i in range(len(ruleset.domains)-1):
				string += ruleset.domains_list[i].linguistic_name+" jest "+rule.input_sets[i].name + " i "
			string = string[:-3]; # remove last ' i '
			i=-1;
			string += " to "+ruleset.domains_list[i].linguistic_name+" jest "+rule.output_set.name+'\n';
		return string
	
class guiOnButtonFPrzynaleznosci(tkinter.Tk):
	def __init__(self, parent):
		self.parent = parent
		##
		self.fuzzyName = ""
		self.fuzzyNameList = ""
		##
		self.fuzzyTypeList=[]
		for type in ['trójkątna', 'prostokątna', 'trapezowa', 'Guassa']:
			self.fuzzyTypeList.append(type)
		self.fuzzyType = StringVar()
		self.fuzzyType.set(self.fuzzyTypeList[0])
		##
		self.values=[]
		for i in [0,1,2,3]:
			e = StringVar()
			e.set('0')
			self.values.append( e )
	
	def show(self, frame, dom):
		self.frame = frame
		self.dom = dom
		##
		self.parent.resetFrame(frame)
		##
		self.fuzzyNameList=[]
		for fuzzyset in dom.fuzzy_sets:
			self.fuzzyNameList.insert(0,fuzzyset.name)
		self.fuzzyName = StringVar()
		self.fuzzyName.set(self.fuzzyNameList[0])
		##
		frame.update()
		top = Frame(frame, width = frame.winfo_width(), height = frame.winfo_height()-50)
		top.pack(side=TOP, fill=BOTH)
		top.pack_propagate(False)
		bottom = Frame(frame, width = frame.winfo_width(), height = 50)
		bottom.pack(side=BOTTOM, fill=BOTH)
		top.pack_propagate(False)
		
		#  nasz plot
		fig = plt.Figure(figsize=(15,15), dpi=60)
		subplot = fig.add_subplot(1,1,1)
		dom.addPlotsToSubplot(subplot)
		canvas = FigureCanvasTkAgg(fig, top)
		toolbar = NavigationToolbar2TkAgg(canvas, top)
		toolbar.pack(side=TOP, fill=BOTH)
		canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH)
		# i dziubki do edycji!
		
		tkinter.Label(bottom,text="Funkcja do edycji:").grid(column=0,row=0)
		tkinter.OptionMenu(bottom,self.fuzzyName,*self.fuzzyNameList).grid(column=0,row=1)
		
		tkinter.Label(bottom,text="Kształt funkcji").grid(column=1,row=0)
		tkinter.OptionMenu(bottom,self.fuzzyType,*self.fuzzyTypeList).grid(column=1,row=1)
		
		tkinter.Label(bottom,text="Wartości punktów").grid(column=2,row=0,columnspan=5)
		
		for i in range(0,len(self.values)):
			tkinter.Entry(bottom, textvariable=self.values[i], width=5).grid(column=2+i,row=1)
		
		b=tkinter.Button(bottom, text="Odśwież", 
			command= lambda: self.onButtonFPrzynaleznosciOdswierz(dom, self.fuzzyName.get(), self.fuzzyType.get(), self.values))
		b.grid(column=7,row=1)
	
	def refresh(self):
		return self.show(self.frame, self.dom)
	
	def onButtonFPrzynaleznosciOdswierz(self, dom, fuzzyName, fuzzyType, values):
		val = []
		for i in range(0, len(values)):
			try:
				val.append( float(values[i].get()) )
			except ValueError:
				print("Bad input")
				return
		old_set = dom.get_set(fuzzyName)
		if fuzzyType == 'trójkątna':
			f_set = fuzzy_sets.TriangleSet(old_set.range_min, val[0], val[1], val[2], old_set.range_max, old_set.name)
		if fuzzyType == 'prostokątna':
			f_set = fuzzy_sets.RectangleSet(old_set.range_min, val[0], val[1], old_set.range_max, old_set.name)
		if fuzzyType == 'trapezowa':
			f_set = fuzzy_sets.TrapezoidSet(old_set.range_min, val[0], val[1], val[2], val[3], old_set.range_max, old_set.name)
		if fuzzyType == 'Guassa':
			f_set = fuzzy_sets.GaussSet(old_set.range_min, val[0], val[1], old_set.range_max, old_set.name)
		dom.change_set(fuzzyName, f_set)
		self.refresh()
		
class guiOnButtonPlaszczyznaSterowan(tkinter.Tk):
	def __init__(self, parent):
		self.parent = parent
		
	def show(self, frame):
		frame.update()
		top = Frame(frame, width = frame.winfo_width(), height = frame.winfo_height()-50)
		top.pack(side=TOP, fill=BOTH)
		top.pack_propagate(False)
		bottom = Frame(frame, width = frame.winfo_width(), height = 50)
		bottom.pack(side=BOTTOM, fill=Y)
		bottom.pack_propagate(False)
		##
		tkinter.Label(bottom, text='Przyczepność: ').pack(side=LEFT)
		tmp = self.parent.dom_przyczepnosc.fuzzy_sets[0]
		scale = tkinter.Scale(bottom, 
			from_=tmp.range_min, to=tmp.range_max, 
			resolution=(tmp.range_max-tmp.range_min)/500,
			orient=HORIZONTAL)
		scale.pack(fill=X, expand=True, side=LEFT)
		tkinter.Button(bottom, text='Rysuj', command= lambda: self.refreshPlot(top, scale.get())).pack(side=LEFT)
		##
		l=tkinter.Label(top, text=(
			"Aby narysować wykres,\n"
			"ustaw parametr przyczepność i wciśnij przycisk 'Rysuj'\n"
			"\n"
			"Generacja wykresu może trochę potrwać!\n"))
		l.pack(fill=BOTH, expand=True, anchor=CENTER)
			
	def refreshPlot(self, frame, przyczepnosc):
		# draw 3d graph of 2 rule input 1 rule output
		num_gridpoints = 25
	
		vels = np.linspace(self.parent.dom_predkosc.fuzzy_sets[0].range_min,
			self.parent.dom_predkosc.fuzzy_sets[0].range_max, num_gridpoints)
		viss = np.linspace(self.parent.dom_widocznosc.fuzzy_sets[0].range_min, 
			self.parent.dom_widocznosc.fuzzy_sets[0].range_max, num_gridpoints)
		vels, viss = np.meshgrid(vels, viss)
	
		risks = np.ones((num_gridpoints, num_gridpoints))
	
		inference = self.parent.ruleset.inference
		for x in range(num_gridpoints):
			for y in range(num_gridpoints):
				risks[x, y] = inference([vels[x][y], viss[x][y], przyczepnosc])
	
		fig = plt.figure(figsize=(15,15), dpi=60)
		fig.canvas.set_window_title('Wykres 3D dla przyczepności = '+'%.2f'%przyczepnosc)
		ax = fig.add_subplot(111,projection="3d")
		ax.set_xlabel("Prędkość [km/h]")
		ax.set_ylabel("Widoczność [km]")
		ax.set_zlabel("Prawdopodobieństwo wypadku [%]")
		ax.set_zlim([self.parent.dom_ryzyko.fuzzy_sets[0].range_min, self.parent.dom_ryzyko.fuzzy_sets[0].range_max]);
		ax.plot_surface(vels, viss, risks, rstride=1, cstride=1, cmap=cm.coolwarm)
		fig.show()
		plt.show()
		
		#~ self.parent.resetFrame(frame)
		#~ canvas = FigureCanvasTkAgg(fig, frame)
		#~ toolbar = NavigationToolbar2TkAgg(canvas, frame)
		#~ toolbar.pack(side=TOP, fill=BOTH)
		#~ canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH)

class guiOnButtonGeneratorLiczb():
	def __init__(self,parent):
		self.parent = parent;
		self.predkosc = StringVar();
		self.widocznosc = StringVar();
		self.przyczepnosc = StringVar();
		self.ryzyko = StringVar();
		self.debug = StringVar();
	def show(self, inframe):
		frame = tkinter.Frame(inframe);
		frame.place(in_=inframe, anchor="c", relx=.5, rely=.5);
		tkinter.Label(frame,text="Wyznacz wyjście dla wejść:\n").grid(column=0, row=0, columnspan=2)
		tkinter.Label(frame,text="Prędkość ").grid(column=0, row=1)
		tkinter.Entry(frame, textvariable=self.predkosc).grid(column=1, row=1)
		tkinter.Label(frame,text="Widoczność ").grid(column=0, row=2)
		tkinter.Entry(frame, textvariable=self.widocznosc).grid(column=1, row=2)
		tkinter.Label(frame,text="Przyczepność ").grid(column=0, row=3)
		tkinter.Entry(frame, textvariable=self.przyczepnosc).grid(column=1, row=3)
		tkinter.Label(frame,text="Prawdopodobieństwo wypadku ").grid(column=0, row=4)
		tkinter.Entry(frame, textvariable=self.ryzyko).grid(column=1, row=4)
		
		tkinter.Label(frame,text="\n").grid(column=0, row=5)
		
		b=tkinter.Button(frame, text="Wygeneruj losowe wartości", command=self.onButtonWygeneruj)
		b.grid(column=0, row=6)
		b=tkinter.Button(frame,text="Wyznacz", command=self.onButtonWyznacz)
		b.grid(column=1, row=6)
		
		tkinter.Label(frame,textvariable=self.debug).grid(column=0, row=8,columnspan=2)
		
	def myRandrange(self, fuzzy_set):
		return round(fuzzy_set.range_min+random.random()*fuzzy_set.range_max, 3);
	def onButtonWygeneruj(self):
		fuzzy_set = self.parent.dom_predkosc.fuzzy_sets[0];
		self.predkosc.set( self.myRandrange(fuzzy_set) );
		fuzzy_set = self.parent.dom_widocznosc.fuzzy_sets[0];
		self.widocznosc.set( self.myRandrange(fuzzy_set) );
		fuzzy_set = self.parent.dom_przyczepnosc.fuzzy_sets[0];
		self.przyczepnosc.set( self.myRandrange(fuzzy_set) );
		self.ryzyko.set("");
		
	
	def onButtonWyznacz(self):
		try:
			pre = float(self.predkosc.get());
			fuzzy_set = self.parent.dom_predkosc.fuzzy_sets[0];
			if pre < fuzzy_set.range_min or pre > fuzzy_set.range_max:
				raise ValueError;
		except ValueError:
			self.debug.set('Zla wartosc predkości.');
			return
		try:
			wid = float(self.widocznosc.get());
			fuzzy_set = self.parent.dom_widocznosc.fuzzy_sets[0];
			if wid < fuzzy_set.range_min or wid > fuzzy_set.range_max:
				raise ValueError;
		except ValueError:
			self.debug.set('Zla wartosc widoczności.');
			return
		try:
			prz = float(self.przyczepnosc.get());
			fuzzy_set = self.parent.dom_przyczepnosc.fuzzy_sets[0];
			if prz < fuzzy_set.range_min or prz > fuzzy_set.range_max:
				raise ValueError;
		except ValueError:
			self.debug.set('Zla wartosc przyczepności.');
			return
		self.ryzyko.set( self.parent.ruleset.inference( [pre, wid, prz ] ) );
		
	

class gui(tkinter.Tk):
	width=700
	height=600
	
	def __init__(self,parent,fuzzy_system):
		tkinter.Tk.__init__(self,parent)
		[dom_predkosc, dom_widocznosc, dom_przyczepnosc, dom_ryzyko, ruleset] = fuzzy_system
		self.dom_predkosc = dom_predkosc
		self.dom_widocznosc = dom_widocznosc
		self.dom_przyczepnosc = dom_przyczepnosc
		self.dom_ryzyko = dom_ryzyko
		self.ruleset = ruleset
		self.parent = parent
		self.guiOnButtonFPrzynaleznosci = guiOnButtonFPrzynaleznosci(self)
		self.guiOnButtonReguly = guiOnButtonReguly(self)
		self.guiOnButtonPlaszczyznaSterowan = guiOnButtonPlaszczyznaSterowan(self)
		self.guiOnButtonDefuzyfikacja = guiOnButtonDefuzyfikacja(self)
		self.guiOnButtonGeneratorLiczb = guiOnButtonGeneratorLiczb(self)
		self.initialize()
		
		
	def initialize(self):
		self.geometry('{}x{}'.format(self.width, self.height))
		
		self.lf = Frame(self, bg = "light gray", width = 100, height = self.height)
		self.lf.pack(side=LEFT, fill=Y, padx=1, pady=1)
		self.initLeftFrame(self.lf)
		
		self.rf = Frame(self, bg = "light gray", width = (self.width-100), height = self.height)
		self.rf.pack(side=RIGHT, anchor=CENTER, expand=True, fill=BOTH, padx=3, pady=3)
		
		quote=("Program zaliczeniowy na przedmiot PSZT.\n"
			"\n\n\n\n\n"
			"Wykonali: Kamila Cipior, Kamil Cukrowski, Michał Kostrzewa\n"
			"Prowadząca: p. Joanna Panasiuk \n"
			"Rok wykonania projektu: 2016\n")
		l=tkinter.Label(self.rf,text=quote, justify=RIGHT, font=("Helvetica", 14))
		l.pack(fill=BOTH, expand=True, anchor=CENTER)
		
		
	def initLeftFrame(self,lf):
		tkinter.Label(lf,text="\nMenu:\n\n-- Funkcje przynależności --").pack(fill=X)
		tkinter.Button(lf,text="prędkość", command= lambda: self.onButtonFPrzynaleznosci(self.dom_predkosc)).pack(fill=X)
		tkinter.Button(lf,text="widoczność", command= lambda: self.onButtonFPrzynaleznosci(self.dom_widocznosc)).pack(fill=X)
		tkinter.Button(lf,text="przyczepność", command= lambda: self.onButtonFPrzynaleznosci(self.dom_przyczepnosc)).pack(fill=X)
		tkinter.Button(lf,text="ryzyko", command= lambda: self.onButtonFPrzynaleznosci(self.dom_ryzyko)).pack(fill=X)
		tkinter.Label(lf,text="\n").pack(fill=X)
		tkinter.Label(lf,text="-- Reguły --").pack(fill=X)
		tkinter.Button(lf,text="Wyświetl", command=self.OnButtonReguly).pack(fill=X)
		tkinter.Button(lf,text="Defuzyfikacja", command=self.OnButtonDefuzyfikacja).pack(fill=X)
		tkinter.Label(lf,text="\n").pack(fill=X)
		tkinter.Label(lf,text="-- Wyostrzanie --").pack(fill=X)
		tkinter.Button(lf,text="Wyostrzanie liczb", command=self.OnButtonGeneratorLiczb).pack(fill=X)
		tkinter.Button(lf,text="Wykres 3D", command=self.OnButtonPlaszczyznaSterowan).pack(fill=X)
		tkinter.Label(lf,text="\n").pack(fill=X)
	
	def OnButtonDefuzyfikacja(self):
		self.resetFrame(self.rf)
		self.guiOnButtonDefuzyfikacja.show(self.rf)
	
	def OnButtonPlaszczyznaSterowan(self):
		self.resetFrame(self.rf)
		self.guiOnButtonPlaszczyznaSterowan.show(self.rf)
		
	def resetFrame(self,frame):
		for widget in frame.winfo_children():
			widget.destroy()
		
	def OnButtonReguly(self):
		self.resetFrame(self.rf)
		self.guiOnButtonReguly.show(self.rf)

	def onButtonFPrzynaleznosci(self,dom):
		self.resetFrame(self.rf)
		self.guiOnButtonFPrzynaleznosci.show(self.rf, dom)
	
	def on_resize(self,event):
		self.resetFrame(self.rf)
		
	def OnButtonGeneratorLiczb(self):
		self.resetFrame(self.rf)
		self.guiOnButtonGeneratorLiczb.show(self.rf)

