#/usr/bin/env python
# -*- Python -*-

import sys
from omniORB import CORBA, URI
from omniORB import any

import OpenRTM
import RTC


from CorbaNaming import *
import SDOPackage

# class RtmEnv :
#       rtm environment manager
#       orb, naming service, rtc proxy list
#
class RtmEnv : 

    def __init__(self, orb_args, name_server="localhost", 
                                     orb=None, naming=None):
        if not orb :
            orb = CORBA.ORB_init(orb_args)
        self.orb = orb
        if not naming :
            naming = CorbaNaming(self.orb, name_server) 
        self.naming = naming
                # could have more than one naming service
        self.b_len = 10 # iteration cut off no.
        self.rtc_handles = {}

    def __del__(self):
        self.orb.shutdown(wait_for_completion=CORBA.FALSE)
        self.orb.destroy()

    # get_object(name:str, Class:type(RTC.RTObject))
    def get_object_by_name(self, name, Class=RTC.RTObject):
        ref = self.naming.resolveStr(name)
        if ref is None: return None     # return CORBA.nil ?
        return ref._narrow(Class)

    def list_rtc(self) :
        self.rtc_handes = {}
        return self.list_rtc1(self.naming._rootContext, "")

    def list_rtc1(self, name_context, parent) :
        if not name_context :
            name_context = self.naming._rootContext 
        rslt = []
        b_list = name_context.list(self.b_len)
        for bd in b_list[0] :
            rslt = rslt + self.proc_bd(bd, name_context, parent)
        if b_list[1] :  # iterator : there exists remaining.
            t_list = b_list[1].next_n(self.b_len)
            while t_list[0] :
                for db in t_list[1] :
                    rslt = rslt + self.proc_bd(bd, name_context, parent)
                t_list = b_list[1].next_n(self.b_len)
        return rslt

    def proc_bd(self, bd, name_context, parent) :
        rslt = []
        pre = ""
        if parent :
            pre = URI.nameToString(parent) + "/"
        if bd.binding_type == CosNaming.nobject :
            tmp = name_context.resolve(bd.binding_name)
            try :
                tmp = tmp._narrow(RTC.RTObject)
                if tmp :
                   nam = pre + URI.nameToString(bd.binding_name)
                   rslt = [[nam, tmp]]
                   self.rtc_handles[nam]=RtcHandle(nam,self,tmp)
                   print 'handle for '+nam+' was created.'
                else :
                   pass
            except :
                print 'is '+nam+' alive?'
                pass
        else :
            tmp = name_context.resolve(bd.binding_name)
            tmp = tmp._narrow(CosNaming.NamingContext)
            rslt = self.list_rtc1(tmp, parent + bd.binding_name)
        return rslt
#
# data conversion
#
def nvlist2dict(nvlist) :
    rslt = {}
    for tmp in nvlist :
        rslt[tmp.name]=tmp.value.value()   # nv.value and any.value()
    return rslt
def dict2nvlist(dict) :
    rslt = []
    for tmp in dict.keys() :
        rslt.append(SDOPackage.NameValue(tmp, any.to_any(dict[tmp])))
    return rslt
#
#  connector, port, inport, outport, service
#                

class Connector :
    def __init__(self, plist, name = None, id="", prop_dict={}) :
       self.plist = plist
       self.port_reflist = [tmp.port_profile.port_ref for tmp in plist]
       if name :
           self.name = name
       else :
           self.name = string.join([tmp.name for tmp in plist],'_')
       self.prop_dict = prop_dict
       self.nego_prop()
       self.prop_nvlist = dict2nvlist(self.prop_dict)
       self.profile = RTC.ConnectorProfile(self.name, id, self.port_reflist, self.prop_nvlist)

    def nego_prop(self) :
       self.possible = True
       for kk in self.def_prop :
           if kk in self.prop_dict :
               if not self.prop_dict[kk] :
                   self.prop_dict[kk]=self.def_prop[kk]
           else :
                self.prop_dict[kk]=self.def_prop[kk]
           for pp in self.plist :  
               if not ((self.prop_dict[kk] in pp.prop[kk]) or 
                                 ('Any' in    pp.prop[kk])) :
                   self.prop_dict[kk] = ""
                   self.possible = False
    def connect(self) :
#
#   out and inout parameters are retuned as a tuple   
#
       ret, self.profile = self.port_reflist[0].connect(self.profile)
       self.prop_nvlist = self.profile.properties
       self.prop_dict = nvlist2dict(self.prop_nvlist)
    def disconnect(self) :
       ret = self.port_reflist[0].disconnect(self.profile.connector_id)

