import cmq, couchdb, copy

'''
because we have memoise,
this system will emit code and use its ID for memoise:call 

ordering a new execution:
    genius solution
    once terminated, all execution trace become a function whom return its result
    this is memoizing
    once new execution trace emitted the id servebas method name of a new rpc call
    the execution trace will wait until the method become available
    tada !!!
    no need to modify cmq.py



dynamo handle dynamic versioned code and stub.
both stub and code are available in same mq.
fn and mock:fn
test will have to check if mock available foreach function and use them instead if available

Dynamo have many responsibility
    versionize the code
    unit test the code
    mock some module
    publish code

wildcard comparison
    used by unittest, mock

dynamic code publication
   used by mock and code

it would be ideal to have separate entity for mock and code because source file differ
it would be ideal to have test apart from code versioning
the only solution would be to move wildcard comparision into cmq witch seam to make not sense
and have separate stub and code
let keep all of it together, not unlike it will be bigger dans jeezus
'''

def cmpJsonWildCard(params,wildcard):
    '''
    I had to build the spec myself
    wildcard is : '[...]' who is equal to everything
    wildcard can be inserted into text '[...]  bottles of beer on the wall, [...] bottles of beer .'
    unless wildcard, all value must equal
    dictionary, its different, params must at least contain all defined key and wildcard from value must match
    list, ist different, all element from params must match one element from wildcard
    dictionary have null, who tell you dont want that key
    list, null discarted


    >>> cmpJsonWildCard(1,1)
    True

    >>> cmpJsonWildCard('a','a')
    True

    >>> cmpJsonWildCard(True,True)
    True

    >>> cmpJsonWildCard({},{})
    True

    >>> cmpJsonWildCard([],[])
    True

    >>> cmpJsonWildCard(1,'[...]')
    True

    >>> cmpJsonWildCard('a','[...]')
    True

    >>> cmpJsonWildCard(True,'[...]')
    True

    >>> cmpJsonWildCard({},'[...]')
    True

    >>> cmpJsonWildCard([],'[...]')
    True

    >>> cmpJsonWildCard(1,'a')
    False

    >>> cmpJsonWildCard(True,'a')
    False

    >>> cmpJsonWildCard({},'a')
    False

    >>> cmpJsonWildCard([],'a')
    False

    >>> cmpJsonWildCard('a',1)
    False

    >>> cmpJsonWildCard(True,1)
    False

    >>> cmpJsonWildCard({},1)
    False

    >>> cmpJsonWildCard([],1)
    False

    >>> cmpJsonWildCard('a',True)
    False

    >>> cmpJsonWildCard(1,True)
    False

    >>> cmpJsonWildCard({},True)
    False

    >>> cmpJsonWildCard([],True)
    False

    >>> cmpJsonWildCard('a',[])
    False

    >>> cmpJsonWildCard(1,[])
    False

    >>> cmpJsonWildCard({},[])
    False

    >>> cmpJsonWildCard(True,[])
    False

    >>> cmpJsonWildCard('a',{})
    False

    >>> cmpJsonWildCard(1,{})
    False

    >>> cmpJsonWildCard(True,{})
    False

    >>> cmpJsonWildCard([],{})
    False

    >>> cmpJsonWildCard([{}],[])
    True

    >>> cmpJsonWildCard([{'a':1}],[])
    True

    >>> cmpJsonWildCard([{'a':1,'b':2}],[{'a':1}])
    True

    >>> cmpJsonWildCard([{'a':1,'b':2}],[{'b':2}])
    True

    >>> cmpJsonWildCard([{'a':1,'b':2}],[{'c':3}])
    False

    >>> cmpJsonWildCard([{'a':1},{'b':2}],[{'a':3}])
    False

    >>> cmpJsonWildCard([{'a':1,'b':2}],[{'a':'[...]'}])
    True

    >>> cmpJsonWildCard("hello","hello[...]")
    True

    >>> cmpJsonWildCard("hello world","hello[...]")
    True

    >>> cmpJsonWildCard("hello","hello")
    True

    >>> cmpJsonWildCard("hello world","hello")
    False

    >>> cmpJsonWildCard("hello","[...]world")
    False

    >>> cmpJsonWildCard("hello world","[...]world")
    True

    >>> cmpJsonWildCard("once upon a fox there was a time who ...","once upon a [...] there was a [...] who [...]")
    True

    #check for a rpc
    >>> cmpJsonWildCard({'rpc':[{'method':'nope'}]},{'rpc':[{'method':'[...]'}]})
    True

    >>> cmpJsonWildCard({'rpc':[{'meth':'nope'}]},{'rpc':[{'method':'[...]'}]})
    False

    >>> cmpJsonWildCard(['a'],[None])
    True

    >>> cmpJsonWildCard(['a'],['a',None])
    True

    >>> cmpJsonWildCard({'a':1},{'b':None})
    True

    >>> cmpJsonWildCard({'b':1},{'b':None})
    False

    >>> cmpJsonWildCard({'a':1},{'b':None,'a':'[...]'})
    True

    >>> cmpJsonWildCard({'a':1,'b':2},{'b':None,'a':'[...]'})
    False



    '''
    if wildcard == '[...]':
        return True
    elif type(params) != type(wildcard):
        return False
    elif isinstance(params,list):
        wildcard = [ w for w in wildcard if w   ]
        #empty list only mean you want a list
        if not wildcard:
            return True
        #list with one itmes mean you want all item to comply with it
        elif len(wildcard) == 1:
            for item in params:
                if not cmpJsonWildCard(item,wildcard[0]):
                    return False
        #many items mean at least one of these must match
        else:
            return not [
                False
                for items in params
                if not [
                    True
                    for w in wildcard
                    if cmpJsonWildCard(items,w)
                ]
            ]
    #string can have wildcard within self
    elif isinstance(params,str):
        if not '[...]' in wildcard:
            return params == wildcard
        else:
            sublst = wildcard.split('[...]')
            tmp = params
            if len(sublst) > 0:
                sub = sublst[0]
                if not tmp.startswith(sub):
                    return False
                tmp = tmp[len(sub):]
            if len(sublst) > 1:
                for sub in sublst[1:]:
                    if sub and not sub in tmp:
                        return False
                    elif sub:
                        pos = tmp.find(sub) + len(sub)
                        tmp = tmp[pos:]
            return True
        #here wildcard string testa
    elif isinstance(params,dict):
        #recursive compare, both dict dont need to be identical
        #only that all key of wildcard is also in params
        for key in wildcard:
            if wildcard[key]:
                if not key in params:
                    return False
                elif not cmpJsonWildCard(params[key],wildcard[key]):
                    return False
            else:
                if key in params:
                    return False
        return True
    else:
        if params != wildcard:
            return False
    return True



