#!/usr/bin/python

# MOLE CONSOLE (c) 2004 Tom Schouten
# This program is free software covered under Version 2
# of the GNU General Public Licence. See the file COPYING for details


# A minimalistic mole console
# This sets up a (one way) network connection to a mole object
# (through a netreceive object in the pd patch)
# Each input line is terminated with ";\n"

# Tab completion is available for all visible commands




import socket
import readline
import time
import re
import os
import sys



class console:
    def __init__(self, host, port, command):
        print "\nMOLE Console. (c) 2004 Tom Schouten <mole@zzz.kotnet.org>"
        readline.parse_and_bind("tab: complete")
        readline.set_completer(self.rl_completer)
        # this is not rl_basic_word_break_characters ...
        readline.set_completer_delims(' \t\n')
        self.words = []
        self.target = (host, port)
        self.command = command
        self.dictfile = "/tmp/dictionary.txt"
        self.historyfile = "~/.mole-history"
        try:
            readline.read_history_file(
                os.path.expanduser(self.historyfile))
        except IOError:
            print "using history file %s" % (self.historyfile)


    def cleanup(self):
        print "\nClosing console...",
        readline.write_history_file(
            os.path.expanduser(self.historyfile))
        self.sockobj.close()
        print "Bye.\n"

    def remove_dictionary_file(self):
        try:
            os.remove (self.dictfile)
        except OSError:
            pass

    def send_command(self, command):
        try:
            self.sockobj.send(command)
        except socket.error:
            self.sock_recover()

    def get_wordlist(self):
        # wait for reply file to appear
        # it would be much cleaner to do this with a socket
        # but that's a bit overkill.. just need this wordlist
        retries = 30  # give it 3 seconds
        while retries:
            try:
                f = open(self.dictfile, 'r')
                break
            except IOError:
                if retries == 20:
                    sys.stdout.write('Waiting for wordlist.')
                if retries <= 20:
                    sys.stdout.write('.')
                    sys.stdout.flush()
                time.sleep(.1)   # don't wait too long
                retries = retries - 1

        if retries:
            line = f.readline()
            self.words = line.split()
            f.close()
        else:
            sys.stdout.write(' timeout. Mole not responding.\n')


    def get_words(self):
        self.remove_dictionary_file()
        self.send_command("<s> %s save-words;\n" % self.dictfile)
        self.get_wordlist()
        self.remove_dictionary_file()

        
    def rl_completer(self, text, state):

        if state == 0:
            self.get_words()
            self.matcher = re.compile(text)
        
        while 1:
            try:
                word = self.words.pop()
            except IndexError:
                return 0
            if self.matcher.match(word) != None :
                return word


    def sock_recover(self):
        print "Connection closed."
        self.sockobj.close()
        self.sockobj = None
        self.connect()

    def mainloop(self):
        while 1:
            try:
                line = raw_input("mole> ")
            except EOFError:
                break
            except KeyboardInterrupt:
                break

            self.send_command("%s;\n" % line)

            
    def connect(self):

        # create a new socket object for each connection
        self.sockobj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print "\nConnecting to pd://%s:%d " % self.target,
        sys.stdout.flush()
        
        while 1:
            try:
                self.sockobj.connect(self.target)
                print "\nConnection established.\n"
                break
            except socket.error:

                try:
                    if (self.command):
                        os.system(self.command)   # start server
                    time.sleep(1)
                except KeyboardInterrupt:
                    c.cleanup()
                    sys.exit(1)

            sys.stdout.write('.')
            sys.stdout.flush()
                

def getarg(n, default):
    if len(sys.argv) > n:
        return sys.argv[n]
    else:
        return default

# get arguments
host    =      getarg(1, "localhost")
port    = eval(getarg(2, "6666"))
command =      getarg(3, "")

# start
c = console(host, port, command)
c.connect()
c.mainloop()
c.cleanup()



