#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  client.py
#  
#  Copyright 2014 John Coppens <john@jcoppens.com>
#  
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#  
#  

import gtk
import pygtk
import sys
import gobject, glib
import os, os.path

FIFO_ROOT = "./fifo"

class Client():
    """ The Client class takes care of our communications by FIFO.
    """
    def __init__(self, fifo_root):
        """ Create the FIFO
        """
        self.fifo_root = fifo_root

        # First check if the client socket is left over from a previous session
        if os.path.exists(self.fifo_root + '.client'):
            os.remove(self.fifo_root + '.client')
        # Then create a new one
        os.mkfifo(self.fifo_root + '.client')

        # Then check if the server has created a fifo
        if os.path.exists(self.fifo_root + '.server'):
            self.out_fd = os.open(self.fifo_root + '.server',
                                  os.O_WRONLY | os.O_APPEND)
            self.out_f = os.fdopen(self.out_fd, "w")
            print "opening client for read"
            self.in_fd = os.open(self.fifo_root + '.client',
                                 os.O_RDONLY | os.O_NONBLOCK, 0o644)
            self.in_f = os.fdopen(self.in_fd)
            print "client open"
        else:
            print "Server is not running!"
            sys.exit(1)

        self.show_status_cb = None
        self.show_data_cb = None

        glib.io_add_watch(self.in_fd, glib.IO_IN, self.process_input)

    def process_input(self, port, cond):
        """ This function is called asynchronously when data is received.
        """
        msg = self.in_f.readline()                # Get the message from the FIFO
        self.show_data(msg)                        # And send it to MainWindow
        return True                                # We want to stay alive

    def send(self, msg):
        self.out_f.write(msg)
        self.out_f.flush()

    def show_status(self, msg):
        """ Show status changes from this class, either on the terminal,
            or else by calling the callback installed by set_status_show().
        """
        if self.show_status_cb == None:
            print "Status: ", msg
        else:
            self.show_status_cb(msg)

    def show_data(self, msg):
        """ Show received messages, either on the terminal,
            or else by calling the callback installed by set_data_show().
        """
        if self.show_data_cb == None:
            print "Data: ", msg
        else:
            self.show_data_cb(msg)

    def set_status_show(self, show_status_cb):
        self.show_status_cb = show_status_cb

    def set_data_show(self, show_data_cb):
        self.show_data_cb = show_data_cb


class MainWindow(gtk.Window):
    def __init__(self):
        gtk.Window.__init__(self);
        self.connect("delete-event", self.destroy);
        self.set_size_request(400, 300)

        # Make the upper window, which the received text.
        txtview = gtk.TextView()
        self.txtbff = gtk.TextBuffer()
        self.txttagtable = self.txtbff.get_tag_table()
        txtview.set_buffer(self.txtbff)
        txtview.set_can_focus(False)

        texttag_red = gtk.TextTag("red")
        texttag_red.set_property("foreground", "red")
        self.txttagtable.add(texttag_red)

        texttag_black = gtk.TextTag("black")
        texttag_black.set_property("foreground", "black")
        self.txttagtable.add(texttag_black)

        # This is where the text to be transmitted is typed.
        # Pressing the 'Enter' key transmits the string.
        entry = gtk.Entry()
        entry.connect("activate", self.entry_activated);

        # And the button to exit the chat program
        button = gtk.Button("Quit")
        button.set_border_width(4)
        button.set_can_focus(False)
        button.connect("clicked", self.quit_clicked)

        # Left of the button is a label, which can be used to
        # show status messages.
        hbox = gtk.HBox()
        self.statlbl = gtk.Label("")
        hbox.pack_start(self.statlbl, expand = True, fill = True)
        hbox.pack_start(button, expand = False)

        # Pack everything into a VBox, then that into the window.
        vbox = gtk.VBox()
        vbox.set_spacing(4)
        vbox.pack_start(txtview)
        vbox.pack_start(entry, expand = False)
        vbox.pack_start(hbox, expand = False)
        self.add(vbox)

        entry.grab_focus()
        self.show_all()

    def destroy(self, window, data):
        """ Called when the MainWindow is closed - this stops the
            GTK+ main loop, and terminates the program
        """
        gtk.main_quit()

    def entry_activated(self, widget):
        msg = widget.get_text() + "\n"
        self.client.send(msg)
        self.show_data(msg, color = 'black')
        widget.set_text("")

    def show_status(self, msg):
        self.statlbl.set_text(msg)    

    def show_data(self, msg, color = 'red'):
        self.txtbff.insert_with_tags_by_name(self.txtbff.get_end_iter(), msg, color)    

    def set_client(self, client):
        """ The client is the communication system which wants to show
            things here, and get strings to transmit from the GtkEntry
        """
        self.client = client
        # We connect callbacks to get news from the client
        client.set_status_show(self.show_status)
        client.set_data_show(self.show_data)

    def quit_clicked(self, widget):
        gtk.main_quit()

    def run(self):
        gtk.main()


def main():
    main = MainWindow()               # Prepare the main window,
    client = Client(FIFO_ROOT)        # and the communications package
    main.set_client(client)           # Announce the client to the main window

    main.run()

    return 0

if __name__ == '__main__':
    main()