#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK
import sys
import argparse
import ConfigParser
from StringIO import StringIO
from npsemrel.carrot.db import db
from plumper.wn import plwnhandler
from plumper.sumo import mapper
from plumper.sumo.processor import SumoProcessor
try:
    import argcomplete
except ImportError:
    argcomplete = None


class SumoCclProcessor(SumoProcessor):
    def __init__(self, db_config, senseindexfile, senseindexfiles, sumo_graph_file,
                 use_rreductor=False, use_bulbulator=False, use_oknak=False,
                 use_serdel=False, use_corec=False, use_rubin=False, tagset='nkjp'):
        self.senseindexfile = senseindexfile
        self.senseindexfiles = senseindexfiles
        self.sumo_graph_file = sumo_graph_file
        self.use_rreductor = use_rreductor
        self.use_bulbulator = use_bulbulator
        self.use_oknak = use_oknak
        self.use_serdel = use_serdel
        self.use_corec = use_corec
        self.use_rubin = use_rubin
        self.tagset = tagset
        self.dbcon = db.DB()
        self.dbconnection = self.dbcon.connect(db_config)
        self.sumo_mapper = mapper.Mapper()
        if not self.dbconnection:
            print >> sys.stderr, 'Cannot connect to DB!'
            exit(1)

        self.dbplwn = plwnhandler.DbPlwnHandler(self.dbconnection)
        '''
        +-----+----------------+
        | ID  | shortcut       |
        +-----+----------------+
        | 201 | mero:cz_pa     |
        | 202 | mero:el_pa     |
        | 203 | mero:mat_pa    |
        | 205 | holo:cz_pa     |
        | 206 | holo:el_pa     |
        | 207 | holo:mat_pa    |
        | 208 | syn_pa         |
        | 210 | hipo_pa        |
        | 212 | hiper_pa       |
        | 222 | po_pa          |
        | 225 | syncz_pa       |
        | 228 | synmr_pa       |
        +-----+----------------+
        '''
        #	bez 222
        self.relations_id = [201, 202, 203, 205, 206, 207, 208, 210, 212, 225,
                             228]

        self.not_mapped_synsets = None

    def __first_step(self, document):
        synids = set()
        for paragraph in document.paragraphs():
            for sentence in paragraph.sentences():
                for token in sentence.tokens():
                    md = token.get_metadata()
                    if md:
                        ukb_syn_id = md.get_attribute('sense:ukb:syns_id')
                        if ukb_syn_id:
                            synids.add(ukb_syn_id)
                            # sense p2s

        fout = StringIO()
        self.sumo_mapper.basic_sumo_mapping(fout, False, self.dbplwn,
                                            self.relations_id,
                                            self.senseindexfile,
                                            self.senseindexfiles, synids)
        
        if self.use_serdel:
            mapped_synsets = set()
            lines = fout.getvalue().split('\n')
            for line in lines[1:-1]:
                spl_line = line.split(';')
                mapped_synsets.add(spl_line[2])

            self.not_mapped_synsets = set()
            self.not_mapped_synsets = synids.difference(mapped_synsets)

            if self.not_mapped_synsets:
                from plumper.tools.mapping.serdel import Serdel
                serdel = Serdel(self.dbconnection)
                
                for synset_id in self.not_mapped_synsets:
                    parents_synsets = set()
                    parents_synsets = serdel.get_parents_synsets(synset_id)
                    synids = synids.union(parents_synsets)
                synids = synids.difference(mapped_synsets)
                synids = synids.difference(self.not_mapped_synsets)
                
                fout_parents_synsets = StringIO()
                self.sumo_mapper.basic_sumo_mapping(fout_parents_synsets, False,
                                                    self.dbplwn,
                                                    self.relations_id,
                                                    self.senseindexfile,
                                                    self.senseindexfiles, synids)

                fout.write(fout_parents_synsets.getvalue()[fout_parents_synsets.getvalue().index('\n'):])

        return fout

    def __second_step(self, inbuf, generate_buffer=False):
        out = {}
        if generate_buffer:
            temp = StringIO()
            temp = self.sumo_mapper.second_step_sumo_mapping(inbuf, temp)
            n_temp = StringIO(temp.getvalue())
            temp = self.__rreductor(n_temp)

            if self.use_bulbulator:
                n_temp = StringIO(temp.getvalue().decode('utf-8'))
                temp = self.__bulbulator(n_temp)
            
            if self.use_oknak:
                n_temp = StringIO(temp.getvalue().decode('utf-8'))
                temp = self.__oknak(n_temp)

            if self.use_serdel:
                n_temp = StringIO(temp.getvalue().decode('utf-8'))
                temp = self.__serdel(n_temp)

            if self.use_corec:
                n_temp = StringIO(temp.getvalue().decode('utf-8'))
                temp = self.__corec(n_temp)

            if self.use_rubin:
                n_temp = StringIO(temp.getvalue().decode('utf-8'))
                temp = self.__rubin(n_temp)

            lines = temp.getvalue().split('\n')
            for line in lines[1:-1]:
                spl_line = line.split(';')
                syn_id = spl_line[2]
                concept = spl_line[8].encode('utf-8')
                sumo_rel = spl_line[-1]
                if syn_id in out:
                    l = out[syn_id].split(';')
                    l.append('%s:%s' % (sumo_rel, concept))
                    l = list(set(l))
                    out[syn_id] = ';'.join(l)
                else:
                    out[syn_id] = "%s:%s" % (sumo_rel, concept)
        else:
            if self.use_bulbulator or self.use_oknak or self.use_serdel \
            or self.use_corec or self.use_rubin:
                temp = StringIO()
                temp = self.sumo_mapper.second_step_sumo_mapping(inbuf, temp)

                is_use = False

                if self.use_bulbulator:
                    n_temp = StringIO(temp.getvalue()) \
                             if not is_use \
                             else StringIO(inbuf.getvalue().decode('utf-8'))
                    is_use = True
                    inbuf = self.__bulbulator(n_temp)

                if self.use_oknak:
                    n_temp = StringIO(temp.getvalue()) \
                             if not is_use \
                             else StringIO(inbuf.getvalue().decode('utf-8'))
                    is_use = True
                    inbuf = self.__oknak(n_temp)

                if self.use_serdel:
                    n_temp = StringIO(temp.getvalue()) \
                             if not is_use \
                             else StringIO(inbuf.getvalue().decode('utf-8'))
                    is_use = True
                    inbuf = self.__serdel(n_temp)

                if self.use_corec:
                    n_temp = StringIO(temp.getvalue()) \
                             if not is_use \
                             else StringIO(inbuf.getvalue().decode('utf-8'))
                    is_use = True
                    inbuf = self.__corec(n_temp)

                if self.use_rubin:
                    n_temp = StringIO(temp.getvalue()) \
                             if not is_use \
                             else StringIO(inbuf.getvalue().decode('utf-8'))
                    is_use = True
                    inbuf = self.__rubin(n_temp)

            for line in inbuf.getvalue().split('\n'):
                pps_m = self.sumo_mapper.parse_line(line)
                if not pps_m:
                    continue
                syn_id = pps_m.plwn_synset().syn_id()
                if syn_id not in out:
                    out[syn_id] = \
                        '%s:%s' % (line.split(';')[-1]
                                   if pps_m.resolve_plwn_SUMO_rel() is '?' and self.use_serdel
                                   else pps_m.resolve_plwn_SUMO_rel(),
                                   pps_m.sumo_concept().concept_name.encode('utf-8'))
                else:
                    l = out[syn_id].split(';')
                    l.append('%s:%s' % (line.split(';')[-1]
                                        if pps_m.resolve_plwn_SUMO_rel() is '?' and self.use_serdel
                                        else pps_m.resolve_plwn_SUMO_rel(),
                                        pps_m.sumo_concept().concept_name.encode('utf-8')))
                    l = list(set(l))
                    out[syn_id] = ';'.join(l)
        return out

    def __bulbulator(self, inbuf):
        from plumper.tools.mapping.bulbulator import Bulbulator
        bulbulator = Bulbulator()
        out = StringIO()
        bulbulator.bulbul(inbuf, out)
        return out

    def __oknak(self, inbuf):
        from plumper.tools.mapping.oknak import Windower
        oknak = Windower()
        out = StringIO()
        oknak.clean_window(inbuf, out)
        return out

    def __serdel(self, inbuf):
        from plumper.tools.mapping.serdel import Serdel
        serdel = Serdel(self.dbconnection)
        out = StringIO()
        serdel.serdluj_z_tym(inbuf, out, False, self.not_mapped_synsets)
        return out

    def __corec(self, inbuf):
        from plumper.tools.mapping.corec import Corec
        corec = Corec(self.sumo_graph_file)
        out = StringIO()
        corec.zakorkuj_mi_to(inbuf, out)
        return out

    def __rubin(self, inbuf):
        from plumper.tools.mapping.rubin import Rubin
        rubin = Rubin()
        out = StringIO()
        rubin.szlifuj(inbuf, out)
        return out

    def __rreductor(self, inbuf):
        from plumper.tools.mapping.rreductor import RReductor
        reductor = RReductor()
        out = StringIO()
        reductor.reduce_R(inbuf, out)
        return out

    def process_document(self, document):
        temp_buf = self.__first_step(document)
        if self.use_rreductor:
            sumo_out = self.__second_step(temp_buf, True)
            print >> sys.stderr, "Using RReductor"
        else:
            sumo_out = self.__second_step(temp_buf)
        for parag in document.paragraphs():
            for sent in parag.sentences():
                for token in sent.tokens():
                    meta_data = token.get_metadata()
                    if not meta_data:
                        token.create_metadata()
                        meta_data = token.get_metadata()
                    synset_id = meta_data.get_attribute('sense:ukb:syns_id')
                    if synset_id and synset_id in sumo_out:
                        meta_data.set_attribute('sumo_map',
                                                sumo_out[synset_id])
        return document

