# == Schema Information
# Schema version: 51
#
# Table name: nowa_segmentacja
#
#  nowa_segmentacja_id :integer         primary key
#  ids                 :text            
#  total_akapitow      :integer         
#  seg_is              :text            
#  seg_should_be       :text            
#  nps                 :boolean         default(TRUE), not null
#  wprowadzona         :boolean         
#  akapit_transzy_id   :integer         
#  transza_id          :integer         
#

 #
 # This file is part of the Anotatornia suite.
 # 
 # Copyright © 2007, 2008, 2009, 2010 by Instytut Podstaw Informatyki
 # Polskiej Akademii Nauk (IPI PAN; Institute of Computer Science, Polish
 # Academy of Sciences; cf. www.ipipan.waw.pl).  All rights reserved.
 # 
 # This file may be distributed and/or modified under the terms of the
 # GNU General Public License version 3 as published by the Free Software
 # Foundation and appearing in the file COPYING included in the packaging
 # of this file.  (See http://www.gnu.org/licenses/translations.html for
 # unofficial translations.)
 # 
 # A commercial license is available from IPI PAN (contact
 # Michal.Ciesiolka.waw.pl or ipi.waw.pl for more
 # information).  Licensees holding a valid commercial license from IPI
 # PAN may use this file in accordance with that license.
 # 
 # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 # THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 # PURPOSE.
 #

require 'ramkowanie'


