require 'shellwords'
require 'clio/usage/signature'
require 'clio/usage/interface'

module Clio

  module Usage #:nodoc:

    # = Parser
    #
    # Parse commandline arguments according to given Usage.
    #
    class Parser
      attr :usage
      attr :argv

      attr :signatures

      #
      def initialize(usage, argv) #, index=0)
        # convert to array if argv string
        if ::String===argv
          argv = Shellwords.shellwords(argv)
        else
          argv = argv.dup
        end

        @usage  = usage
        @argv   = argv

        @parsed     = false
        @signatures = []
        @errors     = []
      end

      #
      def name ; usage.name ; end
      def key  ; usage.key  ; end

      #
      def inspect
        s = "<" + signatures.inspect + ">"
        s
        #s  = "#<#{self.class}"
        #s << " @options=#{@options.inspect}" unless @options.empty?
        #s << " @arguments=#{@arguments.inspect}" unless @arguments.empty?
        #s << " @subcommand=#{@subcommand}>" if @subcommand
        #s << ">"
        #s
      end

      #
      def parse
        @parsed     = false
        @signatures = []
        @errors     = []

        parse_command(usage, argv.dup)

        @parsed     = true

        return Interface.new(@signatures, @errors)
      end

      # Has the commandline been parsed?
      def parsed? ; @parsed ; end

    private

      # TODO: clean-up parsing
      def parse_command(usage, argv)
        options    = {}
        arguments  = []
        command    = nil

        #greedy = parse_greedy(usage, argv)

        #options.update(greedy)

        until argv.empty?
          use, val = parse_option(usage, argv)
          if use
#p "use: #{use.key}, #{val}"
            if val == "\t"
              parse_errors << [val, use]
            elsif use.multiple?
              options[use.key] ||= []
              options[use.key] << val
            else
              options[use.key] = val
            end
          elsif !usage.arguments.empty?
            usage.arguments.each do |a|
              if a.splat
                while argv.first && argv.first !~ /^[-]/
                  arguments << argv.shift
                end
                break if argv.empty?
              else
                arguments << argv.shift
              end
            end
          else
            break if argv.empty?
            arg = argv.shift
            use = usage.commands.find{|c| c === arg}
            if use
              parse_command(use, argv)
              break
            else
              parse_errors << [arg, usage]
              #raise ParseError, "unknown command or argument #{a} for #{usage.key} command"
              break
            end
          end
        end
        @signatures.unshift(Signature.new(usage.name, arguments, options))
      end

=begin
      # Parse greedy options. This function loops thru ARGV and
      # removes any matching greedy options.
      def parse_greedy(usage, argv)
        options = {}
        d, i = [], 0
        while i < argv.size
          case a = argv[i]
          when /^[-].+/
            #res = parse_option(a, i)
            name, val = *a.split('=')
            use = usage.greedy_option?(name)
            if use && use.greedy?
              d << i
              if use.flag?
                val = val || true
              else
                val = argv[i+1] unless val
                i += 1
                d << i
              end
              options[use.key] = val
            end
            res = val
          end
          i += 1
        end
        d.each{ |i| argv[i] = nil }
        argv.compact!
        return options
      end
=end

      #
      def parse_option(usage, argv)
        return if argv.first =~ /^[-]+$/
        return if argv.first !~ /(^-.+|=)/
        arg = argv.shift
        name, val = *arg.split('=')
        if use = usage.option?(name)
          if use.flag?
            val = true unless val
          else
            val = argv.shift unless val
          end
          #options[use.name] = val
        else
          parse_errors << [arg, usage.key]
        end
        return use, val
      end

    public

      #
      def errors
        @errors
      end

      alias_method :parse_errors, :errors

    end #class Parser

    #
    class ParseError < StandardError
    end

  end #class Usage

end #module Clio


