#!/usr/bin/env ruby
$: << File.expand_path('../rubylib/lib', File.dirname(__FILE__))
$: << File.expand_path('sparql/lib', File.dirname(__FILE__))
require "galaxy_tool"
require 'rubygems'
require 'sparql'
require 'rubyrdf'

class SPARQLExecutor < GalaxyTool::Base

  option :endpoint,
    :short => "-e",
    :long => "--endpoint <URL>",
    :class => String,
    :description => "use <URL> as SPARQL endpoint.",
    :proc => proc {|arg| arg.strip },
    :required => true

  option :query,
    :long => "--query-file <filepath>",
    :class => String,
    :description => "read query expression from <filepath>.",
    :proc => proc {|arg| File.read(arg).chomp },
    :required => true

  option :query,
    :short => "-q",
    :long => "--query <expression>",
    :class => String,
    :description => "use <expression> as query expression.",
    :proc => proc {|arg| arg.chomp },
    :required => true

  option_output :short => "-o",
    :long => "--output <filepath>",
    :description => "use <filepath> as output file path."

  option_info :long => "--info <filepath>",
    :description => "use <filepath> as info file path. default is standard out (/dev/stdout)."

  option_error_handler do |optparse, exception|
    $stderr.puts "Error: " + exception.message
    $stderr.puts optparse.help
    raise exception
  end

  def info(info_out)
    info_out.puts "Execute SPARQL query. The endpoint is #{options[:endpoint]}.\n"
  end

  def create_customized_client(endpoint)
    client = SPARQL::Client.new(endpoint)

    def client.parsed_xml(content)
      REXML::Document.new(content).root
    end

    def client.parse_xml(content)
      table = []
      xml = parsed_xml(content)
      
      head = xml.elements['head']
      variables = head.elements.map do |variable|
        variable.attributes['name']
      end
      table << variables.map {|v| "?" + v }
      
      case
      when boolean = xml.elements['boolean']
        boolean.text == 'true'
      when results = xml.elements['results']
        table += results.elements.map do |result|
          row = []
          result.elements.each do |binding|
            name = binding.attributes['name']
            value_node = binding.children.find {|n| !n.is_a?(REXML::Text) }
            value = parse_xml_value(value_node)
            row[variables.index(name)] = value
          end
          row
        end
      else
        raise NotImplementedError # TODO
      end  
      
      table
    end

    def client.graph
      @graph ||= RDF::Graph::Memory.new
    end

    def client.parse_xml_value(element)
      case element
      when REXML::Text
        element.value
      when REXML::Node
        case element.name.to_sym
        when :uri
          RDF::UriNode.new(element.text)
        when :literal
          RDF::PlainLiteralNode.new(element.text)
        when :bnode
          RDF::BlankNode.new(element.text, graph)
        else
          raise NotImplementedError # TODO
        end
      else
        raise NotImplementedError # TODO
      end
    end
    
    client
  end

  def search(client, query)
    client.query(query)
  end

  def main(output)
    client = create_customized_client(options[:endpoint])
    search(client, options[:query]).each do |row|
      output.puts row.join("\t")
    end
  end
end

if $0 == __FILE__
  SPARQLExecutor.new.run(ARGV)
end
