#!/usr/bin/env ruby

# Note: this program is part of qtruby and makes use of its internal functions.
#       You should not rely on those in your own programs.

require 'Qt4'
require 'getoptlong'

$usage = <<USAGE
rbqtapi - a qtruby introspection tool

usage: rbqtapi [-h] [-v] [-p] [-m <re> [-i]] [-r <extension> ...] [<class>]

options:
    -m <re> : find all functions matching regular expression/keyword <re>
    -i : together with -m, performs a case insensitive search
    -p : also display inherited methods for <class>
    -s | show method names with ruby types
    -r : require a qtruby extension
    -v : print qtruby and Qt versions
    -h : print this help message
USAGE

pattern = helpOpt = matchOpt = parentsOpt = showOpt = versionOpt = insensitiveOpt = nil

opts = GetoptLong.new(
  [ "--help", "-h", GetoptLong::NO_ARGUMENT ],
  [ "--match", "-m", GetoptLong::REQUIRED_ARGUMENT ],
  [ "--parents", "-p", GetoptLong::NO_ARGUMENT ],
  [ "--show", "-s", GetoptLong::NO_ARGUMENT ],
  [ "--version", "-v", GetoptLong::NO_ARGUMENT ],
  [ "--insensitive", "-i", GetoptLong::NO_ARGUMENT ],
  [ "--require", "-r", GetoptLong::REQUIRED_ARGUMENT ]
)

opts.each do |opt, arg|
  case opt
  when '--parents'
    parentsOpt = true
  when '--insensitive'
    insensitiveOpt = true
  when '--match'
    matchOpt = arg
  when '--show'
    showOpt = arg
  when '--version'
    versionOpt = true
  when '--help'
    helpOpt = true
  when '--require'
    require(arg)
  end
end

if versionOpt
    print "QtRuby #{Qt::qtruby_version} using Qt-#{Qt::version}\n"
    exit 0 
elsif helpOpt
    print $usage
    exit 0
end

if matchOpt
  pattern = insensitiveOpt ? Regexp.new(matchOpt, Regexp::IGNORECASE) : Regexp.new(matchOpt)
end

classnames = []
if ARGV.length == 0
  classnames = Qt::Internal.classes.keys
else
  ARGV.each do |classname|
    classid = Qt::Internal::find_pclassid(classname)
    if !classid.index
      puts "ERROR: class '#{classname}' not found"
      exit 1
    end
    classnames << classname
  end
end

candidates = ""

classnames.each do |classname|
  classid = Qt::Internal::find_pclassid(classname)
  if !classid.index
    next
  end

  a = Qt::Internal.findAllMethods(classid)
  ids = (a.keys.sort.map{|k|a[k]}).flatten
  candidates << Qt::Internal::dumpCandidates(ids)

  if parentsOpt
    superclasses = []
    Qt::Internal::getAllParents(classid, superclasses)
    superclasses.each do |klass|
        a = Qt::Internal::findAllMethods(klass)
        ids = (a.keys.sort.map{|k|a[k]}).flatten
        candidates << Qt::Internal::dumpCandidates(ids)
    end
  end
end

class String
  def to_ruby_method(show)
    if !show
      return self
    end

    method_name = self.clone

    if method_name =~ /([:A-Za-z0-9]+)\*.*[ :]+([A-Za-z0-9]+)::([A-Za-z0-9]+)(\(.*\))/ && $2 == $3
      method_name = $1 + '#new' + $4 + "\n"
    end

    method_name.gsub!(/char*/, 'String')
    method_name.gsub!(/[*&]/, '')
    method_name.gsub!(/KSharedPtr<([^>]*)>/) { $1 }
    method_name.gsub!(/QPair<([^>]*)>/) { '[' + $1 + ']' }
    method_name.gsub!(/(static|void|const) /, '')
    method_name.gsub!(/operator/, '')
    method_name.gsub!(/ const/, '')
    method_name.gsub!(/enum /, '')
    method_name.gsub!(/QStringList/, '[String, ...]')
    method_name.gsub!(/QString/, 'String')
    method_name.gsub!(/bool/, 'Boolean')
    method_name.gsub!(/mode_t|time_t|long/, 'Integer')
    method_name.gsub!(/float|double|qreal/, 'Float')
    method_name.gsub!(/q?u?longlong/, 'Integer')
    method_name.gsub!(/^q?u?(int|long|short)[0-9]?[0-9]?/, 'Integer')
    method_name.gsub!(/([^A-Za-z])q?u?(int|long|short)[0-9]?[0-9]?([,)])/) { $1 + 'Integer' + $3 }
    method_name.gsub!(/Q(List|Vector)<([^>]+)>/) { '[' + $2 + ', ...]' }
    method_name.gsub!(/Q(Hash|Map)<([^>]+),([^>]+)>/) { '{' + $2 + ' => ' + $3 + ', ...}' }
    method_name.gsub!(/::setRange\(([a-zA-Z]+), ([a-zA-Z]+)\)/) { '::range=' + $1 + '..' + $2 }
    method_name.gsub!(/::(is|has)([A-Z])([\w]+)\(/) { '::' + $2.downcase + $3 + "?" + "(" }
    method_name.gsub!(/::set([A-Z])([a-zA-Z]+)(\([^,]*\))/) { '::' + $1.downcase + $2 + '=' + $3 }
    method_name.sub!(/::([a-z])/) { '#' + $1 }
    method_name.gsub!(/Q([A-Z])/) { 'Qt::' + $1 }
    method_name.sub!('()', '')
    return method_name
  end
end

candidates.gsub!("\t", "")
candidates.split($/).each do |candidate|
  if candidate =~ /~|qt_metacall|staticMetaObject/
    next
  end
  puts candidate.to_ruby_method(showOpt) if pattern.nil? || pattern =~ candidate
end

# kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off;