def aparser():
    arg_parser = argparse.ArgumentParser(description='Sumo mapping tool for' +
                                                     ' ccl files')
    arg_parser.add_argument('input_file', help='input file. If you want' +
                                               ' to use stdin use "-"')
    arg_parser.add_argument('-o', '--output', help='output file path if not' +
                                                   ' present script will' +
                                                   ' print to stdout')
    arg_parser.add_argument('-c', '--config', required=True,
                            help='path to sumo config file')
    if argcomplete:
        argcomplete.autocomplete(arg_parser)
    return arg_parser.parse_args()


def main():
    args = aparser()
    config = ConfigParser.RawConfigParser()
    config.readfp(open(args.config))
    proc = SumoCclProcessor(config.get('sumo', 'db_config'),
                            config.get('sumo', 'sense_index_file'),
                            config.get('sumo', 'sense_index_files').split(),
                            config.get('sumo', 'sumo_graph_file'),
                            config.getboolean('sumo', 'reductor'),
                            config.getboolean('sumo', 'bulbulator'),
                            config.getboolean('sumo', 'oknak'),
                            config.getboolean('sumo', 'serdel'),
                            config.getboolean('sumo', 'corec'),
                            config.getboolean('sumo', 'rubin'),
                            config.get('sumo', 'tagset'))
    proc.process_ccl(args.input_file, args.output if args.output else None)


if __name__ == '__main__':
    main()