class NowaSegmentacja < ActiveRecord::Base

  has_one :prosby_anotatorek, :as => :dotyczy
  serialize :ids # są to id-y akapitów zawierających układ tokenów podany w~#{seg_is}. Zapisujemy je w~tabeli, żeby nie powtarzać kosztownego wyszukiwania, jednak użyjemy tej zapisanej wartości tylko wówczas, gdy w~momencie jej użycia liczba akapitów w~bazie jest taka, jak w~momencie utworzenia danego rekordu.
  serialize :seg_is
  serialize :seg_should_be

  include XpointerCreator


  def fresh_ids
    if self.total_akapitow == Akapit.count
      self.ids
    else
      Akapit.znajdź_po_orthach( self.seg_is, :strict ).collect{ |a142| a142.id }
    end
  end # of #fresh_ids


  def instanze( is_or_should = :seg_is ) ## ( która=nil )
    ins142 = []
    is142 = self.send( is_or_should )
    is_size142 = is142.size
    # rozważałem utworzenie wersji tekstu akapitu, w~której tokeny są rozdzielone swoimi id-ami i~dopasowywanie specjalnie skonstruowanego wyrażenia regularnego. Ale to by było chyba kosztowniejsze od przejrzenia $n$\dy elementowych podtablic i~porównanie ich równościowe.
    
    ##    if która # a~ jednak  wariant tablicowy w~bazie \rz{III} testu wykonuje się 7\,s.
    ##      Akapit.find( self.fresh_ids, :include => :token ).each {|a|
    ##        0.upto( a.token.size - is_size ) { |i|
    ##          if ( a.tokorths[ i, is_size ] ) == is
    ##            ins << a.tokids[ i, is_size ]
    ##          end
    ##        }
    ##      }
    ##    else # a~wariant regexpowy — w~< 1\,s.
    rx142 = Regexp.new(   is142.collect {|s142| Regexp.escape( s142 ) + '\((\d+)\)' }.join )
    ## puts rx142.inspect
    subs_a142 = Proc.new {eval( '[' + ("$1".."$#{is142.size}").to_a.join(', ') + ']')}
    # w~poprzedniej wersji regexp zaczynało się od jeszcze jednej grupy #{'(?:^|.*\d\))'}, co wobec zachłanności dopasowań powodowało znalezienie jedynie ostatniego wystąpienia szukanego układu tokenów.
    
    Akapit.find( self.ids # było: #{fresh_ids}, ale okazuje się, że należy to dodawać tylko w~tym akapicie, w~którym zgłoszono. 
                 ).each {|a143|
      ati143 =      a143.treść_idowana
      ##      puts ati143
ati143.gsub( rx142 ) { |match| ins142 << subs_a142.call }
    }

    logger.info "@@@@@ instancje n.segm.: #{ins142.inspect}"
    
    ins142.collect! {|ids142|   Token.find( ids142 ) }
    
    ##  end
    ins142 
  end # of #instanze


  def should_xp( dii144 )
    create_xpointers( self.seg_should_be, dii144, self.seg_is )
    # z modułu #XpointerCreator zdef. w \file{ramkowanie.rb}
  end # of #{should_xp}

  
  def commit # wołana wyłącznie z~#{aProsbyAnotatorek.commit}.
    #    zwrócimy hasz idów obsłużonych akapitów i~tokenów i~dodanych tokenów
    instantial194 = 
      ##      ( self.ids.sort == ( self.ids &
      ##                           Akapit.znajdź_po_orthach( self.seg_is, :strict ).collect{ |x194| x194.id }
      ##                           ).sort ) 
      self.instanze( :seg_is )[0]   and # »jest« występuje
      not self.instanze( :seg_should_be )[0]  # »ma być« nie występuje

    if ( not self.wprowadzona? ) and instantial194
      ## puts "@@@@ nowa_segmentacja nie wprowadzona, popełniam"

      di145 = self.instanze
      ##      puts " @@@ mam #{di145.size} wystąpień"
      di_sgch_ids = di145.collect { |instanza|
        ico145 = instanza.collect {|t145| t145.sg_choice_id }.compact.uniq
        raise "aNowaSegmentacja.commit: Obecnie nie obsługuję przypadku przynależności do >1 sg_choice'ów" if ico145.size > 1 
        ico145[0]          
      }
      akids145 = []
      tokids145 = []
      logger.info "@@@@ aNowaSegmentacja.commit: di_sgch_ids==#{di_sgch_ids.inspect}"
      di145.each_index { |i145|
        # dla każdej instancji układu tokenów »is«
        dii145 = di145[ i145 ] # to są tokeny zastane
        if j145 = di_sgch_ids[ i145 ]
          sg_choice145 = SgChoice.find( j145 )
        else
          sg_choice145 = SgChoice.create!(
                                       :akapit_id => dii145[ 0 ].akapit_id,
                                       :dodany => true
                                       )
        end
        sgvs145 = dii145.collect {|t146| t146.sg_variant }.compact
        
        # jeśli mamy dokładnie 1 wariant segmentacyjny, to sprawdzamy, czy jest on równoważny naszemu »is«
        # jeśli nie, to kopiujemy tokeny do nowego wariantu.
        # jeśli jest więcej niż 1 wariant, to też kopiujemy tokeny.
        if  sgvs145.size == 1 
          if sgvs145[0].token.collect { |t147| t147.orth} == self.seg_is
            # porównanie tabel tokenów wariantu i~#dii działało źle.
            copy_is_variant = false
            v145 = sgvs145[0]
          else
            copy_is_variant = true
            v145 = nil
          end
        elsif sgvs145.size == 0
          copy_is_variant = false
          v145 = nil
        else
          copy_is_variant = true
          v145 = nil
        end
      
        v145 ||= sg_choice145.sg_variant.create!( :dodany => true ) 

        unless copy_is_variant
          # not #{copy_is_variant}
          dii145.each{ |t148|
            t148.sg_choice_id = sg_choice145.id
            t148.sg_variant_id = v145.id
            t148.save!            
          }
        end # of #{unless copy_is_variant}

        # Teraz tworzymy wariant(y) (ewentualnie dla »is« i) dla »should_be«. Że ten ostatni jest inny, sprawdziliśmy przy zapisywaniu prośby.
       
        t_ost = dii145.collect { |t149| 
          ((( sv149 = t149.sg_variant ) && sv149.token) || [t149] )[-1]
          # jeśli dany token naszego układu ma #{sg_variant}, to bierzemy ostatni token takiego wariantu, a~jeśli dany token jest bezwariantowy, to bierzemy sam ten token. Z tak utworzonej tabeli pobieramy element ostatni: jesteśmy pewni, że wszystkie tokeny danego wariantu następują przed następnym tokenem. Zapewne overkill, bo jest niemożliwe podanie jako »is« niespójnego podciągu tokenów (nie przeszłoby sprawdzenia).
        }[-1]

logger.info "@@@@ aNowaSegmentacja.commit 180"
        
        kol_ost = t_ost.kolejnosc
        raise "aNowaSegmentacja.commit: kolejność is NULL in token #{t_ost.id}"   unless kol_ost 

        # token następny jest zawsze wybrany. A w~przypadku dodawania wariantu segmentacyjnego nie zawsze musi o~to chodzić: 2010/4/7 zdarzyło się, że był już dodany jakiś wariant segmentacyjny, a~chcieli dodać inny, lepszy. Powinni móc to zrobić, o~ile tylko  rozdzielczość kolejności na to pozwala.
        ##        kol_nast = if t178 = t_ost.następny( :nie_krzycz )
        ##                     t178.kolejnosc
        ##                   else kol_ost + 216
        ##                   end
        ##        raise "aNowaSegmentacja.commit: kolejność is NULL in token #{t_ost.id}"   unless kol_nast
        kol_nast = Token.minimum( :kolejnosc, :conditions => " kolejnosc> #{kol_ost}" )
        kol_nast = kol_ost + 216 unless kol_nast # w~przypadku, gdybyśmy byli na kolejnościowym końcu tokenów.
        
        news_size = if copy_is_variant
                      self.seg_is.size  
                    else 0
                    end + self.seg_should_be.size

        kol_nowa =  ( kol_ost + kol_nast ) / 2
        # przez chwilę zastanawiałem się, czy przedziału nie należy „wgłębiać” o~(mniejszą) połowę rozmiaru tego, co dodajemy. Myślę jednak, że nie: wskutek „wgłębienia” dostajemy po obu stronach coś być może nieparzystego, a~bez wgłębiania — co najwyżej po jednej.

        if Token.find_by_kolejnosc( (kol_nowa...(kol_nowa + news_size)).to_a )
          # to znajdzie nam pierwszy token o~kolejności z~podanego zakresu, o~ile taki jest.
          raise "aNowaSegmentacja.commit: próba zbyt zagnieżdżonego dodawania wariantów"
        end
        
        start = 0
        
        copy_tokens = Proc.new { |field150, xp150|
          ( ssb150 = field150 ).each_index{ |j150|
            t150 = dii145[ 0 ]
            t_nowy150 = Token.create!(
                                      :kolejnosc => kol_nowa + start + j150,
                                      :akapit_id => t150.akapit_id,
                                      :xpointer => xp150[ j150 ],
                                      ##  fs_morph_comment             :text            
                                      :path_id => t150.path_id,
                                      :orth => ssb150[ j150 ],
                                      :czy_interp  => if /\w/ =~ ssb150[ j150 ] : false else true end,
                                      :czy_konczy_zdanie => if j150 < ssb150.size - 1
                                                              false
                                                            else
                                                              dii145[ -1 ].czy_konczy_zdanie?
                                                            end,
                                      # jeśli tworzony token jest nieostatni, to nie kończy zdania (nie może być segmentacji międzyzdaniowej), jeśli zaś jest ostatni, to bierzemy wartość ostatniego tokenu zastanego.
                                      :czy_konczy_zdanie_updated_at => Time.now,
                                      :sg_choice_id => sg_choice145.id,
                                      :sg_variant_id => v145.id,
                                      :dodany  => true,
                                      :chosen => true
                                      ##  chosen_updated_at
                                      ##  created_at
                                      ##  updated_at
                                   )

            ##            logger.info "@@@ committed token #{t_nowy150.inspect}"
          
            xmlid150 = "#{t150.akapit_id}.#{t_nowy150.id}"
          
            t_nowy150.ns_poprzedza = if j150 == 0
                                        dii145[ 0 ].ns_poprzedza?
                                      else
                                        self.nps?
                                      end
            # jeśli jesteśmy pierwszym tokenem instancji, to bierzemy nps pierwszego zastanego; jeśli zaś nie, to bierzemy wartość przekazaną w~prośbie.
            
            t_nowy150.ns_nastepuje = if  j150 < ssb150.size - 1
                                       self.nps?
                                     else
                                       dii145[ -1 ].ns_nastepuje?
                                     end
            # dla nieostatniego tokenu bierzemy co podano w~prośbie; dla ostatniego — wartość z~ostatniego zastanego.

            t_nowy150.segmentation_xmlid = xmlid150
            t_nowy150.morphosyntactic_xmlid = xmlid150
            t_nowy150.save!
            akids145 << t150.akapit_id
            tokids145 << t_nowy150.id
          } # of each (is or) should-be token
        } # of #copy_token

        start = 0
        if copy_is_variant
          copy_tokens.call( self.seg_is, dii145.collect {|t151| t151.xpointer })
          start = self.seg_is.size
        end

        # a~teraz (wreszcie) ten nowy wariant.
        v145 = sg_choice145.sg_variant.create!( :dodany => true )
        copy_tokens.call( self.seg_should_be, self.should_xp( dii145 ) )

      } # of each instanza (index)
      
      if akids145[0]
        Akapit.find( akids145.uniq, :include => :akapit_transzy ).each { |a152|
          a152.update_treści
          a152.akapit_transzy.each{ |at152| 
            at152.set_status( :segmentation, :dopuszczony, true )
          }
        }
      end
      
      self.wprowadzona = true
      self.save!
      
      if akids145[0] or tokids145[0]
        { :akids => akids145, :tokids => tokids145 }
      else
        nil
      end    
    elsif not instantial194
        # jeżeli docelowy układ tokenów już występuje w~którymś z~podanych akapitów, to oznaczamy się jako wprowadzoną.
      self.wprowadzona = true
      self.save!
    end # of if (not wprowadzona) and instantial194
    logger.info "@@@@ aNowaSegmentacja.commit 292"
  end # of #{commit}
    
end # of class
