#  Name:       "Client4" : Client-Anwendung zur Didaktik der Socketprogrammierung
#  Autoren:    Marco Schneider(Gymnasium Kusel) und Michael Schlemmer(Gymnasium am Rittersberg Kaiserslautern)
#  Datum:      03.10.2010
#  Version:    4.2
#  Python:     3.1
# -*- coding: utf-8 -*-


# model

import asyncore, socket

class Model(asyncore.dispatcher):
    def __init__(self, cbVerb, cbGetr, cbEmpf):
        asyncore.dispatcher.__init__(self)
        self.buffer = b''
        self.Ausgabe = ''
        self.cbVerb = cbVerb
        self.cbGetr = cbGetr
        self.cbEmpf = cbEmpf
        self.verbunden = False
        
    def handle_close(self):
        if self.verbunden:
            self.close()
            self.cbGetr()
        
    def handle_read(self):
        ausg=str(self.recv(4096),encoding='iso8859_15')
        if ausg != '':
           self.Ausgabe = ausg.replace("\r","")  #Steuerzeichen "Carriage-Return" entfernen, da dieses als BOX in einer ASCII-Umgebung dargestellt würde. Diese Zeichen kommen nur in Folge einer unter Windows erzeugten ASCII-Datei vor, die an den Client zurückübermittelt wird. Z.B. bei Webseiten
           self.cbEmpf()
    
    def writable(self):
        return (len(self.buffer) > 0)

    def handle_write(self):
        sent = self.send(self.buffer)
        self.buffer = self.buffer[sent:]    #pop buffer

    def cbVerb(self):
        pass
      
    def cbGetr(self):
        pass

    def cbEmpf(self):
        pass
    


# view

from tkinter import *

class View(Tk):
    def __init__(self, cbVerbinde, cbSende, cbTrenne, cbHalt):
        Tk.__init__(self)

        # Callbacks
        self.cbVerbinde = cbVerbinde
        self.cbSende = cbSende
        self.cbTrenne = cbTrenne
        self.protocol("WM_DELETE_WINDOW",cbHalt)      # cbHalt wird aufgerufen, wenn das 'X' gedrückt wird

        # Fenster
        self.title("Socket Client Version 4")
        self.geometry('687x609')

        # Labels
        self.lEmpfangen = Label(master=self, text="Verbindungsprotokoll", anchor=W, justify=LEFT)
        self.lEmpfangen.place(x=16, y=134, width=200, height=15)

        self.lAdresse = Label(master=self, text="Adresse", font=("Arial", 12), anchor=W, justify=LEFT)
        self.lAdresse.place(x=16, y=12, width=65, height=18)

        self.lremotePort = Label(master=self, text="Port", font=("Arial", 12), anchor=W, justify=LEFT)
        self.lremotePort.place(x=16, y=44, width=65, height=18)

        self.lzuSenden = Label(master=self, text="zu senden", anchor=W, justify=LEFT)
        self.lzuSenden.place(x=452, y=134, width=80, height=13)

        self.lMeldungen = Label(master=self, text="Meldungen", anchor=W, justify=LEFT)
        self.lMeldungen.place(x=16, y=486, width=80, height=15)

        self.lStatus = Label(master=self, text="", font=("Arial", 12, "bold"), fg="red", anchor=W, justify=LEFT)
        self.lStatus.place(x=210, y=72, width=100, height=25)

        # Entries
        self.eHost = Entry(master=self, background="white")
        self.eHost.place(x=88, y=8, width=121, height=21)
        self.eHost.insert(0,"www.hsg-kl.de")

        self.ePort = Entry(master=self, background="white")
        self.ePort.place(x=88, y=40, width=121, height=21)
        self.ePort.insert(0,"80")

        # Buttons
        self.bLoesche = Button(master=self, text="Ausgaben löschen", command=self.saeubern)
        self.bLoesche.place(x=523, y=104, width=150, height=25)

        self.bVerbinde = Button(master=self, text="Verbinden")
        self.bVerbinde.bind('<ButtonRelease>',self.cbVerbinde)
        self.bVerbinde.place(x=88, y=72, width=105, height=25)

        self.bSende = Button(master=self, text="Senden")
        self.bSende.bind('<ButtonRelease>',self.cbSende)
        self.bSende.place(x=210, y=104, width=220, height=25)
        self.bSende.config(state='disabled')

        self.bTrenne = Button(master=self, text="Trennen")
        self.bTrenne.bind('<ButtonRelease>',self.cbTrenne)
        self.bTrenne.place(x=88, y=104, width=105, height=25)
        self.bTrenne.config(state='disabled')

        # Textwidgets und Scrollbars
        self.tSenden = Text(master=self, background="white")
        self.tSenden.place(x=452, y=152, width=206, height=329)
        self.tSenden.insert(END,'GET /abc.html HTTP1.1\n')
        self.tSenden.insert(END,'Host: www.hsg-kl.de\n')
        self.scrollbar1 = Scrollbar(master=self, orient=VERTICAL)
        self.tSenden.config(yscrollcommand=self.scrollbar1.set)
        self.scrollbar1.place(x=658, y=152, width=15, height=329)
        self.scrollbar1.config(command=self.tSenden.yview)

        self.tEmpfangen = Text(master=self, background="white")
        self.tEmpfangen.place(x=16, y=152, width=406, height=329)
        self.tEmpfangen.tag_config("senden", background="#FFDEAD")
        self.tEmpfangen.tag_config("getrennt", background="#FF3300")
        self.tEmpfangen.tag_config("empfangen", background="#ADD8E6")
        self.tEmpfangen.tag_config("aufgebaut", background="#7CFC00")
        self.tEmpfangen.tag_config(SEL, background="#FFD700")
        self.scrollbar2 = Scrollbar(master=self, orient=VERTICAL)
        self.tEmpfangen.config(yscrollcommand=self.scrollbar2.set)
        self.scrollbar2.place(x=422, y=152, width=15, height=329)
        self.scrollbar2.config(command=self.tEmpfangen.yview)
        

        self.tMeldungen = Text(master=self, background="white")
        self.tMeldungen.place(x=16, y=504, width=642, height=81)
        self.scrollbar3 = Scrollbar(master=self, orient=VERTICAL)
        self.tMeldungen.config(yscrollcommand=self.scrollbar3.set)
        self.scrollbar3.place(x=658, y=504, width=15, height=81)
        self.scrollbar3.config(command=self.tMeldungen.yview)

        # Timer starten
        self.after(0,self.poll)
        
    def poll(self):
            asyncore.poll(.1)
            self.after(1, self.poll)

    def saeubern(self):
            self.tEmpfangen.delete(1.0,END)
            self.tMeldungen.delete(1.0,END)

