#!/usr/bin/env python3
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 8 -*- 
#
# main.py
# Copyright (C) 2019 John Coppens <john@jcoppens.com>
# 
# test_multi 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 3 of the License, or
# (at your option) any later version.
# 
# test_multi 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, see <http://www.gnu.org/licenses/>.

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Gio
import os, sys
import pdb
import socket
import fcntl
import subprocess as sp


UI_FILE = "test_multi.ui"
RTC_DEV = "/dev/rtc0"
IP      = "127.0.0.1"
PORT    = 9999
XTERM   = "xterm"

RTC_UIE_ON = 0x00007003

class RTC():
    """ RTC connects a handler to the RTC device (normally /dev/rtc0)
            <callback> parameter    if defined, will be called on each tick
            <viewer> parameter      if defined, must be a Gtk.TextView, and a
                                    character will be inserted on each tick

        NOTE: At the moment, the 'tick' signal is the default 1 second period.
            The rate can be changed with ioctl commands, but this is NOT
            implemented here for simplicity.
    """
    def __init__(self, parent, viewer = None, callback = None):
        self.parent = parent
        self.callback = callback
        self.viewer = viewer
        
        self.rtc = open(RTC_DEV, "rb")
        if not self.rtc:
            print("Can't open file:", RTC_DEV)
            exit(1)
        self.ioch = GLib.IOChannel.unix_new(self.rtc.fileno())
        
        # Datos leidos del RTC son del tipo 'long': No son caracteres validos
        # y es preciso de deshabilitar la codificacion:
        self.ioch.set_encoding(None)
        
        GLib.io_add_watch(self.ioch, GLib.IO_IN, self.read)
        
        self.enable_interrupts()


    def read(self, ioch, par2):
        try:
            data = self.ioch.read(8)    # Cada 'long' tiene 8 bytes
            self.append_text("@")
                
        except Exception as e:
            print(e)
            print("Data read contains strange things")
            
        return True


    def enable_interrupts(self):
        r = fcntl.ioctl(self.rtc, RTC_UIE_ON, 0)
        if r == -1:
            print("Could not enable interrupts")


    def append_text(self, txt):
        if self.callback != None:
            self.callback(txt)
            
        bff = self.viewer.get_buffer()
        bff.insert(bff.get_end_iter(), txt)


    def disable_interrupts(self):
        fcntl.ioctl(self.rtc, RTC_UIE_OFF, 0);



class TCP_client():
    """ The TCP client tries to connect to the server on instantiation of the
        class. So the server must be running!
            <callback> parameter    if defined, will be called with the data
                                    received on the TCP port.
            <viewer> parameter      if defined, must be a Gtk.TextView, and
                                    will be updated with the received data.
            semd method             can be called to send a message to the
                                    TCP port
    """
    def __init__(self, host, port,
                callback = None,
                viewer = None):
        self.callback = callback
        self.viewer = viewer

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.socket.connect((host, port))
        self.socket.setblocking(False)
        self.fd = self.socket.fileno()             # Obtener el file descriptor
        
        print("Connected to {}".format(host))
        GLib.io_add_watch(self.fd, GLib.IO_IN, self.process_input)
                                                   # Instalar la funcion en caso
                                                   # de datos recibidos


    def process_input(self, skt, cond):
        """ This function is called asynchronously when data is received.
        """
        msg = self.socket.recv(100)                # Recibir el mensage de la red
        if self.callback != None:
            self.callback(msg)
            
        bff = self.viewer.get_buffer()
        bff.insert(bff.get_end_iter(), msg.decode("latin-1"))
        
        return True                                # Queremos quedar activos


    def send(self, msg):
        self.socket.send(msg.encode("latin-1"))



class GUI:
    def __init__(self):
        self.builder = Gtk.Builder()
        self.builder.add_from_file(UI_FILE)
        self.builder.connect_signals(self)

        main_window = self.builder.get_object("MainWindow")
        # Make an dictionary of references to widgets we are interested in
        # from the UI file
        self.obj = {}
        for objref in ["from_RTC_buffer",
                       "from_RTC_view",
                       "from_socket_buffer",
                       "from_socket_view"]:
            self.obj[objref] = self.builder.get_object(objref)

        self.rtc = RTC(self,
                    viewer = self.obj["from_RTC_view"],
                    callback = self.process_rtc)

        self.tcp = TCP_client(IP, PORT, 
                    viewer = self.obj["from_socket_view"],
                    callback = self.process_tcp)
                    
        self.tcp.send("Simulated server (netcat) \n")

        main_window.show_all()


    def on_window_destroy(self, window):
        Gtk.main_quit()

    def process_rtc(self, data):
        """ Execute anything which has to be synced with the RTC
        """
        print(data)
        return True

    def process_tcp(self, data):
        """ Process the data coming from the server
        """
        print(data)
        return True


    def kbd_entry_activate_cb(self, entry):
        """ Send the data from the keyboard to the server (and print
            on the terminal
        """
        text = entry.get_text()
        print(text)
        self.tcp.send(text + "\n")
        entry.set_text('')
        

def main():
    sp.Popen([XTERM, '-e', 'nc', '-l', '-p', '9999'])
    gui = GUI()
    Gtk.main()
		
if __name__ == "__main__":
    sys.exit(main())