'''
{
    ??? : {
        name : "", #used for method broadcast
        doc : "",
        version : "",
        src : {what we expect in [rpc]},
        test : [test case],
    }
}

[test case]
{
    from : {json},
    expect : {json},
    exec ID : "couchdb id",  #generated once test start
    trace : [execution trace], # updated until finised
    status : done, # will be done once last step of trace dont emit
}

'''

def _codeGetter(url,repo):
    server = couchdb.Server(url)
    assert repo in server
    db = server[repo]
    procDir = {}
    for doc in db.view('_all_docs'):
        if not '_design/' in doc.id:
            code = db.get(doc.id)
            procDir[doc.id] = code
    print("procedure list %s"%procDir.keys())
    return procDir

def _procTolog(code):
    def closure(params):
        tmp = copy.deepcopy(code)
        #tmp['params'] = params
        raise Exception('not implemented yet', tmp, params)
        return tmp

def _makeAgentProcDir(cmqinst,codeDir):
    procdir = dict([    (key,_procTolog(code))
                        for key,code in codeDir.items() ])
    return procdir


'''
stubastian stubble stub
also leonardo dicaprio who can act as anyone

it is a stub system
that expose all defined functionality
and act the same way wenever wat happen
{
  stub : [
    {
       method : "",
       case : [
          {
             doc : "",
             params : {},
             result : {if result},
             error : {if error},
          }
       ],
       default : {
          result : {},
          error : {}
       }
    }
  ]
}

it would be innovativw to make it scan execution queue and see if it find any bew use case

'''

def BuildProcDir():
    '''
    search for all document holding a stub key
    {
        'stub' : [
            {
                'method' : '[...]',
                'case' : [
                    {
                        'params':'[...]',
                        'result':'[...]'
                    },
                    {
                        'params':'[...]',
                        'error':'[...]'
                    }
                ],
                'default' : {}
            }
        ]
    }
    foreach method taken, publish function that will return any result based on params
    if more than one method is declared, one is chosen randomly
    '''
    return {}

if __name__ == '__main__':
    def moreArg(parser):
        parser.add_argument('--repo', dest='repo', action='store', help='were the program are hosted')
        return parser

    import cmq
    args = cmq.cmqarg(BuildProcDir(),moreArg)

    print('done')
