// Copyright 2013 Traceur Authors. // // Licensed under the Apache License, Version 2.0 (the “License”); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an “AS IS” BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License.

var format = require('util').format;

function addAbbrev(o) {

var ks = [''].concat(Object.keys(o).sort()), k, kprev = '';
for (var i = ks.length - 1; i > 0; i--) {
  var ka = k = ks[i], pre = 0;

  // find length of common prefix, clamp to min of 1.
  while (kprev[pre] === k[pre]) {
    pre++;
  }
  pre = pre || 1;

  // add all unique prefixes for k.
  while (!o[ka = ka.slice(0, -1)] && ka.length > pre && ka > ks[i - 1]) {
    o[ka] = o[k];
  }
  kprev = k;
}

}

function Getopt(opts) {

this.opt = null;
this.optarg = null;
this.optopt = null;
this.optdata = null;

this.optind = 2;
this.nextchar = 0;

this.opts_ = Object.create(null);
for (var i = 0; i < opts.length; i++) {
  var opt = opts[i], data = null, m;
  if (Array.isArray(opt)) {
    data = opt[1] || null;
    opt = opt[0];
  }
  if (!(m = opt.match(/^([\w\-]+)(:{0,2})$/))) {
    throw new Error('invalid option initializer: ' + opt);
  }
  this.opts_[m[1]] = {name: m[1], arg: m[2], data: data};
}
addAbbrev(this.opts_);

}

Getopt.prototype = {

getopt: function(argv) {
  var m, arg, optInf;
  this.opt = this.optarg = this.optopt = this.optdata = null;
  if (this.optind >= argv.length) {
    return false;
  }
  arg = argv[this.optind];
  if (!this.nextchar && /^-[^\-]/.test(arg)) {
    this.nextchar = 1;
  }
  if (this.nextchar) {
    // short opt
    this.opt = arg[this.nextchar] || null;
    this.optarg = arg.slice(++this.nextchar) || null;
  } else if (m = arg.match(/^--([^=]+)(?:=(.*))?$|^--(.+)$/)) {
    // long opt
    this.opt = m[1] || m[3];
    this.optarg = m[2] === undefined ? null : m[2];
  } else {
    // free arg
    this.optind++;
    this.opt = '=';
    this.optarg = arg;
    return true;
  }

  if (optInf = this.opts_[this.opt]) {
    this.opt = optInf.name;
    this.optdata = optInf.data;
    switch (optInf.arg) {
      case '':
        // no arg
        if (!this.nextchar && this.optarg) {
          // unexpected arg
          this.optopt = this.opt;
          this.opt = '!';
          break;
        }
        this.optarg = null;
        break;
      case ':':
        // required arg
        if (this.optarg === null) {
          if (++this.optind >= argv.length) {
            // missing arg
            this.optopt = this.opt;
            this.opt = ':';
            break;
          }
          this.optarg = argv[this.optind];
        }
        // fall through
      case '::':
        // optional arg
        this.nextchar = 0;
        break;
    }
  } else {
    // unknown opt
    this.optopt = this.opt;
    this.opt = '?';
  }

  this.optind += !(this.nextchar %= arg.length);

  return true;
},
message: function() {
  switch (this.opt) {
    case ':':
      return format('missing argument for \'%s\'.', this.optopt);
    case '?':
      return format('unknown option \'%s\'.', this.optopt);
    case '!':
      return format('\'%s\' does not take an argument.', this.optopt);
    case '=':
      return format('optarg \'%s\'.', this.optarg);
    default:
      if (this.optarg === null)
        return format('opt \'%s\'.', this.opt);
      else
        return format('opt \'%s\', optarg \'%s\'.', this.opt, this.optarg);
  }
}

}

exports.Getopt = Getopt;