#!/usr/bin/env python3

from tkinter import *
from tkinter import ttk


# --------------------------
# Validator
# --------------------------
class EntryValidator:
    def __init__(self, app):
        self.app = app

    def check_entry(self, what: str = ""):
        """ Validiert, ob Eingabe leer oder eine Fließkommazahl ist.

        :param what: str eingegebene Zeichenkette
        :return: boolean
        """
        self.app.controller.switch_status(1)
        if what == "":
            # Nichts eingegeben
            return True
        else:
            try:
                # DEBUG
                print(what)
                what = what.replace(',', '.')
                float(what)
                return True
            except ValueError:
                return False


# --------------------------
# Model
# --------------------------
class FeetToMeterData:
    feet: float
    meter: float

    def __init__(self):
        self.feet = 0.0
        self.meter = 0.0

    def get_meter(self):
        return self.meter

    def get_feet(self):
        return self.feet

    def set_feet(self, newfeet: float = 0.0):
        self.feet = newfeet
        self.calculate_meter()
        return True

    def set_meter(self, newmeter: float = 0.0):
        self.meter = newmeter
        return True

    def calculate_meter(self):
        m_value = 0.3048 * self.feet
        m_value = round(m_value, 2)
        self.set_meter(m_value)
        return True

    def print_data(self):
        print("{} ft = {} m".format(self.feet, self.meter))


# --------------------------
# Controller
# --------------------------
class FeetToMeterController:
    def __init__(self, app):
        self.app = app
        self.status = 1  # 1 - neue Eingabe, 2 - Eingabe verarbeitet

    def switch_status(self, new_status: int = None):
        """ Verändert den globalen Status

        :param new_status: neuer Statuswert
        :return: None
        """
        if not new_status:
            if self.status == 1:
                self.status = 2
            else:
                self.status = 1
        else:
            self.status = new_status

        self.app.update_statusbar(self.status)

    def calculate(self, *args):
        """ Berechnet aus der eingegebenen Länge in feet die Länge in Metern.

        :param args:
        :return: None
        """
        try:
            stringvalue = self.app.feet.get()
            stringvalue = stringvalue.replace(",", ".")
            value = float(stringvalue)
            self.app.data.set_feet(value)
            m_value = self.app.data.get_meter()
            self.app.meters.set(m_value)
            # Berechnung abgeschlossen.
            self.app.data.print_data()
            self.switch_status(2)
        except ValueError:
            pass
        return True


# --------------------------
# View / Main Application
# --------------------------
class FeetToMeter(Tk):
    def __init__(self):
        super().__init__()
        self.controller = FeetToMeterController(self)
        self.validator = EntryValidator(self)
        self.data = FeetToMeterData()

        self.title("Feet to Meters")

        # Textvariablen, um Inhalte der Eingabefelder zu kontrollieren
        self.feet = StringVar()
        self.meters = StringVar()

        # Event Handler (Validator) registrieren
        self.check_entry_wrapper = self.register(self.validator.check_entry)

        # Zugriff auf Stilinformationen
        self.s = ttk.Style()
        # Zugriff auf Statusleiste
        self.frame_statusbar = ttk.Frame(self)

        # Aufbau des Fensters
        self.create_styles()
        self.create_widgets()
        self.bind_events()

    def create_styles(self):
        """ Erstellen der speziellen Stil-Konfigurationen.

        :return:
        """
        self.s.configure('aFrame.TFrame', background="yellow")
        self.s.configure('bFrame.TFrame', background="green")
        self.s.configure('cFrame.TFrame', background="red")
        return True

    def create_widgets(self):
        """ Erstellen der Fenster-Bestandteile.

        :return:
        """
        # Rahmen im Hauptfenster (aus ttk für Farbanpassung)
        mainframe = ttk.Frame(self, padding="3 3 12 12")
        mainframe.grid(column=0, row=0, sticky=N + W + S + E)

        # mainframe darf sich ausdehnen.
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        # Eingabefeld für Länge in feet
        feet_entry = ttk.Entry(mainframe, width=7, textvariable=self.feet,
                               validatecommand=(self.check_entry_wrapper, '%P'),
                               validate='key')
        feet_entry.grid(column=2, row=1, sticky=W + E)

        # Einheit-Label für Eingabefeld der Länge in feet
        ttk.Label(mainframe, text="feet").grid(column=3, row=1, sticky=W)

        # Label für Ausgabefeld der Länge in Metern
        ttk.Label(mainframe, text="ist äquivalent zu").grid(column=1, row=2, sticky=E)

        # Einheit-Label für Ausgabefeld der Länge in Metern
        ttk.Label(mainframe, text="Meter").grid(column=3, row=2, sticky=W)

        # Ausgabefeld für Länge in Metern
        meters_entry = ttk.Entry(mainframe, width=7, textvariable=self.meters)
        meters_entry.grid(column=2, row=2, sticky=(W, E))
        meters_entry.configure(state='readonly')  # keine Eingabe, aber selektierbar

        # Button für Berechnung

        button_calc = ttk.Button(mainframe, text="Berechne", command=self.controller.calculate)
        button_calc.grid(column=3, row=3, sticky=W)

        # schönere Abstände
        for child in mainframe.winfo_children():
            child.grid_configure(padx=5, pady=5)

        # Statusbar
        st = self.frame_statusbar
        st['relief'] = 'sunken'
        st['height'] = 12
        st['style'] = 'aFrame.TFrame'
        st.grid_propagate(0)  # Feste Größe an Grid-Packer weitergeben
        st.grid(column=0, row=1, sticky=W + S + E)

        # Setze Fokus in Eingabefeld
        feet_entry.focus()

    def bind_events(self):
        """ Ereignisse an Kommandos binden.

        :return:
        """
        self.bind("<Return>", self.controller.calculate)
        self.bind("<KP_Enter>", self.controller.calculate)
        self.bind("<Escape>", self.close)

    def update_statusbar(self, status: int = 1):
        """ Passt die Farbe der Statuszeile an den Status an.

        :return: None
        """
        if status == 1:
            self.frame_statusbar['style'] = 'aFrame.TFrame'
        elif status == 2:
            self.frame_statusbar['style'] = 'bFrame.TFrame'
        else:
            self.frame_statusbar['style'] = 'cFrame.TFrame'

    def close(self, event=None):
        self.destroy()


# --------------------------
# Starting Point
# --------------------------
if __name__ == "__main__":
    app = FeetToMeter()
    app.mainloop()