# encoding: utf-8


module Culter end			# only to avoid errors

module Culter::XML end

module Culter::XML::Load
    
    begin
        require 'anguilla'  # If available, can increase load performances
        def load(src, extension, callback) Anguilla::parse(src,callback) end
    rescue LoadError    # However this should not be a strict dependency for Culter
        def load(src, extension, callback)
            if src.is_a? String then
                if (src =~ /\.(xml|#{extension})$/) then 
                    callback.file = src if callback.respond_to?('file=') 
                    File.open(src, 'r:UTF-8') { |source| REXML::Document.parse_stream(source, callback) } 
                elsif src =~ /<\w/ then
                    REXML::Document.parse_stream(src, callback)
                end
            elsif src.is_a? IO then
                REXML::Document.parse_stream(src, callback)
            end
        end
	end

end
	
module Culter::XML::Convert

	##
	# Display the result in SRX format
	# Param dest (IO): if present, write to the IO. Else, return a string
	# Param version: can be used to generate SRX 1.0
	# Param langs: if provided, array of languages to generate an non-cascade SRX file
	# Param mapruleName: to generate SRX 2.0 from other format having several rule names, select one
	# Param mode: one of
	#		machine (default):	converts template to big regular expressions, which are faster to parse_stream
	#		human:	converts template to set of rules. Slower, but easier to read for humans.	
	def to_srx(dest = nil, version = '2.0', langs = nil, mapruleName = nil, mode = 'machine')
		if dest == nil
			dest = StringIO.new
			to_srx(dest, version, langs, mapRuleName, mode)
			return dest
		else
			dest.puts "<srx version='#{version}' xmlns='http://www.lisa.org/srx#{version.gsub(/\./,'')}'>"
			if version =~ /^1/ then 
				dest.puts "\t<header segmentsubflows='true' />"
			else
				if langs == nil then cascadeSt = @cascade end
				if cascadeSt then cascadeSt = 'yes' else cascadeSt = 'no' end				
				dest.puts "\t<header segmentsubflows='yes' cascade='#{cascadeSt}'>"
				@formatHandle.each do |k,v| 
					dest.write "\t\t<formathandle type='#{k}' "
					if v then dest.puts "include='yes' />" else dest.puts "include='no' />" end 
				end
				dest.puts "\t</header>"
			end
			dest.puts "\t<body>"
			dest.puts "\t\t<languagerules>"
			if langs == nil then
				@langRules.each do |k,v| 
					dest.puts "\t\t\t<languagerule languagerulename='#{k}'>"
					v.each { |r| r.to_srx(dest, mode) }
					dest.puts "\t\t\t</languagerule>"
				end
			else
				langs.each do |lg|
					dest.puts "\t\t\t<languagerule languagerulename='#{lg}'>"
					self.segmenter(lg,mapruleName == nil ? '' : mapruleName).rules.each { |r| r.to_srx(dest) }
					dest.puts "\t\t\t</languagerule>"					
				end				
			end
			dest.puts "\t\t</languagerules>"
			dest.puts "\t\t<maprules>"
			if langs != nil then
				if mapruleName == nil then mapruleName = 'default' end
				if version =~ /^1/ then dest.puts "<maprule maprulename='#{mapruleName}'>" end
				langs.each { |lg| dest.puts "\t\t<languagemap languagepattern='#{lg}' languagerulename='#{lg}' />" }
				if version =~ /^1/ then dest.puts "</maprule>" end
			else
				if version =~ /^1/ then
					if mapruleName != nil then 
						maps = { mapruleName => mapRules[mapruleName] }
					elsif @mapRules.empty? then
						maps = { 'default' => @defaultMapRule }
					end
					maps.each do |k,v|
						dest.puts "\t\t\t<maprule maprulename='#{k}'>"
						v.each { |i| dest.puts "\t\t\t#{i.to_srx}" }
						dest.puts "\t\t\t</maprule>"
					end
				else
					if mapruleName != nil then curMapRule = @mapRules[mapruleName] else curMapRule = @defaultMapRule end
					curMapRule.each { |i| dest.puts "\t\t\t#{i.to_srx}" }
				end
			end
			dest.puts "\t\t</maprules>"				
			dest.puts "\t</body>"
			dest.puts "</srx>"
		end
	end
	
		
	##
	# Display the result in CSCX format
	# Param dest (IO): if present, write to the IO. Else, return a string
	# Param langs: if provided, array of languages to generate an non-cascade SRX file
	# Param mapruleName: restrict to one map rule
	# Param ruleTemplates: optional set of templates which we will try to re-use
	def to_cscx(dest = nil, langs = nil, mapruleName = nil, ruleTemplates = nil)
		if dest == nil
			dest = StringIO.new
			to_cscx(dest, version, cascade, mapRuleName)
			return dest
		else
			dest.puts "<seg-rules xmlns='http://culter.silvestris-lab.org/compatible'>"
			dest.puts "\t<format-handles>"
			@formatHandle.each do |k,v| 
				dest.write "\t\t<formathandle type='#{k}' "
				if v then dest.puts "include='yes' />" else dest.puts "include='no' />" end 
			end
			dest.puts "\t</format-handles>"	
			if langs == nil then cascadeSt = @cascade else cascade = false end
			dest.puts "\t<rules-mapping cascade='#{cascadeSt}'>"
			if langs != nil then
				langs.each { |lg| dest.puts "\t\t<languagemap languagepattern='#{lg}' languagerulename='#{lg}' />" }
			else
				if mapruleName != nil then 
					@mapRules[mapruleName].each { |i| dest.puts "\t\t\t#{i.to_srx}" }
				elsif @mapRules.keys.count > 1
					@mapRules.each do |mapName,mapVal|
						dest.puts "\t\t<rules-mapping-option name='#{mapName}'>"
						mapVal.each { |i| dest.puts "\t\t\t#{i.to_srx}" }
						dest.puts "\t\t</rules-mapping-option>"
					end
				else
					@defaultMapRule.each { |i| dest.puts "\t\t\t#{i.to_srx}" }
				end
			end
			dest.puts "\t</rules-mapping>"
			if (self.respond_to? 'ruleTemplates') or (ruleTemplates != nil) then
				dest.puts "\t<rule-templates>"
				if ruleTemplates == nil then 
					ruleTemplates = self.ruleTemplates
                   
				elsif self.respond_to? 'ruleTemplates' then
					self.ruleTemplates.each { |k,v| ruleTemplates[k] = v } # priority to rules from file
				end
				ruleTemplates.each do |k,v| 
					dest.puts "\t\t<rule-template name='#{k}'>"
					dest.puts "\t\t\t<rewrite>"
					v.rewriteRule.to_srx(dest)
					dest.puts "\t\t\t</rewrite>"					
					dest.puts "\t\t</rule-template>"
				end
				dest.puts "\t</rule-templates>"	
            end
            if ruleTemplates == nil then ruleTemplates = [] end  #only to avoid exception 
			dest.puts "\t<languagerules>"
			if langs == nil then
				@langRules.each do |k,v| 
					dest.puts "\t\t<languagerule languagerulename='#{k}'>"
					to_cscx_rules(k, dest,ruleTemplates,v) 
					dest.puts "\t\t</languagerule>"
				end
			else
				langs.each do |lg|
					dest.puts "\t\t<languagerule languagerulename='#{lg}'>"
					to_cscx_rules(lg, dest,ruleTemplates,self.segmenter(lg,mapruleName == nil ? '' : mapruleName).rules) 
					dest.puts "\t\t</languagerule>"					
				end				
			end
			dest.puts "\t</languagerules>"
			dest.puts "</seg-rules>"
		end
	end
    
    private 
    
    # Tries to find a template matching the rules, else use a standard conversion
    def to_cscx_rules(lang, dest,ruleTemplates,rules)
        prevTemplate = nil
        matchesList = []
        rules.each do |curRule| 
            if curRule.respond_to?('to_cscx') then 
                curRule.to_cscx(dest) # already in correct format
            else # Try to find a template. Temporarily restricted to 1-variable templates                
                found = false
                if prevTemplate != nil then
                   curMatch = prevTemplate.matches(curRule)
                   if curMatch != nil and curMatch.count == 1 then 
                       if $CULTER_VERBOSE > 2 then puts "Continue with template #{prevTemplate.name}" end                            
                       matchesList << curMatch 
                       found = true
                   else
                       # Transform matchesList to parameters. Temporarily restricted to 1-variable templates
                       params = {}; params[matchesList[0].keys[0]] = []
                       matchesList.each do |curMatch|
                          params[curMatch.keys[0]] << curMatch.values[0]
                       end
                       if params[params.keys[0]].count > 1 then
                           if $CULTER_VERBOSE > 2 then puts "#{lang}: Apply template #{prevTemplate.name} with #{params[params.keys[0]].count} items" end
                          Culter::CSC::ApplyRuleTemplate.new(prevTemplate,params).to_cscx(dest)
                       else
                           curRule.to_srx(dest)
                       end
                       prevTemplate = nil   # for current rule, search for a new template
                   end
                end
                if prevTemplate == nil then # was already nil or has just been reset
                    ruleTemplates.each do |k,v|
                        curMatch = v.matches(curRule)
                        if curMatch != nil and curMatch.count == 1 then 
                            if $CULTER_VERBOSE > 2 then puts "Found match for template #{k}" end                            
                            prevTemplate = v; matchesList = [curMatch]; found = true 
                        end
                    end
                end
                if not found then curRule.to_srx(dest) end    # no template found, display the rule as is
            end # cscx/srx
        end
        if prevTemplate != nil then # no more rule, but the last one is a template
            params = {}; params[matchesList[0].keys[0]] = []
            matchesList.each do |curMatch|
                params[curMatch.keys[0]] << curMatch.values[0]
            end
            Culter::CSC::ApplyRuleTemplate.new(prevTemplate,params).to_cscx(dest)
        end
    end
	
end