# controller

import time

class Controller(object):

    def __init__(self):
        self.model = Model(self.verbunden, self.getrennt, self.ausgeben)
        self.view = View(self.verbinde, self.sende, self.trenne, self.Halt)
        self.ausloeser = True
        self.view.mainloop()

    def verbinde(self,event):
        if self.view.bVerbinde.cget( 'state' ) == 'active':
           self.model.create_socket(socket.AF_INET, socket.SOCK_STREAM)
           self.model.connect((self.view.eHost.get(),int(self.view.ePort.get())))
           self.verbunden()

    def verbunden(self):
        self.model.verbunden=True
        self.view.lStatus.config(text="verbunden")
        self.view.tEmpfangen.insert(END, '--- Socket-Verbindung aufgebaut ---\n', 'aufgebaut')
        self.view.tEmpfangen.see(END)
        self.view.bSende.config(state='active')
        self.view.bTrenne.config(state='active')
        self.view.bVerbinde.config(state='disabled')
                
    def sende(self,event):
        if self.view.bSende.cget( 'state' ) == 'active':
           text=self.view.tSenden.get("1.0",END)
           self.view.tEmpfangen.insert(END, text, 'senden')
           self.view.tEmpfangen.see(END)
           self.view.tSenden.delete(1.0,END)
           text=text.replace('\n','\r\n')              # Carriage Return und Line Feed veranlassen das Absenden des Puffers
           self.model.buffer = bytes(text,encoding='iso8859_15')

    def trenne(self,event):
        self.ausloeser = False
        self.model.handle_close()
        self.model.verbunden=False

    def getrennt(self):
        self.view.lStatus.config(text="getrennt")
        if self.ausloeser:
            werwars = 'Server'
        else:
            werwars = 'Client'
        self.ausloeser = True
        self.view.tMeldungen.insert(END, "Verbindung wurde vom "+werwars+" um "+time.strftime("%H:%M:%S Uhr",time.localtime())+" getrennt.\n")
        self.view.tEmpfangen.insert(END, '--- Socket-Verbindung-getrennt ---\n', 'getrennt')
        self.view.tEmpfangen.see(END)
        self.view.bSende.config(state='disabled')
        self.view.bTrenne.config(state='disabled')
        self.view.bVerbinde.config(state='active')

    def ausgeben(self):
           self.view.tEmpfangen.insert(END, self.model.Ausgabe+'\n', 'empfangen')
           self.view.tEmpfangen.see(END)
    
    def Halt(self):                                   # Aufräumarbeiten
        self.model.handle_close()                     # Schnittstelle schließen
        self.view.quit()                              # mainloop beenden
        self.view.destroy()                           # Fenster beseitigen

# Hauptprogramm

c = Controller()

