# $Id: extension.rb,v 1.5 2004/04/22 14:47:49 eto Exp $
# Copyright (C) 2003-2004 Kouichirou Eto, All rights reserved.
# This is free software with ABSOLUTELY NO WARRANTY.
# You can redistribute it and/or modify it under the terms of the GNU GPL2.

module Qwik
  class PluginException < Exception; end

  class Plugin
    METHODS = {} # use as class var
    NAMES = %w(header update delete body_enter body_leave page_attribute footer edit form)
    NAMES.each {|n|
      METHODS[n] = []
      eval("def #{n}_proc()   method('#{n}')   end")
      eval("def self.add_#{n}_method(m) add_method('#{n}', m) end")
    }

    def initialize(config, options)
      @config = config
      init_plugin(@config.lib_dir+"/qwik/ext_*.rb")
      @options = options
      options.each_key {|opt|
        eval("@#{opt} = options['#{opt}']") unless opt.index('.')
      }
      @g = Qxhtml.new
    end

    def self.add_method(n, m)
      METHODS[n] << m unless METHODS[n].include?(m)
    end

    def method(n)
      res = []
      METHODS[n].each {|m|
        begin
	  res << send(m)
        rescue Exception
          res << extension_error(n+"_method", $!)
        end
      }
      res
    end

    def call_plugin_method(method, param, data)
      param = nil if param == ""

      args = nil
      if param
	args = csv_split(param).collect! {|a|
	  case a
	  when /^[-+]?\d+(\.\d+)?$/
	    $1 ? a.to_f : a.to_i
	  when /^'(.+)'$/, /^"(.+)"$/
	    $1.escapeHTML
	  else
	    a
	  end
	}
      end

      res = []
      begin
	if self.respond_to?(method) && !Object.method_defined?(method)
	  if args
	    res << self.send( method, *args ) { data }
	  else
	    res << self.send( method ) { data }
	  end
	else
	  res << "nosuch plugin"
	end
      rescue
	res << [$!.to_s, $!.backtrace].inspect
      end
      res
    end

    private

    def init_plugin(glob)
      Dir.glob(glob) {|f|
	next unless FileTest.file?(f)
	require f
      }
    end

    def plugin_error( method, e )
      msg = "<strong>#{e.class}(#{e.message}): #{method.escapeHTML}</strong><br>"
      e.backtrace.each {|bt| msg << "<strong>#{bt}</strong><br>" }
      msg
    end

    def csv_split( source, delimiter = ',' )
      status = :IN_FIELD
      csv = []
      csv.push(last = "")
      while !source.empty?
	case status
	when :IN_FIELD
	  case source
	  when /^'/
	    source = $'
	    last.concat "'"
	    status = :IN_QFIELD
	  when /^#{delimiter}/
	    source = $'
	    csv.push(last = "")
	  when /^(\\|\s)/
	    source = $'
	  when /^([^#{delimiter}'\\\s]*)/
	    source = $'
	    last.concat $1
	  end
	when :IN_QFIELD
	  case source
	  when /^'/
	    source = $'
	    last.concat "'"
	    status = :IN_FIELD
	  when /^(\\)/
	    source = $'
	    last.concat $1
	    status = :IN_ESCAPE
	  when /^([^'\\]*)/
	    source = $'
	    last.concat $1
	  end
	when :IN_ESCAPE
	  if /^(.)/ =~ source
	    source = $'
	    last.concat $1
	  end
	  status = :IN_QFIELD
	end
      end
      csv
    end
  end
end
