import os
import sys
import glob
import codecs
import cPickle
import numpy as np

#################################################
# Parser Serializers
#################################################

class SerializedParser(object):
    def __init__(self,parser,encoding="utf-8"):
        '''Interface for persisting mark-up data generated by parser object
        
        Parameters
        ----------
        parser : object
            DDlite parser object.
        encoding: string, "utf-8" by default
            String encoding
            
        '''
        self.parser = parser
        self.encoding = encoding
        self.doc_cache = {}
        
    def _write(self,sents,doc_id):
        raise NotImplementedError()
    
    def _read(self,doc_id):
        raise NotImplementedError()   

    def parse(self,s,doc_id):
        '''Parse provided string or use cached version'''
        if doc_id in self.doc_cache:
            sents = self._read(doc_id)
        else:   
            try:
                sents = [s for s in self.parser.parse(s,doc_id=doc_id)]
            except Exception as e:
                print "Fatal Parser Error {} {}".format(doc_id,e)
                sys.exit()
            self._write(sents,doc_id)
        
        self.doc_cache[doc_id] = 0   
        return sents    
        

class PickleSerializedParser(SerializedParser):
    
    def __init__(self,parser,rootdir):
        super(PickleSerializedParser, self).__init__(parser)
        self.rootdir = rootdir
        # initialize previously cached documents
        filelist = glob.glob("{}*.pkl".format(self.rootdir))
        self.doc_cache = {n.split("/")[-1].split(".")[0]:0 for n in filelist}
        
    def _write(self,sents,doc_id):
        '''write pickle file to disk'''
        fname = "%s/%s.pkl" % (self.rootdir, doc_id)
        with open(fname, 'w+') as f:
            cPickle.dump(sents,f)
   
    def _read(self,doc_id):
        fname = "%s/%s.pkl" % (self.rootdir, doc_id)
        with open(fname, 'rb') as f:
            return cPickle.load(f)   
        
        
class TextSerializedParser(SerializedParser):
    '''Write results to offset indexed text file'''
    def __init__(self,parser,rootdir):
        super(TextSerializedParser, self).__init__(parser)
        self.idx_dtype = np.int64
        self.rootdir = rootdir
        self._load_index()
        self.datfile = "{}/docs.dat".format(self.rootdir)
       
    def _load_index(self):
        '''Loading index of doc_id to file offsets'''
        fname = "{}/docs.idx".format(self.rootdir)
        if os.path.exists(fname):
            tmp = open(fname,"rU").readlines()
            self.doc_idx = dict([line.strip().split("\t") for line in tmp])
            self.doc_idx = {uid:int(idx) for uid,idx in self.doc_idx.items()}
            self.doc_cache = {uid:0 for uid in self.doc_idx}
        else:
            self.doc_cache = {}
            self.doc_idx = {}
            
    def __del__(self):
        try:
            # store previous version if it exists
            fname = "{}/docs.idx".format(self.rootdir)
            if os.path.exists(fname):
                os.rename(fname,fname+".bak")
            # write index to disk
            with open(fname,"w") as f:
                for uid,idx in self.doc_idx.items():
                    f.write("{}\t{}\n".format(uid,idx))
        except Exception as e:
            print "Error writing index to disk {}".format(e)
        
    def _write(self, sents, doc_id):
        with codecs.open(self.datfile,"a+",self.encoding) as f:
            self.doc_idx[doc_id] = f.tell()
            f.write("{}\n".format(sents))
            
    def _read(self,doc_id):
        idx = self.doc_idx[doc_id]
        with codecs.open(self.datfile,"r",self.encoding) as f:
            f.seek(idx) 
            return eval(f.readline())


class DatabaseSerializedParser(SerializedParser):
    
    def __init__(self, parser, host, user, database, password=""):
        super(DatabaseSerializedParser, self).__init__(parser)
        self.params = (host,user,database,password)
        
    def __getitem__(self,doc_id):
        '''query database'''
        raise NotImplementedError()
