function resolveProps(tokens, {
  flow,
  indicator,
  next,
  offset,
  onError,
  startOnNewline
}) {
  let spaceBefore = false;
  let atNewline = startOnNewline;
  let hasSpace = startOnNewline;
  let comment = '';
  let commentSep = '';
  let hasNewline = false;
  let hasNewlineAfterProp = false;
  let reqSpace = false;
  let anchor = null;
  let tag = null;
  let comma = null;
  let found = null;
  let start = null;
  for (const token of tokens) {
    if (reqSpace) {
      if (token.type !== 'space' && token.type !== 'newline' && token.type !== 'comma') onError(token.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
      reqSpace = false;
    }
    switch (token.type) {
      case 'space':
        // At the doc level, tabs at line start may be parsed
        // as leading white space rather than indentation.
        // In a flow collection, only the parser handles indent.
        if (!flow && atNewline && indicator !== 'doc-start' && token.source[0] === '\t') onError(token, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
        hasSpace = true;
        break;
      case 'comment':
        {
          if (!hasSpace) onError(token, 'MISSING_CHAR', 'Comments must be separated from other tokens by white space characters');
          const cb = token.source.substring(1) || ' ';
          if (!comment) comment = cb;else comment += commentSep + cb;
          commentSep = '';
          atNewline = false;
          break;
        }
      case 'newline':
        if (atNewline) {
          if (comment) comment += token.source;else spaceBefore = true;
        } else commentSep += token.source;
        atNewline = true;
        hasNewline = true;
        if (anchor || tag) hasNewlineAfterProp = true;
        hasSpace = true;
        break;
      case 'anchor':
        if (anchor) onError(token, 'MULTIPLE_ANCHORS', 'A node can have at most one anchor');
        if (token.source.endsWith(':')) onError(token.offset + token.source.length - 1, 'BAD_ALIAS', 'Anchor ending in : is ambiguous', true);
        anchor = token;
        if (start === null) start = token.offset;
        atNewline = false;
        hasSpace = false;
        reqSpace = true;
        break;
      case 'tag':
        {
          if (tag) onError(token, 'MULTIPLE_TAGS', 'A node can have at most one tag');
          tag = token;
          if (start === null) start = token.offset;
          atNewline = false;
          hasSpace = false;
          reqSpace = true;
          break;
        }
      case indicator:
        // Could here handle preceding comments differently
        if (anchor || tag) onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`);
        if (found) onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow ?? 'collection'}`);
        found = token;
        atNewline = false;
        hasSpace = false;
        break;
      case 'comma':
        if (flow) {
          if (comma) onError(token, 'UNEXPECTED_TOKEN', `Unexpected , in ${flow}`);
          comma = token;
          atNewline = false;
          hasSpace = false;
          break;
        }
      // else fallthrough
      default:
        onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.type} token`);
        atNewline = false;
        hasSpace = false;
    }
  }
  const last = tokens[tokens.length - 1];
  const end = last ? last.offset + last.source.length : offset;
  if (reqSpace && next && next.type !== 'space' && next.type !== 'newline' && next.type !== 'comma' && (next.type !== 'scalar' || next.source !== '')) onError(next.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
  return {
    comma,
    found,
    spaceBefore,
    comment,
    hasNewline,
    hasNewlineAfterProp,
    anchor,
    tag,
    end,
    start: start ?? end
  };
}

export { resolveProps };
