import collections
import os
import json

import jsonrpc.jsonutil
import emen2.db
import emen2.db.config
import emen2.util.listops


def dump_json(outfile, items):
    if os.path.exists(outfile):
        print "Warning: File %s exists"%outfile
    print "Saving output to %s"%outfile
    with open(outfile,'w') as f:
        for item in items:
            f.write(json.dumps(item)+"\n")


class Dumper(object):
    
    keytypes = ['paramdef', 'recorddef', 'user', 'group', 'binary', 'record']
    
    def __init__(self, db):
        self.db = db
        self._ref_pds = {}
        self._ref_vts = collections.defaultdict(set)
        
    def dump(self, names=None, keytype='record', **kwargs):
        # Query
        kwargs['keytype'] = keytype
        names = names or self.db.query(**kwargs)['names']
        print "Starting with:"
        print len(names)
        keys = collections.defaultdict(set)
        keys[keytype] |= set(names)

        # Find referenced items.
        for chunk in emen2.util.listops.chunk(keys[keytype], count=1000):
            items = self.db.get(chunk, keytype=keytype)
            print "Processing: %s ... %s"%(items[0].name, items[-1].name)
            # Find paramdefs that reference other items..
            self._findrefs(items)
            # Find other items..
            for kt in self.keytypes:
                keys[kt] |= getattr(self, '_find_%s'%kt)(items)

        return keys
        
    def write(self, keys, outfile='dump.json', uri=None):
        f = open(outfile, 'w')
        for keytype in self.keytypes:
            modify = getattr(self, '_modify_%s'%keytype, lambda x:x)
            
            for chunk in emen2.util.listops.chunk(keys.get(keytype, []), count=1000):
                items = self.db.get(chunk, keytype=keytype)
                for item in items:
                    if uri:
                        item.__dict__['uri'] = '%s/%s/%s'%(uri, keytype, item.name)
                    item = modify(item)
                    print "%s: %s"%(item.keytype, item.name)
                    f.write(jsonrpc.jsonutil.encode(item))
                    f.write('\n')
        f.close()
        return outfile        

    def _findrefs(self, items):
        # Get the referencing vartypes from the items.
        pds = self._find_paramdef(items)
        for param in pds - set(self._ref_pds.keys()):
            pd = self.db.get(param, keytype='paramdef')
            if pd.vartype in self.keytypes:
                self._ref_pds[pd.name] = pd.vartype
                self._ref_vts[pd.vartype].add(pd.name)
    
    def _findvalues(self, vartypes, items):
        # Get the values referenced by a vartype from the items.
        ret = set()
        pds = set()
        for vartype in vartypes:
            pds |= self._ref_vts[vartype]
        for pd in pds:
            pd = self.db.paramdef.get(pd)
            if pd.iter:
                for item in items:
                    ret |= set(item.get(pd.name, []))
            else:
                for item in items:
                    ret.add(item.get(pd.name))
        ret -= set([None])
        return ret

    def _find_paramdef(self, items):
        r = set()
        for rec in items:
            r |= set(rec.keys())
        return r

    def _find_record(self, items):
        return self._findvalues(['record'], items)
            
    def _find_recorddef(self, items):
        return self._findvalues(['recorddef'], items)
        
    def _find_user(self, items):
        return self._findvalues(['user'], items)
                
    def _find_group(self, items):
        return self._findvalues(['group'], items)

    def _find_binary(self, items):
        return self._findvalues(['binary'], items)

    def _modify_record(self, item):
        return item
        
    def _modify_user(self, item):
        return item



class PublicDumper(Dumper):
    def _modify_record(self, item):
        # Remove permissions, mark published data as anon
        if set(['authenticated', 'publish']) & item.__dict__['groups']:
            item.__dict__['groups'] = set(['anon'])
        else:
            item.__dict__['groups'] = set()
        item.__dict__['permissions'] = [[],[],[],[]]
        return item
        
    def _modify_user(self, item):
        # Remove user passwords
        item.__dict__['password'] = ''
        return item



class DumpOptions(emen2.db.config.DBOptions):
    def parseArgs(self, infile):
        self['infile'] = infile



if __name__ == "__main__":
    import emen2.db
    db = emen2.db.opendb(admin=True)
    dumper = Dumper(db=db) # PublicDumper(db=db)
    keys = dumper.dump(c=[['groups','==','publish']])
    keys['paramdef'] = db.paramdef.filter(None)
    keys['recorddef'] = db.recorddef.filter(None)
    keys['user'] = db.user.filter()
    keys['user'].remove('root')
    dumper.write(keys, outfile="dump-publicdata.json", uri="http://ncmidb.bcm.edu")
    
    
    
