exports.v8LocationToInspectorLocation = function(v8loc) {
  return {
    scriptId: v8loc.script_id.toString(),
    lineNumber: v8loc.line,
    columnNumber: v8loc.column
  };
};


// Conversions between v8 file paths and node-inspector urls
// Kind      Path            Url
// UNIX      /dir/app.js     file:///dir/app.js
// Windows   c:\dir\app.js   file:///C:/dir/app.js
// UNC       \\SHARE\app.js  file://SHARE/app.js

exports.v8NameToInspectorUrl = function(v8name) {
  if (!v8name || v8name === 'repl') {
    // Call to `evaluate` from user-land creates a new script with undefined URL.
    // REPL has null main script file and calls `evaluate` with `repl`
    // as the file name.
    //
    // When we send an empty string as URL, front-end opens the source
    // as VM-only script (named "[VM] {script-id}").
    //
    // The empty name of the main script file is displayed as "(program)".
    return '';
  }

  if (/^\//.test(v8name)) {
    return 'file://' + v8name;
  } else if (/^[a-zA-Z]:\\/.test(v8name)) {
    return 'file:///' + v8name.replace(/\\/g, '/');
  } else if (/^\\\\/.test(v8name)) {
    return 'file://' + v8name.substring(2).replace(/\\/g, '/');
  }

  return v8name;
};

exports.inspectorUrlToV8Name = function(url, normalize) {
  var path = normalize(url).replace(/^file:\/\//, '');
  if (/^\/[a-zA-Z]:\//.test(path))
    return path.substring(1).replace(/\//g, '\\'); // Windows disk path
  if (/^\//.test(path))
    return path; // UNIX-style
  if (/^file:\/\//.test(url))
    return '\\\\' + path.replace(/\//g, '\\'); // Windows UNC path

  return url;
};

exports.v8ScopeTypeToString = function(v8ScopeType) {
  switch (v8ScopeType) {
    case 0:
      return 'global';
    case 1:
      return 'local';
    case 2:
      return 'with';
    case 3:
      return 'closure';
    case 4:
      return 'catch';
    default:
      return 'unknown';
  }
};

exports.v8RefToInspectorObject = function(ref) {
  var desc = '',
      type = ref.type,
      subtype,
      size,
      name,
      objectId,
      inspectorResult;

  switch (type) {
    case 'object':
      name = /#<(\w+)>/.exec(ref.text);
      if (name && name.length > 1) {
        desc = name[1];
        if (desc === 'Array' || desc === 'Buffer') {
          size = ref.properties.filter(function(p) { return /^\d+$/.test(p.name);}).length;
          desc += '[' + size + ']';
          subtype = 'array';
        }
      } else if (ref.className === 'Date') {
        desc = new Date(ref.value || NaN).toString();
        subtype = 'date';
      } else {
        desc = ref.className || 'Object';
      }
      break;
    case 'regexp':
      type = 'object';
      subtype = 'regexp';
      desc = ref.text || '';
      /*
        We need to collect RegExp flags and append they to description,
        or open issue in NodeJS same as 'RegExp text serialized without flags'
      */
      break;
    case 'function':
      desc = ref.text || 'function()';
      break;
    case 'error':
      type = 'object';
      desc = ref.text || 'Error';
      break;
    default:
      desc = ref.text || '';
      break;
  }
  if (desc.length > 100) {
    desc = desc.substring(0, 100) + '\u2026';
  }

  objectId = ref.handle;
  if (objectId === undefined)
    objectId = ref.ref;

  inspectorResult = {
    type: type,
    subtype: subtype,
    objectId: String(objectId),
    className: ref.className,
    description: desc
  };

  return inspectorResult;
};

exports.v8ObjectToInspectorProperties = function(obj, refs, options) {
  var proto = obj.protoObject,
      props = obj.properties || [],
      ownProperties = options.ownProperties,
      accessorPropertiesOnly = options.accessorPropertiesOnly;

  props = props.map(function(prop) {
    var ref = refs[prop.ref];
    var inspectorProperty = {
        name: String(prop.name),
        writable: !(prop.attributes & 1 << 0),
        enumerable: !(prop.attributes & 1 << 1),
        configurable: !(prop.attributes & 1 << 2),
        value: exports.v8ResultToInspectorResult(ref)
      };
    return inspectorProperty;
  });

  if (ownProperties && proto) {
    proto = refs[proto.ref];
    if (proto.type !== 'undefined') {
      props.push({
        name: '__proto__',
        value: exports.v8RefToInspectorObject(proto),
        writable: true,
        configurable: true,
        enumerable: false,
        isOwn: true
      });
    }
  }

  props = props.filter(function(prop) {
  /*
    Node.js does not return get/set property descriptors now (v0.11.11),
      therefore we can't fully implement 'accessorPropertiesOnly'.
    See https://github.com/joyent/node/issues/7139
  */
    var isAccessorProperty = ('get' in prop || 'set' in prop);

    return accessorPropertiesOnly ? isAccessorProperty : !isAccessorProperty;
  });

  return props;
};

exports.v8ErrorToInspectorError = function(message) {
  var nameMatch = /^([^:]+):/.exec(message);

  return {
    type: 'object',
    objectId: 'ERROR',
    className: nameMatch ? nameMatch[1] : 'Error',
    description: message
  };
};

exports.v8ResultToInspectorResult = function(result) {
  var subtype,
      inspectorResult;
  if (['object', 'function', 'regexp', 'error'].indexOf(result.type) > -1) {
    return exports.v8RefToInspectorObject(result);
  }

  if (result.type == 'null') {
    // workaround for the problem with front-end's setVariableValue
    // implementation not preserving null type
    result.value = null;
    subtype = 'null';
  }

  inspectorResult = {
    type: result.type,
    subtype: subtype,
    value: result.value,
    description: String(result.value)
  };

  return inspectorResult;
};

exports.inspectorValueToV8Value = function(value) {
  if (value.value === undefined && value.objectId === undefined)
    return { type: 'undefined' };
  if (value.objectId) {
    return { handle: Number(value.objectId) };
  }
  return value;
};

exports.v8FunctionLookupToFunctionDetails = function(handleData) {
  return {
    details: {
      location: {
        scriptId: String(handleData.scriptId),
        lineNumber: handleData.line,
        columnNumber: handleData.column
      },
      name: handleData.name || handleData.inferredName,

      // There is a list of scope ids in responseBody.scopes, but not scope
      // details :( // We need to issue `scopes` request to fetch scopes
      // details, but we don't have frame number where the function was defined.
      // Let's leave the scopeChain empty for now.
      scopeChain: []
    }
  };
};

exports.v8ScriptIdToInspectorId = function(scriptId) {
  return String(scriptId);
};

exports.inspectorScriptIdToV8Id = function(scriptId) {
  return Number(scriptId);
};
