#	Programmer:	limodou
#	E-mail:		limodou@gmail.com
#
#	Copyleft 2005 limodou
#
#	Distributed under the terms of the GPL (GNU Public License)
#
#   NewEdit 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#	$Id$

import threading
import time

class DUMP_CLASS:pass
class AbortException(Exception):pass

class SyncVar(object):
    def __init__(self):
        self.flag = False
        self.lock = threading.Lock()
        
    def set(self, flag=True):
        self.lock.acquire()
        self.flag = flag
        self.lock.release()
        
    def isset(self):
        return self.flag
    
    def get(self):
        return self.flag
    
    def clear(self):
        self.lock.acquire()
        self.flag = False
        self.lock.release()
        
    def __ne__(self, other):
        return self.flag != other
    
    def __eq__(self, other):
        return self.flag == other

    def __nonzero__(self):
        return bool(self.flag)

class FuncThread(threading.Thread):
    def __init__(self, casing, syncvar, sync=False):
        threading.Thread.__init__(self)
        self.casing = casing
        self.syncvar = syncvar
        self.sync = sync
        
    def run(self):
        if self.sync:
            self.casing.sync_start(self.syncvar)
        else:
            self.casing.start()
        self.syncvar.clear()
    
class ProcessThread(threading.Thread):
    def __init__(self, casing, syncvar):
        threading.Thread.__init__(self)
        self.casing = casing
        self.syncvar = syncvar
        
    def run(self):
        func, args, kwargs = self.casing.on_process
        if kwargs.has_key('timestep'):
            timestep = kwargs['timestep']
            del kwargs['timestep']
        else:
            timestep = 0.5
        while 1:
            if self.syncvar:
                kwargs['syncvar'] = self.syncvar
                func(*args, **kwargs)
                time.sleep(timestep)
            else:
                break

class Casing(object):
    def __init__(self, func=None, *args, **kwargs):
        self.funcs = []
        if func:
            self.funcs.append((func, args, kwargs))
        self.on_success = None
        self.on_exception = None
        self.on_abort = None
        self.on_process = None
        
        self.syncvar = None
        self.t_func = None
        self.p_func = None
        
    def __add__(self, obj):
        assert isinstance(obj, Casing)
        self.funcs.extend(obj.funcs)
        return self
        
    def __radd__(self, obj):
        assert isinstance(obj, Casing)
        self.funcs.extend(obj.funcs)
        return self

    def append(self, func, *args, **kwargs):
        self.funcs.append((func, args, kwargs))
        
    def onsuccess(self, func, *args, **kwargs):
        self.on_success = func, args, kwargs
        
    def onexception(self, func, *args, **kwargs):
        self.on_exception = func, args, kwargs
        
    def onabort(self, func, *args, **kwargs):
        self.on_abort = func, args, kwargs

    def onprocess(self, func, *args, **kwargs):
        self.on_process = func, args, kwargs

    def start(self):
        try:
            for func, args, kwargs in self.funcs:
                ret = self._run((func, args, kwargs))
            if self.on_success:
                self._run(self.on_success)
        except AbortException:
            if self.on_abort:
                self._run(self.on_abort)
            else:
                print 'Abort'
            return
        except:
            if self.on_exception:
                self._run(self.on_exception)
            else:
                import traceback
                traceback.print_exc()
            
    def start_thread(self):
        self.syncvar = syncvar = SyncVar()
        self.syncvar.set()
        self.t_func = t = FuncThread(self, syncvar)
        self.p_func = None
        t.setDaemon(True)
        t.start()
        if self.on_process:
            self.p_func = t1 = ProcessThread(self, syncvar)
            t1.setDaemon(True)
            t1.start()
            
    def sync_start(self, syncvar):
        try:
            for func, args, kwargs in self.funcs:
                kwargs['syncvar'] = syncvar
                if not syncvar:
                    return
                self._run((func, args, kwargs))
            if self.on_success:
                self._run(self.on_success)
        except AbortException:
            if self.on_abort:
                self._run(self.on_abort)
            else:
                print 'Abort'
            return
        except:
            if self.on_exception:
                self._run(self.on_exception)
            else:
                import traceback
                traceback.print_exc()

    def start_sync_thread(self):
        self.syncvar = syncvar = SyncVar()
        self.syncvar.set()
        self.t_func = t = FuncThread(self, syncvar, sync=True)
        self.p_func = None
        t.setDaemon(True)
        t.start()
        if self.on_process:
            self.p_func = t1 = ProcessThread(self, syncvar)
            t1.setDaemon(True)
            t1.start()

    def stop_thread(self):
        if self.syncvar:
            self.syncvar.clear()
                        
    def _run(self, func):
        f, args, kwargs = func
        return f(*args, **kwargs)
    
def new_obj():
    return DUMP_CLASS()
        
if __name__ == '__main__':
    def test(n, syncvar):
        for i in range(1, n): 
            if syncvar:
                syncvar.set(i)
                print "=",i
                time.sleep(1)
            else:
                break
            
    def process(syncvar):
        print 'process...', syncvar.get()

    d = Casing(test, 10) + Casing(test, 20)
    d.onprocess(process, timestep=2)
    d.start_sync_thread()
    time.sleep(10)
    print 'stop'
    d.stop_thread()
    
