module Temple::Mixins::EngineDSL

def after(name, *args, &block)

def after(name, *args, &block)
  name = chain_name(name)
  e = chain_element(args, block)
  found, i = false, 0
  while i < chain.size
    if chain[i].first == name
      found = true
      i += 1
      chain.insert(i, e)
    end
    i += 1
  end
  raise "#{name} not found" unless found
  chain_modified!
end

def append(*args, &block)

def append(*args, &block)
  chain << chain_element(args, block)
  chain_modified!
end

def before(name, *args, &block)

def before(name, *args, &block)
  name = chain_name(name)
  e = chain_element(args, block)
  found, i = false, 0
  while i < chain.size
    if chain[i].first == name
      found = true
      chain.insert(i, e)
      i += 2
    else
      i += 1
    end
  end
  raise "#{name} not found" unless found
  chain_modified!
end

def chain_callable_constructor(filter)

def chain_callable_constructor(filter)
  raise(ArgumentError, 'Class or callable argument is required') unless filter.respond_to?(:call)
  proc {|engine| filter }
end

def chain_class_constructor(filter, option_filter)

def chain_class_constructor(filter, option_filter)
  local_options = option_filter.last.respond_to?(:to_hash) ? option_filter.pop.to_hash : {}
  raise(ArgumentError, 'Only symbols allowed in option filter') unless option_filter.all? {|o| Symbol === o }
  define_options(*option_filter) if respond_to?(:define_options)
  proc do |engine|
    filter.new({}.update(engine.options).delete_if {|k,v| !option_filter.include?(k) }.update(local_options))
  end
end

def chain_element(args, block)

def chain_element(args, block)
  name = args.shift
  if Class === name
    filter = name
    name = filter.name.to_sym
  else
    raise(ArgumentError, 'Name argument must be Class or Symbol') unless Symbol === name
  end
  if block
    raise(ArgumentError, 'Class and block argument are not allowed at the same time') if filter
    filter = block
  end
  filter ||= args.shift
  case filter
  when Proc
    # Proc or block argument
    # The proc is converted to a method of the engine class.
    # The proc can then access the option hash of the engine.
    raise(ArgumentError, 'Too many arguments') unless args.empty?
    [name, chain_proc_constructor(name, filter)]
  when Class
    # Class argument (e.g Filter class)
    # The options are passed to the classes constructor.
    [name, chain_class_constructor(filter, args)]
  else
    # Other callable argument (e.g. Object of class which implements #call or Method)
    # The callable has no access to the option hash of the engine.
    raise(ArgumentError, 'Too many arguments') unless args.empty?
    [name, chain_callable_constructor(filter)]
  end
end

def chain_modified!

def chain_modified!
end

def chain_name(name)

def chain_name(name)
  name = Class === name ? name.name.to_sym : name
  raise(ArgumentError, 'Name argument must be Class or Symbol') unless Symbol === name
  name
end

def chain_proc_constructor(name, filter)

def chain_proc_constructor(name, filter)
  raise(ArgumentError, 'Proc or blocks must have arity 0 or 1') if filter.arity > 1
  method_name = "FILTER #{name}"
  if Class === self
    define_method(method_name, &filter)
    filter = instance_method(method_name)
    if filter.arity == 1
      proc {|engine| filter.bind(engine) }
    else
      proc do |engine|
        f = filter.bind(engine).call
        raise 'Constructor must return callable object' unless f.respond_to?(:call)
        f
      end
    end
  else
    (class << self; self; end).class_eval { define_method(method_name, &filter) }
    filter = method(method_name)
    proc {|engine| filter }
  end
end

def prepend(*args, &block)

def prepend(*args, &block)
  chain.unshift(chain_element(args, block))
  chain_modified!
end

def remove(name)

def remove(name)
  name = chain_name(name)
  found = false
  chain.reject! do |i|
    if i.first == name
      found = true
    else
      false
    end
  end
  raise "#{name} not found" unless found
  chain_modified!
end

def replace(name, *args, &block)

def replace(name, *args, &block)
  name = chain_name(name)
  e = chain_element(args, block)
  found = false
  chain.each_with_index do |c, i|
    if c.first == name
      found = true
      chain[i] = e
    end
  end
  raise "#{name} not found" unless found
  chain_modified!
end