/* eslint-disable */
define(function () {
  var rison = {};
  //////////////////////////////////////////////////
  //
  //  the stringifier is based on
  //    http://json.org/json.js as of 2006-04-28 from json.org
  //  the parser is based on
  //    http://osteele.com/sources/openlaszlo/json
  //

  /**
   *  rules for an uri encoder that is more tolerant than encodeURIComponent
   *
   *  encodeURIComponent passes  ~!*()-_.'
   *
   *  we also allow              ,:@$/
   *
   */
  rison.uri_ok = {  // ok in url paths and in form query args
              '~': true,  '!': true,  '*': true,  '(': true,  ')': true,
              '-': true,  '_': true,  '.': true,  ',': true,
              ':': true,  '@': true,  '$': true,
              "'": true,  '/': true
  };

  /*
   * we divide the uri-safe glyphs into three sets
   *   <rison> - used by rison                         ' ! : ( ) ,
   *   <reserved> - not common in strings, reserved    * @ $ & ; =
   *
   * we define <identifier> as anything that's not forbidden
   */

  /**
   * punctuation characters that are legal inside ids.
   */
  // this var isn't actually used
  //rison.idchar_punctuation = "_-./~";

  (function () {
      var l = [];
      for (var hi = 0; hi < 16; hi++) {
          for (var lo = 0; lo < 16; lo++) {
              if (hi+lo == 0) continue;
              var c = String.fromCharCode(hi*16 + lo);
              if (! /\w|[-_.\/~]/.test(c))
                  l.push('\\u00' + hi.toString(16) + lo.toString(16));
          }
      }
      /**
       * characters that are illegal inside ids.
       * <rison> and <reserved> classes are illegal in ids.
       *
       */
      rison.not_idchar = l.join('')
      //idcrx = new RegExp('[' + rison.not_idchar + ']');
      //console.log('NOT', (idcrx.test(' ')) );
  })();
  //rison.not_idchar  = " \t\r\n\"<>[]{}'!=:(),*@$;&";
  rison.not_idchar  = " '!:(),*@$";


  /**
   * characters that are illegal as the start of an id
   * this is so ids can't look like numbers.
   */
  rison.not_idstart = "-0123456789";


  (function () {
      var idrx = '[^' + rison.not_idstart + rison.not_idchar +
                 '][^' + rison.not_idchar + ']*';

      rison.id_ok = new RegExp('^' + idrx + '$');

      // regexp to find the end of an id when parsing
      // g flag on the regexp is necessary for iterative regexp.exec()
      rison.next_id = new RegExp(idrx, 'g');
  })();

  /**
   * this is like encodeURIComponent() but quotes fewer characters.
   *
   * @see rison.uri_ok
   *
   * encodeURIComponent passes   ~!*()-_.'
   * rison.quote also passes   ,:@$/
   *   and quotes " " as "+" instead of "%20"
   */
  rison.quote = function(x) {
      if (/^[-A-Za-z0-9~!*()_.',:@$\/]*$/.test(x))
          return x;

      return encodeURIComponent(x)
          .replace('%2C', ',', 'g')
          .replace('%3A', ':', 'g')
          .replace('%40', '@', 'g')
          .replace('%24', '$', 'g')
          .replace('%2F', '/', 'g')
          .replace('%20', '+', 'g');
  };


  //
  //  based on json.js 2006-04-28 from json.org
  //  license: http://www.json.org/license.html
  //
  //  hacked by nix for use in uris.
  //

  (function () {
      var sq = { // url-ok but quoted in strings
                 "'": true,  '!': true
      },
      enc = function (v) {
          if (v && typeof v.toJSON === 'function') v = v.toJSON();
          var fn = s[typeof v];
          if (fn) return fn(v);
      },
      s = {
              array: function (x) {
                  var a = ['!('], b, f, i, l = x.length, v;
                  for (i = 0; i < l; i += 1) {
                      v = enc(x[i]);
                      if (typeof v == 'string') {
                          if (b) {
                              a[a.length] = ',';
                          }
                          a[a.length] = v;
                          b = true;
                      }
                  }
                  a[a.length] = ')';
                  return a.join('');
              },
              'boolean': function (x) {
                  if (x)
                      return '!t';
                  return '!f'
              },
              'null': function (x) {
                  return "!n";
              },
              number: function (x) {
                  if (!isFinite(x))
                      return '!n';
                  // strip '+' out of exponent, '-' is ok though
                  return String(x).replace(/\+/,'');
              },
              object: function (x) {
                  if (x) {
                      if (x instanceof Array) {
                          return s.array(x);
                      }

                      var a = ['('], b, f, i, v, ki, ks=[];
                      for (i in x)
                          ks[ks.length] = i;
                      ks.sort();
                      for (ki = 0; ki < ks.length; ki++) {
                          i = ks[ki];
                          v = enc(x[i]);
                          if (typeof v == 'string') {
                              if (b) {
                                  a[a.length] = ',';
                              }
                              a.push(s.string(i), ':', v);
                              b = true;
                          }
                      }
                      a[a.length] = ')';
                      return a.join('');
                  }
                  return '!n';
              },
              string: function (x) {
                  if (x == '')
                      return "''";

                  if (rison.id_ok.test(x))
                      return x;

                  x = x.replace(/(['!])/g, function(a, b) {
                      if (sq[b]) return '!'+b;
                      return b;
                  });
                  return "'" + x + "'";
              },
              undefined: function (x) {
                  // ignore undefined just like JSON
                  // throw new Error("rison can't encode the undefined value");
              }
          };


      /**
       * rison-encode a javascript structure
       *
       *  implemementation based on Douglas Crockford's json.js:
       *    http://json.org/json.js as of 2006-04-28 from json.org
       *
       */
      rison.encode = function (v) {
          return enc(v);
      };

      /**
       * rison-encode a javascript object without surrounding parens
       *
       */
      rison.encode_object = function (v) {
          if (typeof v != 'object' || v === null || v instanceof Array)
              throw new Error("rison.encode_object expects an object argument");
          var r = s[typeof v](v);
          return r.substring(1, r.length-1);
      };

      /**
       * rison-encode a javascript array without surrounding parens
       *
       */
      rison.encode_array = function (v) {
          if (!(v instanceof Array))
              throw new Error("rison.encode_array expects an array argument");
          var r = s[typeof v](v);
          return r.substring(2, r.length-1);
      };

      /**
       * rison-encode and uri-encode a javascript structure
       *
       */
      rison.encode_uri = function (v) {
          return rison.quote(s[typeof v](v));
      };

  })();




  //
  // based on openlaszlo-json and hacked by nix for use in uris.
  //
  // Author: Oliver Steele
  // Copyright: Copyright 2006 Oliver Steele.  All rights reserved.
  // Homepage: http://osteele.com/sources/openlaszlo/json
  // License: MIT License.
  // Version: 1.0


  /**
   * parse a rison string into a javascript structure.
   *
   * this is the simplest decoder entry point.
   *
   *  based on Oliver Steele's OpenLaszlo-JSON
   *     http://osteele.com/sources/openlaszlo/json
   */
  rison.decode = function(r) {
      var errcb = function(e) { throw Error('rison decoder error: ' + e); };
      var p = new rison.parser(errcb);
      return p.parse(r);
  };

  /**
   * parse an o-rison string into a javascript structure.
   *
   * this simply adds parentheses around the string before parsing.
   */
  rison.decode_object = function(r) {
      return rison.decode('('+r+')');
  };

  /**
   * parse an a-rison string into a javascript structure.
   *
   * this simply adds array markup around the string before parsing.
   */
  rison.decode_array = function(r) {
      return rison.decode('!('+r+')');
  };


  /**
   * construct a new parser object for reuse.
   *
   * @constructor
   * @class A Rison parser class.  You should probably
   *        use rison.decode instead.
   * @see rison.decode
   */
  rison.parser = function (errcb) {
      this.errorHandler = errcb;
  };

  /**
   * a string containing acceptable whitespace characters.
   * by default the rison decoder tolerates no whitespace.
   * to accept whitespace set rison.parser.WHITESPACE = " \t\n\r\f";
   */
  rison.parser.WHITESPACE = "";

  // expose this as-is?
  rison.parser.prototype.setOptions = function (options) {
      if (options['errorHandler'])
          this.errorHandler = options.errorHandler;
  };

  /**
   * parse a rison string into a javascript structure.
   */
  rison.parser.prototype.parse = function (str) {
      this.string = str;
      this.index = 0;
      this.message = null;
      var value = this.readValue();
      if (!this.message && this.next())
          value = this.error("unable to parse string as rison: '" + rison.encode(str) + "'");
      if (this.message && this.errorHandler)
          this.errorHandler(this.message, this.index);
      return value;
  };

  rison.parser.prototype.error = function (message) {
      if (typeof(console) != 'undefined')
          console.log('rison parser error: ', message);
      this.message = message;
      return undefined;
  }

  rison.parser.prototype.readValue = function () {
      var c = this.next();
      var fn = c && this.table[c];

      if (fn)
          return fn.apply(this);

      // fell through table, parse as an id

      var s = this.string;
      var i = this.index-1;

      // Regexp.lastIndex may not work right in IE before 5.5?
      // g flag on the regexp is also necessary
      rison.next_id.lastIndex = i;
      var m = rison.next_id.exec(s);

      // console.log('matched id', i, r.lastIndex);

      if (m.length > 0) {
          var id = m[0];
          this.index = i+id.length;
          return id;  // a string
      }

      if (c) return this.error("invalid character: '" + c + "'");
      return this.error("empty expression");
  }

  rison.parser.parse_array = function (parser) {
      var ar = [];
      var c;
      while ((c = parser.next()) != ')') {
          if (!c) return parser.error("unmatched '!('");
          if (ar.length) {
              if (c != ',')
                  parser.error("missing ','");
          } else if (c == ',') {
              return parser.error("extra ','");
          } else
              --parser.index;
          var n = parser.readValue();
          if (typeof n == "undefined") return undefined;
          ar.push(n);
      }
      return ar;
  };

  rison.parser.bangs = {
      t: true,
      f: false,
      n: null,
      '(': rison.parser.parse_array
  }

  rison.parser.prototype.table = {
      '!': function () {
          var s = this.string;
          var c = s.charAt(this.index++);
          if (!c) return this.error('"!" at end of input');
          var x = rison.parser.bangs[c];
          if (typeof(x) == 'function') {
              return x.call(null, this);
          } else if (typeof(x) == 'undefined') {
              return this.error('unknown literal: "!' + c + '"');
          }
          return x;
      },
      '(': function () {
          var o = {};
          var c;
          var count = 0;
          while ((c = this.next()) != ')') {
              if (count) {
                  if (c != ',')
                      this.error("missing ','");
              } else if (c == ',') {
                  return this.error("extra ','");
              } else
                  --this.index;
              var k = this.readValue();
              if (typeof k == "undefined") return undefined;
              if (this.next() != ':') return this.error("missing ':'");
              var v = this.readValue();
              if (typeof v == "undefined") return undefined;
              o[k] = v;
              count++;
          }
          return o;
      },
      "'": function () {
          var s = this.string;
          var i = this.index;
          var start = i;
          var segments = [];
          var c;
          while ((c = s.charAt(i++)) != "'") {
              //if (i == s.length) return this.error('unmatched "\'"');
              if (!c) return this.error('unmatched "\'"');
              if (c == '!') {
                  if (start < i-1)
                      segments.push(s.slice(start, i-1));
                  c = s.charAt(i++);
                  if ("!'".indexOf(c) >= 0) {
                      segments.push(c);
                  } else {
                      return this.error('invalid string escape: "!'+c+'"');
                  }
                  start = i;
              }
          }
          if (start < i-1)
              segments.push(s.slice(start, i-1));
          this.index = i;
          return segments.length == 1 ? segments[0] : segments.join('');
      },
      // Also any digit.  The statement that follows this table
      // definition fills in the digits.
      '-': function () {
          var s = this.string;
          var i = this.index;
          var start = i-1;
          var state = 'int';
          var permittedSigns = '-';
          var transitions = {
              'int+.': 'frac',
              'int+e': 'exp',
              'frac+e': 'exp'
          };
          do {
              var c = s.charAt(i++);
              if (!c) break;
              if ('0' <= c && c <= '9') continue;
              if (permittedSigns.indexOf(c) >= 0) {
                  permittedSigns = '';
                  continue;
              }
              state = transitions[state+'+'+c.toLowerCase()];
              if (state == 'exp') permittedSigns = '-';
          } while (state);
          this.index = --i;
          s = s.slice(start, i)
          if (s == '-') return this.error("invalid number");
          return Number(s);
      }
  };
  // copy table['-'] to each of table[i] | i <- '0'..'9':
  (function (table) {
      for (var i = 0; i <= 9; i++)
          table[String(i)] = table['-'];
  })(rison.parser.prototype.table);

  // return the next non-whitespace character, or undefined
  rison.parser.prototype.next = function () {
      var s = this.string;
      var i = this.index;
      do {
          if (i == s.length) return undefined;
          var c = s.charAt(i++);
      } while (rison.parser.WHITESPACE.indexOf(c) >= 0);
      this.index = i;
      return c;
  };
  return rison;
});
