'use strict';

var common = require('../common'); var Type = require('../type');

function isHexCode© {

return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) ||
       ((0x41/* A */ <= c) && (c <= 0x46/* F */)) ||
       ((0x61/* a */ <= c) && (c <= 0x66/* f */));

}

function isOctCode© {

return ((0x30/* 0 */ <= c) && (c <= 0x37/* 7 */));

}

function isDecCode© {

return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */));

}

function resolveYamlInteger(data) {

if (data === null) return false;

var max = data.length,
    index = 0,
    hasDigits = false,
    ch;

if (!max) return false;

ch = data[index];

// sign
if (ch === '-' || ch === '+') {
  ch = data[++index];
}

if (ch === '0') {
  // 0
  if (index + 1 === max) return true;
  ch = data[++index];

  // base 2, base 8, base 16

  if (ch === 'b') {
    // base 2
    index++;

    for (; index < max; index++) {
      ch = data[index];
      if (ch === '_') continue;
      if (ch !== '0' && ch !== '1') return false;
      hasDigits = true;
    }
    return hasDigits && ch !== '_';
  }

  if (ch === 'x') {
    // base 16
    index++;

    for (; index < max; index++) {
      ch = data[index];
      if (ch === '_') continue;
      if (!isHexCode(data.charCodeAt(index))) return false;
      hasDigits = true;
    }
    return hasDigits && ch !== '_';
  }

  // base 8
  for (; index < max; index++) {
    ch = data[index];
    if (ch === '_') continue;
    if (!isOctCode(data.charCodeAt(index))) return false;
    hasDigits = true;
  }
  return hasDigits && ch !== '_';
}

// base 10 (except 0) or base 60

// value should not start with `_`;
if (ch === '_') return false;

for (; index < max; index++) {
  ch = data[index];
  if (ch === '_') continue;
  if (ch === ':') break;
  if (!isDecCode(data.charCodeAt(index))) {
    return false;
  }
  hasDigits = true;
}

// Should have digits and should not end with `_`
if (!hasDigits || ch === '_') return false;

// if !base60 - done;
if (ch !== ':') return true;

// base60 almost not used, no needs to optimize
return /^(:[0-5]?[0-9])+$/.test(data.slice(index));

}

function constructYamlInteger(data) {

var value = data, sign = 1, ch, base, digits = [];

if (value.indexOf('_') !== -1) {
  value = value.replace(/_/g, '');
}

ch = value[0];

if (ch === '-' || ch === '+') {
  if (ch === '-') sign = -1;
  value = value.slice(1);
  ch = value[0];
}

if (value === '0') return 0;

if (ch === '0') {
  if (value[1] === 'b') return sign * parseInt(value.slice(2), 2);
  if (value[1] === 'x') return sign * parseInt(value, 16);
  return sign * parseInt(value, 8);
}

if (value.indexOf(':') !== -1) {
  value.split(':').forEach(function (v) {
    digits.unshift(parseInt(v, 10));
  });

  value = 0;
  base = 1;

  digits.forEach(function (d) {
    value += (d * base);
    base *= 60;
  });

  return sign * value;

}

return sign * parseInt(value, 10);

}

function isInteger(object) {

return (Object.prototype.toString.call(object)) === '[object Number]' &&
       (object % 1 === 0 && !common.isNegativeZero(object));

}

module.exports = new Type('tag:yaml.org,2002:int', {

kind: 'scalar',
resolve: resolveYamlInteger,
construct: constructYamlInteger,
predicate: isInteger,
represent: {
  binary:      function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1); },
  octal:       function (obj) { return obj >= 0 ? '0'  + obj.toString(8) : '-0'  + obj.toString(8).slice(1); },
  decimal:     function (obj) { return obj.toString(10); },
  /* eslint-disable max-len */
  hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() :  '-0x' + obj.toString(16).toUpperCase().slice(1); }
},
defaultStyle: 'decimal',
styleAliases: {
  binary:      [ 2,  'bin' ],
  octal:       [ 8,  'oct' ],
  decimal:     [ 10, 'dec' ],
  hexadecimal: [ 16, 'hex' ]
}

});