import msched.core.tools as tl
import msched.core.ctree as ct
import msched.core.stage as sg
import msched.core.controller as cn

import sys,time,mpi4py
from mpi4py import MPI

###############################################################################
###############################################################################

def build_network(state):
    if not check_network(state,'build_network','build','up'):return True
    state['foremen'] = {}
    for sgn in state['stagemap']:
        stage = state['stagemap'][sgn]
        sport = MPI.Open_port(MPI.INFO_NULL)
        foreman = state['radio'].spawn(sgn,sport)
        foreman.acmd('idle',encycle,'')
        finfo = stage.tree
        fprot = finfo.protocols['dispatch']
        foreman.send('proto',(fprot,finfo))
    state['graph'].state = 'up'
    return True

def tear_network(state):
    if not check_network(state,'tear_network','tear'):return True
    if state['tearwait']:
        for sgn in state['stagemap']:
            stage = state['stagemap'][sgn]
            while not stage.finished(state):
                tl.log(0,'tear_network is waiting for stage',sgn)
                time.sleep(1.0)
                state['last'] = sgn
                state['radio'].channels[sgn](state,'passive')
            state['radio'].disband(sgn)
    else:state['radio'].disband(state['stagemap'].keys())
    state['graph'].state = 'down'
    return True

def check_network(state,f,d,w = 'down',m = '%s()->cannot %s using %s network'):
    g = state['graph']
    if g.state == w:
        tl.log(2,'cannot comply while network is %s' % (d),w)
        return False
    return True

def connect_to_server(state):
    s = state['pinfo']
    schan = state['radio'].connect(s,'passive','monitor')
    #schan.acmd('build',build_network,'build network')
    #schan.acmd('')
    #sinstr = {
    #    'pass'  : (None,              lambda s:True),
    #    'e'     : ('send out an echo',   echoserver),
    #    'o'     : ('order work',      enqueueserver),
    #    'r'     : ('report results',   reportserver),
    #    's'     : ('order a stage',orderstageserver)}
    return True

def disconnect_from_server(state):
    state['radio'].disconnect(state['pinfo'])
    return True

def order_stage(state):
    r,l = state['radio'],state['last']
    stageinfo = r.channels[l].recv('active')
    return state['controller'].astage(state,stageinfo)

def enqueue(state):
    r,l = state['radio'],state['last']
    subqueue = r.channels[l].recv('active')
    if not check_network(state,'enqueue','enqueue'):return True
    for sgn,warg in subqueue:
        state['stagemap'][sgn].enqueue(state,warg)
    return True

def report(state):
    if not check_network(state,'report','report'):return True
    r,l = state['radio'],state['last']
    for fman in r.below:
        stage = state['stagemap'][fman]
        stage.status(state)
    return True

def stage_info(state):
    r,l = state['radio'],state['last']
    sinfo = state['stagemap'].keys()
    r.sendto(l,sinfo)
    return True

def encycle(state):
    r,l = state['radio'],state['last']
    stage = state['stagemap'][l]
    fres = r.channels[l].recv('active')
    if not stage.inform(state,fres):
        tl.log(4,'inform stage failure')
        return False
    return True

###############################################################################
###############################################################################

# a protocol for the root vertex of a process hierarchy
class monitor_protocol(cn.controller):

    def astage(self,state,sinfo):
        sgn,sgw,sgt = sinfo
        state['stagemap'][sgn] = sg.stage(sgn,sgw,sgt)
        g = state['graph']
        g.avert(g.root,sgn,state['stagemap'][sgn])

        print 'STAGE ORDER FROM!!',sinfo
        print 'at astage',state['stagemap']

        return True

    def init(self,state):
        g = ct.ctree('monitor')
        state['graph'] = g
        state['results'] = []
        state['tearwait'] = True
        state['stagemap'] = sg.stages(state)
        for sgn in state['stagemap']:
            g.avert(g.root,sgn,state['stagemap'][sgn])
        s = state['pinfo']
        cinit = cn.controller.init(self,state)
        abv = state['radio'].above
        abv.schm = 'passive'
        abv.acmd('build',build_network,'')
        abv.acmd('tear',tear_network,'')
        abv.acmd('serverconnect',connect_to_server,'')
        abv.acmd('serverdisconnect',disconnect_from_server,'')
        abv.acmd('order',order_stage,'')
        abv.acmd('sinfo',stage_info,'')
        abv.acmd('enqueue',enqueue,'')
        abv.acmd('report',report,'')
        return cinit

    def work(self,state):
        if state['graph'].state == 'up':
            for sgn in state['stagemap']:
                stage = state['stagemap'][sgn]
                state['radio'].sendto(sgn,'enqueue')
                while stage.undeployed(state):
                    jb = stage.deploy(state)
                    state['radio'].sendto(sgn,jb)
                state['radio'].sendto(sgn,'enqueueend')
        time.sleep(0.1)
        return True

# create and run a monitor protocol
def monitor(s):
    mprot = monitor_protocol(s['name']+':monitor')
    mprot.run(state = s)
    
###############################################################################
###############################################################################

if __name__ == '__main__':pass

###############################################################################





