# coding: utf-8

# This file is a part of TaCo: a morphosyntactic tagset converter
#
# License: GPL v. 3
# Author: Bartosz Zaborowski
#

require "#{File.dirname(__FILE__)}/helpers"
require "#{File.dirname(__FILE__)}/configuration"

class MorphoAnalyser
  @@morfeusz = nil

@@meta_starts=Configuration.meta_toks.map{|x| x.first}.uniq

  def self.correct_meta_toks toks
    ntoks = []
    pos = 0
    while pos < toks.length
      if @@meta_starts.include?(toks[pos].first)
        idx = Configuration.meta_toks.index {|m| toks[pos..pos+m.length-1].map{|x| x.first} == m}
        unless idx
          warn toks[pos..-1].map{|x| x.first}.inspect
          raise
        end
        npos = Configuration.meta_toks[idx].length-1
        o = toks[pos..pos+npos].map{|x| x.first}.join
        ntoks << [o, [[o, Configuration.meta_tag]]]
        pos += npos
      else
        ntoks << toks[pos]
      end
      pos +=  1
    end
    ntoks
  end

  def self.debug x
    Helpers::DEBUGSTREAM.puts x, Helpers::DEBUG
  end

  def self.warn x
    Helpers::DEBUGSTREAM.puts x, Helpers::WARNING
  end

  def self.align toks, texts, tagset
    # try to align 'unknown words'
    ntoks = []
    pos1 = 0
    pos2 = 0
    while pos1 < texts.length
      if pos2 >= toks.length
        warn ntoks.map{|x| x.first}.inspect
        warn toks.map{|x| x.first}.inspect
        warn pos2
        warn pos1
        warn texts.inspect
        raise
      end
      if texts[pos1] == " " && toks[pos2].first == :space
        ntoks << toks[pos2]
        pos1 += 1
        pos2 += 1
        next
      end
      if texts[pos1] == " " && toks[pos2].first != :space
        ntoks << [:space, nil]
        pos1 += 1
        next
      end
      if texts[pos1] == "" && toks[pos2].first == :space
        pos2 += 1
        next
      end
      if texts[pos1] == toks[pos2].first
        ntoks << toks[pos2]
        pos1 += 1
        pos2 += 1
        next
      end
      if texts[pos1].length < toks[pos2].first.length
        # analyser doesn't split; unknown word?
        unless toks[pos2].last.empty?
          debug "splitting #{toks[pos2].first.inspect} because: #{texts[pos1..pos1+5]}"
        end
        p1 = pos1
        o1 = ""
        o2 = toks[pos2].first
        while o1.length < o2.length && p1 < texts.length
          o1 += texts[p1]
          p1 += 1
        end
        if o1 != o2
          warn o1
          warn o2
          raise
        end
        ntoks += texts[pos1..p1-1].map do |t|
          if t == " "
            [:space, nil]
          else
            [t, []]
          end
        end
        pos1 = p1
        pos2 += 1
      else
        # zlepianie, heureza: tagi z ostatniego
        p2 = pos2
        o = ""
        tx = texts[pos1]
        while o.length < tx.length
          o += toks[p2].first
          p2 += 1
        end
        if o == tx
          debug "concatenating #{toks[pos2..p2-1].map{|x| x.first}.inspect} because: #{texts[pos1].inspect}"
          b = toks[pos2..p2-2].map{|x| x.first}.join
          last_o = toks[p2-1].first
          if Configuration.concatenate_special_no_suffix.find{|x| x =~ last_o}
            b = toks[pos2..p2-2].map{|x| x.last.empty? ? x.first : x.last.first.first}.join
            ls = toks[pos2].last.map{|x| [b, x[1]]}
          elsif Configuration.concatenate_special_suffixes.find{|x| x =~ last_o} || toks[p2-1].last.empty?
            b += last_o
            ls = toks[pos2].last.map{|x| [b, x[1]]}
          else
            b += toks[p2-1].last.first.first
            ls = toks[p2-1].last.map{|x| [b, x[1]]}
          end
          debug "base: #{b}, tags: #{ls.map{|x| x[1]}}"
          newtoks = analyse [tx], tagset, false
          newtok = newtoks.find{|n| n.first == o}
          if newtok
            debug "alt orth: #{newtok.first}, base: #{(newtok.last.first || []).first}, tags: #{newtok.last.map{|x| x[1]}}"
            ntoks << [o, ls+newtok.last]
          else
            ntoks << [o, ls]
          end
          pos1 += 1
          pos2 = p2
        else
          warn texts[pos1].inspect
          warn toks[pos2..pos2+5].inspect
          raise
        end
      end
    end
    ntoks
  end

  def self.insert_with_check_for_duplicate? toks, tok, text, verif, add_always = false
    if toks.length > 1 && tok.first.end_with?(toks[-1].first)
      tx = tok.first
      p2 = -1
      o = ""
      while o != tx && o.length < tx.length
        if toks[p2].first == :space
          o = " "+o
        else
          o = toks[p2].first+o
        end
        p2 -= 1
      end
      p2 += 1
      if o == tx
        if verif.end_with?(o) && text.start_with?(verif) && text[verif.length..-1].start_with?(o)
          # duplicate is intentional
          toks << tok
          return true
        end
        if add_always
          toks << tok
          return false
        end
        if Configuration.together.find{|t| tx =~ t}
          (-p2).times {|i| toks.pop}
          toks << tok
          return false
        else
          return false
        end
      end
    end
    toks << tok
    return true
  end
  
  def self.analyse texts, tagset, postprocess = true
    return [] if texts.empty?
    
    text = texts.join+"_"   # _ is a sentry

    @@morfeusz ||= IO.popen(Configuration.morfeusz_path, "r+")
    @@morfeusz.puts text
    @@morfeusz.flush
    line = @@morfeusz.gets
    nogets = false
    nogets = true if line =~ />\[.*\]/
    toks = []
    verif_text = ""
    begin
      if nogets
        nogets = false
      else
        line = @@morfeusz.gets
      end
      line = line[1..-1] if line[0] == ">"
      line = line.strip[1..-2] # []
      lxs = []
      o = nil
      if line == ";,;,#{Configuration.interp_tag}"
        toks << [";", [[";", Configuration.interp_tag]]]
        verif_text += ";"
        next
      end
      line.split(";").each do |x|
        obt = x.strip.split(",")
        obt = [",", ",", obt[4]] if obt.length == 5 && obt[4] == Configuration.interp_tag && obt[0..3] == ["","","",""]
        o ||= obt[0]
        if obt[1] == "?"
          next
        end
        if obt[2] == "sp"
          lxs << :sp
          o = " "
          next
        end
        tt = []
        obt[2].split("|").each do |ft|
          t = [""]
          pos = nil
          ft.split(":").each_with_index do |tp, attrid|
            pos = tp if attrid == 0
            vs = tp.split(".")
            if tp == "_"
              vs = tagset.attrs[tagset.poss[pos][attrid-1]]
            end
            t = t.map{|partial| vs.map{|v| partial+":"+v}}.flatten
          end
          tt += t
        end
        lxs += tt.map{|x| [obt[1], tagset.correct_analyser(x[1..-1])]}
      end
      if lxs == [:sp]
        toks << [:space, nil]
        verif_text += o
      else
          verif_text += o if insert_with_check_for_duplicate? toks, [o, lxs.uniq], text, verif_text, !postprocess
      end
    end while text != verif_text && text.length > verif_text.length

    toks = toks[0..-2] # remove the sentry
    if postprocess
      toks = correct_meta_toks toks
      toks = align toks, texts, tagset
    end
    return toks
  end
end


