class YAMLError extends Error {
  constructor(name, pos, code, message) {
    super();
    this.name = name;
    this.code = code;
    this.message = message;
    this.pos = pos;
  }
}
class YAMLParseError extends YAMLError {
  constructor(pos, code, message) {
    super('YAMLParseError', pos, code, message);
  }
}
class YAMLWarning extends YAMLError {
  constructor(pos, code, message) {
    super('YAMLWarning', pos, code, message);
  }
}
const prettifyError = (src, lc) => error => {
  if (error.pos[0] === -1) return;
  error.linePos = error.pos.map(pos => lc.linePos(pos));
  const {
    line,
    col
  } = error.linePos[0];
  error.message += ` at line ${line}, column ${col}`;
  let ci = col - 1;
  let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, '');
  // Trim to max 80 chars, keeping col position near the middle
  if (ci >= 60 && lineStr.length > 80) {
    const trimStart = Math.min(ci - 39, lineStr.length - 79);
    lineStr = '…' + lineStr.substring(trimStart);
    ci -= trimStart - 1;
  }
  if (lineStr.length > 80) lineStr = lineStr.substring(0, 79) + '…';
  // Include previous line in context if pointing at line start
  if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) {
    // Regexp won't match if start is trimmed
    let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]);
    if (prev.length > 80) prev = prev.substring(0, 79) + '…\n';
    lineStr = prev + lineStr;
  }
  if (/[^ ]/.test(lineStr)) {
    let count = 1;
    const end = error.linePos[1];
    if (end && end.line === line && end.col > col) {
      count = Math.max(1, Math.min(end.col - col, 80 - ci));
    }
    const pointer = ' '.repeat(ci) + '^'.repeat(count);
    error.message += `:\n\n${lineStr}\n${pointer}\n`;
  }
};

export { YAMLError, YAMLParseError, YAMLWarning, prettifyError };
