"""
This module contains mapping class from polish wordnet to Sumo onthology. It
generates csv-like strings which contains all generated and used data.

The header of mentioned string goes like this:
synset_str_plwn;dziedzina_plwn;synset_id_plwn;rel_miedzyjezykowa;
synset_str_pwn;pwn_synset_pos;dziedzina_pwn;synset_id_pwn;'pojecie_sumo;
rel_sumo_pwn;rel_sumo_pwn_skrot;plwn_sumo_rel
"""
from plumper.sumo import plwnpwnsumo_map, pwn2sumo, sumo
from plumper.wn import synset, pwn
from npsemrel.core import verboser
import codecs
import sys
import StringIO

__author__ = 'blaz'


class Mapper:
    def __init__(self, logfile_path = 'plumper.log'):
        self._logfile_path = logfile_path

    def _convert_dbpos_to_str(self, pos_int):
        # czasownik / verb
        if pos_int == 5:
            return 'v'
        # rzeczownik / noun
        elif pos_int == 6:
            return 'n'
        # przyslowek / adverb
        elif pos_int == 7:
            return 'r'
        # przymiotnik / adjective
        elif pos_int == 8:
            return 'a'
        return '?'

    def __find_p2s_lemmasensenum(self, lemma, sensenum, pos, p2s_map_list):
        pos_str = self._convert_dbpos_to_str(pos)
        for p2s in p2s_map_list:
            sense_idx = p2s.pwn_sense_index
            if sense_idx.lemma.lower() == lemma.lower() \
                and sense_idx.sensenum == sensenum \
                and sense_idx.pos_disamb_str() == pos_str:
                return p2s

    def __make_plwnpwnsumomap(self, plwn_syn_id, plwn_lex, pwn_syn_id, pwn_lex,
                             plwn_pwn_rel, pwn2sumo):
        '''
        Whith given objects:
          * plwn_syn - synset object form polish wordnet
          * pwn_syn - synset object Princeton Wordnetu
          * plwn_pwn_rel - string with shortcut of cross-lingual relation, np.
                           hiper_pa
          * pwn2sumo - PWN2SUMOMapper objact mapping Princeton Wordnetu to
                       SUMO
        it creates and returns npsemrel.sumo.plwn_pwn_sumo_map object
        '''
        # syn_str, syn_sensenum
        plwn_syn = synset.Synset(plwn_syn_id, plwn_lex[1], plwn_lex[2])
        plwn_syn._domain = plwn_lex[4]

        pwn_syn = synset.Synset(pwn_syn_id, pwn_lex[1], pwn_lex[2])
        pwn_syn._domain = pwn_lex[4]

        pps_map = plwnpwnsumo_map.PlwnPwnSumoMap(plwn_syn, pwn_syn,
                                                 pwn2sumo.sumoinstance)
        pps_map._plWN_PWN_rel_str = plwn_pwn_rel
        pps_map._PWN_SUMO_rel_str = pwn2sumo.sumo2pwn_relation
        pps_map._PWN_SUMO_rel_str_shortcut = pwn2sumo.sumo2pwn_relation_shortcut

        return pps_map

    def basic_sumo_mapping(self, output_file, resolve_plwn_sumo,
                            dbplwn, relations_id, senseindexfile,
                            senseindexfiles, synids):
        logfile = codecs.open(self._logfile_path, mode = 'a', encoding = 'utf8', buffering = 0)

        # reading sense index file
        print >> sys.stderr, 'Reading PWN sense index...',
        spwnreader = pwn.PWNSenseIndexReader(senseindexfile)
        sensesidxs = spwnreader.read()
        print >> sys.stderr, ' read %d instances' % len(sensesidxs)
        # reading mapping PWN to SUMO
        print >> sys.stderr, 'Reading map of SUMO instances'
        p2s_map_list = []
        for sumofilename in senseindexfiles:
            print >> sys.stderr, '\tReading %s' % (sumofilename),
            p2sreader = pwn2sumo.PWN2SUMOReader(sumofilename, sensesidxs)
            p2sm = p2sreader.read(clear_synset_lemma=True)
            print >> sys.stderr, ' read %d instances' % len(p2sm)
            p2s_map_list.extend(p2sm)
        print >> sys.stderr, 'Read %d maps' % len(p2s_map_list)
        # a little communicate
        if resolve_plwn_sumo:
            print >> sys.stderr, 'Mapping plWN to SUMO with resolving relation plWN-SUMO'
        else:
            print >> sys.stderr, 'Mapping plWN to SUMO without resolving relation plWN-SUMO'

        # timeleft
        timeleft = verboser.Testimator(len(synids))
        # here we start SUMO mapping
        # all synset ids are disambiguated in text
        if type(output_file) is str:
            fout = codecs.open(output_file, 'wt', 'utf8')
        else:
            fout = output_file
        # save header
        if resolve_plwn_sumo:
            fout.write('synset_str_plwn;dziedzina_plwn;synset_id_plwn;' \
                       'rel_miedzyjezykowa;' \
                       'synset_str_pwn;pwn_synset_pos;dziedzina_pwn;synset_id_pwn;' \
                       'pojecie_sumo;rel_sumo_pwn;rel_sumo_pwn_skrot;plwn_sumo_rel\n')
        else:
            fout.write('synset_str_plwn;dziedzina_plwn;synset_id_plwn;' \
                       'rel_miedzyjezykowa;' \
                       'synset_str_pwn;pwn_synset_pos;dziedzina_pwn;synset_id_pwn;' \
                       'pojecie_sumo;rel_sumo_pwn;rel_sumo_pwn_skrot\n')

        for p_synid in synids:
            # first lexical unit for parent
            p_lu = dbplwn.get_lu_in_synset(p_synid)
            if not p_lu:
                logfile.write(
                    'Skipping: %d. Cannot find parent lexical unit\n' % (
                    int(p_synid)))
                continue
            p_lu = p_lu[0]
            # for every synset we check is it in cross-lingual relation
            for ch_synid, (
                rel_shortcut, rel_fullname) in dbplwn.get_relationsynsets(
                    p_synid,
                    relations_id):

                # first lexical unit for child
                ch_lu = dbplwn.get_lu_in_synset(ch_synid)[0]

                # SUMO mapping
                lemma = ch_lu[1].replace(' ', '_')
                sensenum = ch_lu[2]
                pos = ch_lu[3]
                p2s = self.__find_p2s_lemmasensenum(lemma, sensenum, pos,
                                                   p2s_map_list)
                if not p2s:
                    logfile.write('%s.%d-%d(%s) has not been mapped to SUMO\n' % (
                        lemma, sensenum, pos,
                        self._convert_dbpos_to_str(pos)))
                    continue

                # i have relation from p_synid to ch_synid mam relacje od p_synid od ch_synid
                plwnwnsumo = self.__make_plwnpwnsumomap(p_synid, p_lu,
                                                       ch_synid, ch_lu,
                                                       rel_shortcut, p2s)
                if resolve_plwn_sumo:
                    fout.write('%s.%d;%s;%d;%s;%s.%d;%s;%s;%d;%s;%s;%s;%s\n' % (
                        p_lu[1], p_lu[2],
                        '%s-%s' % (
                            plwnwnsumo.plwn_synset().syn_domain_str(),
                            plwnwnsumo.plwn_synset().syn_domain()),
                        int(p_synid),
                        rel_shortcut,
                        ch_lu[1], ch_lu[2],
                        p2s.pwn_sense_index.pos_disamb_str(),
                        '%s-%s' % (
                            plwnwnsumo.pwn_synset().syn_domain_str(),
                            plwnwnsumo.pwn_synset().syn_domain()),
                        int(ch_synid),
                        p2s.sumoinstance.concept_name,
                        p2s.sumo2pwn_relation,
                        p2s.sumo2pwn_relation_shortcut,
                        plwnwnsumo.resolve_plwn_SUMO_rel()
                    ))
                else:
                    fout.write('%s.%d;%s;%d;%s;%s.%d;%s;%s;%d;%s;%s;%s\n' % (
                        p_lu[1], p_lu[2],
                        '%s-%s' % (plwnwnsumo.plwn_synset().syn_domain_str(),
                                   plwnwnsumo.plwn_synset().syn_domain()),
                        int(p_synid),
                        rel_shortcut,
                        ch_lu[1], ch_lu[2],
                        p2s.pwn_sense_index.pos_disamb_str(),
                        '%s-%s' % (plwnwnsumo.pwn_synset().syn_domain_str(),
                                   plwnwnsumo.pwn_synset().syn_domain()),
                        int(ch_synid),
                        p2s.sumoinstance.concept_name,
                        p2s.sumo2pwn_relation,
                        p2s.sumo2pwn_relation_shortcut
                    ))
            print >> sys.stderr, '\r\t%s' % timeleft.etl_autostep(),
        if type(output_file) is str:
            fout.close()
        print >> sys.stderr, '\nAll is done!'

    def parse_line(self, str_line):
        '''
            Przekazana linia ma postac:
                ulica.1;1167;syn_pa;street.1;151845;Roadway;equivalent;=
                nazwisko.1;por-7;6988;syn_pa;surname.1;por-7;161948;
                ContentBearingObject;subsumed;+

                0 - nazwisko.1
                1 - por-7
                2 - 6988
                3 - syn_pa
                4 - surname.1
                5 - por-7
                6 - 161948
                7 - ContentBearingObject
                8 - subsumed
                9 - +
        '''

        if str_line.find('.') == -1:
            return None

            # strline (split by ';'):
            # 0 -     absolwent.1;
            # 1 -    os-15;
            # 2 -     12;
            # 3 -    syn_pa;
            # 4 -     alumnus.1;
            # 5 -    n;
            # 6 -    os-15;
            # 7 -    337482;
            # 8 -   SocialRole;
            # 9 -    subsumed;
            # 10 -    +

        spl_line = str_line.strip().split(';')
        dotpos = spl_line[0].rfind('.')
        s_plwn = synset.Synset(
            spl_line[2],
            spl_line[0][0: dotpos],
            spl_line[0][dotpos + 1:])
        domain_str = spl_line[1].split('-')
        s_plwn._domain = 0 if len(domain_str) != 2 or not domain_str[1].isdigit() else int(domain_str[1])

        dotpos = spl_line[4].rfind('.')
        s_pwn = synset.Synset(
            spl_line[7],
            spl_line[4][0: dotpos],
            spl_line[4][dotpos + 1:])
        domain_str = spl_line[6].split('-')
        s_pwn._domain = 0 if len(domain_str) != 2 or not domain_str[1].isdigit() else int(domain_str[1])
        s_pwn._pos_str = spl_line[5]

        c_sumo = sumo.SumoInstance(spl_line[8])

        pps_m = plwnpwnsumo_map.PlwnPwnSumoMap(s_plwn, s_pwn, c_sumo)
        pps_m._plWN_PWN_rel_str = spl_line[3]
        pps_m._PWN_SUMO_rel_str = spl_line[9]
        pps_m._PWN_SUMO_rel_str_shortcut = spl_line[10]

        return pps_m

    def __write_header(self, fout):
        fout.write('synset_str_plwn;dziedzina_plwn;synset_id_plwn;'
                   'rel_miedzyjezykowa;synset_str_pwn;pwn_synset_pos;'
                   'dziedzina_pwn;synset_id_pwn;pojecie_sumo;rel_sumo_pwn;'
                   'rel_sumo_pwn_skrot;plwn_sumo_rel\n')

    def second_step_sumo_mapping(self, in_file, out_file):
        if type(in_file) is str:
            fin = open(in_file, 'rt')
        else:
            fin = in_file.getvalue().split('\n')
        if type(out_file) is str:
            fout = open(out_file, 'wt')
        else:
            fout = out_file
        self.__write_header(fout)

        for line in fin:
            pps_m = self.parse_line(line)
            if not pps_m:
                continue
                # fout.write('%s;%s;%d;%s;%s;%s;%s;%d;%s;%s;%s;%s\n' % (
            fout.write('%s;%s;%d;%s;%s;%s;%s;%d;%s;%s;%s;%s\n' % (
                '%s.%d' % (
                    pps_m.plwn_synset().syn_str(),
                    int(pps_m.plwn_synset().syn_sensenum())),
                '%s-%s' % (
                    pps_m.plwn_synset().syn_domain_str(),
                    pps_m.plwn_synset().syn_domain()),
                int(pps_m.plwn_synset().syn_id()),
                pps_m.plwn_pwn_rel_str(),
                '%s.%d' % (
                    pps_m.pwn_synset().syn_str(),
                    int(pps_m.pwn_synset().syn_sensenum())),
                pps_m.pwn_synset().pos_str(),
                '%s-%s' % (
                    pps_m.pwn_synset().syn_domain_str(),
                    pps_m.pwn_synset().syn_domain()),
                int(pps_m.pwn_synset().syn_id()),
                pps_m.sumo_concept().concept_name,
                pps_m.pwn_sumo_rel_str(),
                pps_m.pwn_sumo_rel_shortuct_str(),
                pps_m.resolve_plwn_SUMO_rel()))
        if type(in_file) is str:
            fin.close()
        return fout
