import socket, optparse, threading

__version__ = '0.1.0'
versionstring = 'VNCheck %s' % __version__

scks = []

class TaskThread(threading.Thread):
    """Thread that executes a task every N seconds"""
    
    def __init__(self):
        threading.Thread.__init__(self)
        self._finished = threading.Event()
        self._interval = 15.0
    
    def setInterval(self, interval):
        """Set the number of seconds we sleep between executing our task"""
        self._interval = interval
    
    def shutdown(self):
        """Stop this thread"""
        self._finished.set()
    
    def run(self):
        while 1:
            if self._finished.isSet(): return
            self.task()
            
            # sleep for interval or until shutdown
            self._finished.wait(self._interval)
    
    def task(self):
        """The task done by this thread - override in subclasses"""
        pass

class ReaquireThread(TaskThread):
    def __init__(self, threadnum=None):
        TaskThread.__init__(self)
        self.threadnum = threadnum + 1
    
    def spawn_conn(self):
        try:
            if options.debug:
                print 'Rthread: Opening threaded connection #%s' % str(self.threadnum)
            sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sck.connect((host, port))
            serve_forever(implicit_silence=True)
        except socket.error:
            if options.debug:
                print 'Rthread: Opening threaded connection #%s failed' % str(self.threadnum)
        
    def task(self):
        self.spawn_conn()

def Reaquire():
    if options.verbode or options.debug:
        print 'NotImplemented'

class KillerThread(threading.Thread):
    def __init__(self, threadnum=None):
        threading.Thread.__init__(self)
        self.threadnum = threadnum + 1
    
    def spawn_conn(self):
        try:
            if options.debug:
                print 'Kthread: Opening threaded connection #%s' % str(self.threadnum)
            sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sck.connect((host, port))
        except socket.error:
            if options.debug:
                print 'Kthread: Opening threaded connection #%s failed' % str(self.threadnum)
    
    def run(self):
        self.spawn_conn()
        serve_forever(implicit_silence=True)

def connect():
    for i in xrange(options.connections):
        if options.verbose or options.debug:
            print 'Opening connection #%s' % str(i+1)
        sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sck.connect((host, port))
        scks.append(sck)

def close():
    for sck, number in zip(scks, range(len(scks))):
        if options.verbose or options.debug:
            print 'Closing connection #%s' % str(number + 1)
        sck.close()

def serve_forever(implicit_silence=False):
    """Sets the program on waiting - with all open connections"""
    if options.verbose or options.debug:
        if not implicit_silence:
            print 'Use Ctrl+C to quit'
    while True:
        pass

def banner():
    print versionstring

def main():
    if options.verbose:
        banner()
    
    if not options.noconnect:
        connect()
        if options.debug:
            print 'Sockets after connect: %s' % str(scks)
    
    # the TaskThread code
    for threadnum in xrange(options.rthreads):
        rt = ReaquireThread(threadnum)
        rt.setInterval(options.delay)
        rt.start()
    
    # KillerThread spawning code - if --kthreads not zero
    kths = []
    for threadnum in xrange(options.kthreads):
        kt = KillerThread(threadnum)
        kths.append(kt)
    
    # start these KillerThreads
    for kthread in kths:
        kthread.start()
    
    # TimerThread code - disabled by now
    #tt = threading.Timer(5.0, Reaquire)
    #tt.start()
    
    try:
        serve_forever()
    except KeyboardInterrupt:
        if not options.noclose or options.noconnect:
            close()
            
def parseargs():
    parser = optparse.OptionParser(version=versionstring)
    parser.add_option("-m", "--machine", action="store", dest="host",
                            default="127.0.0.1:5900", help="IP:Port to connect")
    parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
                            default=False, help="be chatty")
    parser.add_option("-q", "--quiet", action="store_false", dest="verbose",
                            help="shut up, default")
    parser.add_option("-d", "--debug", action="store_true", dest="debug",
                            default=False, help="show debugging output")
    parser.add_option("-t", "--tthreads", action="store", dest="tthreads",
                            default=0, type='int',
                            help="use number of TimerThreads, experimental, yet they only work once, default is zero")
    parser.add_option("-r", "--rthreads", action="store", dest="rthreads",
                            default=0, type='int',
                            help="use number of ReaquireThreads, experimental, does probably not work, default is zero")
    parser.add_option("--delay", action="store", type="float", dest="delay",
                            default=5.0, help="delay of Timer- and TaskThreads, in seconsds, float, default 5")
    parser.add_option("-k", "--kthreads", action="store", dest="kthreads",
                            default=0, type='int',
                            help="use number of KillerThreads, experimental, does probably not work, default is zero")
    parser.add_option("-n", "--noconnect", action="store_true", dest="noconnect",
                            default=False, 
                            help="do not regularly connect at start, not recommended")
    parser.add_option("--noclose", action="store_true", dest="noclose",
                            default=False, 
                            help="do not regularly close at end, no big difference")
    parser.add_option("-c", "--connections", action="store", type="int", dest="connections",
                            default=6, help="use number of regular connections, default is 6 for Ultr@VNC")
    parser.add_option("--max", action="store_true", dest="maxconns",
                            default=False, 
                            help="get the maximum connections a VNC server allows (switches to another mode)")
                            
    global options
    (options, args) = parser.parse_args()
    
    # get the host ip and port
    global host, port
    host = options.host.split(':')
    port = int(host[1])
    host = host[0]

def getmaxconns():
    if options.verbose:
        banner()
    maxconns = 0
    socks = []
    while True:
        try:
            if options.debug:
                print 'Trying connection #%s' % str(maxconns)
            sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sck.connect((host, port))
            socks.append(sck)
            maxconns += 1
        except socket.error:
            print 'Maximum connections: %s' % str(maxconns)
            break

if __name__ == '__main__':
    parseargs()
    if options.maxconns:
        getmaxconns()
    else:
        main()