#!/usr/bin/env python

import serial
import select
import sys, os
import termios
import time

class Replica1Terminal:
    def __init__(self, portname):
        self.portname = portname
        self.port = serial.Serial(self.portname, 115200, timeout = 0, rtscts = 1)
        self.keyboard_fd = sys.stdin.fileno()
        self.serial_fd = self.port.fileno()
        self.terminal_defaults = termios.tcgetattr(self.keyboard_fd)

    def getkeypress(self):
        return os.read(self.keyboard_fd, 1)

    def getserialbyte(self):
        return self.port.read(1)

    def terminal_unbuffered(self):
        # put stdin into single-character mode
        new = termios.tcgetattr(self.keyboard_fd)
        new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
        new[6][termios.VMIN] = 1
        new[6][termios.VTIME] = 0
        termios.tcsetattr(self.keyboard_fd, termios.TCSANOW, new)

    def terminal_restore(self):
        termios.tcsetattr(self.keyboard_fd, termios.TCSAFLUSH, self.terminal_defaults)

    def run(self):
        self.terminal_unbuffered()
        try:
            while True:
                iw, ow, ew = select.select([self.serial_fd, self.keyboard_fd], [], [], None)
                if self.keyboard_fd in iw:
                    keypress = self.getkeypress()
                    self.process_keypress(keypress)
                if self.serial_fd in iw:
                    inbyte = self.getserialbyte()
                    self.process_terminal(inbyte)
        finally:
            self.terminal_restore()

    def process_keypress(self, key):
        if key == '\n':
            key = '\r'
        elif key == '~':
            self.terminal_restore()
            self.do_menu()
            self.terminal_unbuffered()
            return # send nothing to the serial port
        else:
            key = key.upper()
        self.port.write(key)

    def process_terminal(self, byte):
        if byte == '\r':
            byte = '\n'
        sys.stdout.write(byte)
        sys.stdout.flush()

    def do_menu(self):
        sys.stdout.write(" --- \n")
        sys.stdout.write("[REPLICA1TERM] Enter Filename: ")
        sys.stdout.flush()
        filename = sys.stdin.readline().strip()
        try:
            datafile = open(filename, 'r')
        except Exception, e:
            sys.stdout.write("[REPLICA1TERM] Failed to open file: %s\n" % str(e))
            sys.stdout.flush()
            return
        lines = list()
        for line in datafile.readlines():
            line = line.strip() + '\r'
            lines.append(line)
        dataout = ''.join(lines)
        bytecount = len(dataout)

        start = time.time()
        while len(dataout):
            iw, ow, ew = select.select([self.serial_fd], [self.serial_fd], [], 1.0)
            if self.serial_fd in iw:
                inbyte = self.getserialbyte()
                self.process_terminal(inbyte)
            if self.serial_fd in ow:
                self.port.write(dataout[:10])
                dataout = dataout[10:]
        taken = time.time() - start
        sys.stdout.write("[REPLICA1TERM] Upload completed, %.1f sec, %.1f chars/sec\n" % (taken, float(bytecount) / taken))
        sys.stdout.flush()

if len(sys.argv) == 1:
    print "Syntax: %s [port]" % sys.argv[0]
    print "eg: %s /dev/ttyUSB0" % sys.argv[0]
else:
    print "Hit Ctrl+C to terminate"
    print "Hit ~ to initiate file upload"
    t = Replica1Terminal(sys.argv[1])
    try:
        t.run()
    except KeyboardInterrupt:
        pass