class IOConnector(Connector) :
    def __init__(self, plist, name = None, id="", prop_dict={}) :
       self.def_prop = {'dataport.dataflow_type':'Push' ,
                        'dataport.interface_type':'CORBA_Any' ,
                        'dataport.subscription_type':'Flush'}
       Connector.__init__(self, plist, name, id, prop_dict)

class ServiceConnector(Connector) :
    def __init__(self, plist, name = None, id="", prop_dict={}) :
       self.def_prop = {'port.port_type':'CorbaPort' }
       Connector.__init__(self, plist, name, id, prop_dict)


class Port :
    def __init__(self, profile,nv_dict=None) :
        self.name=profile.name    
        self.port_profile = profile
        if not nv_dict :
            nv_dict = nvlist2dict(profile.properties)
        self.prop = nv_dict

class CorbaServer :
    def __init__(self, profile, port) :
        self.profile = profile
        self.port = port
        self.name = profile.instance_name
        self.type = profile.type_name
        self.ref = None
        ref_key = 'port.' + self.type + '.' + self.name
        self.ref=self.port.con.prop_dict[ref_key]
    def narrow_ref(self) :
        narrow_sym = eval('_GlobalIDL.' + self.type)
        self.ref = self.ref_narrow(narrow_sym)

class CorbaClient :
    def __init__(self, profile) :
        self.profile = profile
        self.name = profile.instance_name
        self.type = profile.type_name

class RtcService(Port) :
    def __init__(self, profile,nv_dict=None) :
        Port.__init__(self, profile, nv_dict)
        self.get_info()
        tmp = self.port_profile.interfaces
        for itf in tmp :
            if itf.polarity == 'PROVIDED' :
                self.provided[itf.instance_name] = CorbaServer(itf,self)
            elif itf.polarity == 'REQUIRED' :
                self.required[itf.instance_name] = CorbaClient(itf,self)
    def get_info(self) :
        self.con = ServiceConnector([self])
        self.con.connect()
        self.con.disconnect()

class RtcInport(Port) :
    def __init__(self, profile, nv_dict=None) :
        Port.__init__(self, profile, nv_dict)
        self.get_info() 
        self.ref = self.con.prop_dict['dataport.corba_any.inport_ref']
    def get_info(self) :
        self.con = IOConnector([self])
        self.con.connect()
        self.con.disconnect()

class RtcOutport(Port) :
    def __init__(self, profile,nv_dict=None) :
        Port.__init__(self, profile, nv_dict)
        self.get_info()
        self.ref = self.con.prop_dict['dataport.corba_any.outport_ref']
    def get_info(self) :
        self.con = IOConnector([self])
        self.con.connect()
        self.con.disconnect()

class Pipe :
    pass
#
# RtcHandle
#
class RtcHandle :
  def __init__(self, name, env, ref=None) :
    self.name = name
    self.env = env
    if ref :
        self.rtc_ref = ref
    else :
        self.rtc_ref = env.naming.resolve(name)._narrow(RTC.RTObject)
    self.configulation_ref = None
    self.retrieve_info()
  def retrieve_info(self) :
    self.port_refs = []
    self.execution_contexts =[]
    if self.rtc_ref :
       self.port_refs = self.rtc_ref.get_ports()  
                # this includes inports, outports and service ports
       self.execution_contexts = self.rtc_ref.get_contexts()
    self.ports = {}
    self.services = {}
    self.inports = {}
    self.outports = {}
    for pp in self.port_refs :
       tmp = pp.get_port_profile()
       tmp_prop = nvlist2dict(tmp.properties)
#       self.ports[tmp.name]=Port(tmp, tmp_prop)
       if tmp_prop['port.port_type']=='DataInPort' :
            self.inports[tmp.name]=RtcInport(tmp,tmp_prop)
#            self.inports[tmp.name]=Port(tmp, tmp_prop)
       elif tmp_prop['port.port_type']=='DataOutPort' :
            self.outports[tmp.name]=RtcOutport(tmp, tmp_prop)
#            self.outports[tmp.name]=Port(tmp, tmp_prop)
       elif tmp_prop['port.port_type']=='CorbaPort' :
            self.services[tmp.name]=RtcService(tmp, tmp_prop)
#            self.services[tmp.name]=Port(tmp, tmp_prop)
  def activate(self):
      self.execution_contexts[0].activate_component(self.rtc_ref)
  def deactivate(self):
      self.execution_contexts[0].deactivate_component(self.rtc_ref)
#
#
#
class PipeOut(Pipe):
    def __init__(self, name, data_buf, size=8) :
        self.name =  name
        self.data =  data_buf
        self.OpenRTM.InPort(name,data_buf,OpenRTM.RingBuffer(size))