"use strict";

var singleQuote = "'".charCodeAt(0), doubleQuote = "\"".charCodeAt(0), backslash = "\\".charCodeAt(0), slash = "/".charCodeAt(0), newline = "\n".charCodeAt(0), space = " ".charCodeAt(0), feed = "\f".charCodeAt(0), tab = "\t".charCodeAt(0), cr = "\r".charCodeAt(0), openBracket = "(".charCodeAt(0), closeBracket = ")".charCodeAt(0), openCurly = "{".charCodeAt(0), closeCurly = "}".charCodeAt(0), semicolon = ";".charCodeAt(0), asterisk = "*".charCodeAt(0), colon = ":".charCodeAt(0), at = "@".charCodeAt(0), atEnd = /[ \n\t\r\{\(\)'"\\/]/g, wordEnd = /[ \n\t\r\(\)\{\}:;@!'"\\]|\/(?=\*)/g, badBracket = /.[\\\/\("'\n]/;

module.exports = function (input) {
  var tokens = [];
  var css = input.css.valueOf();

  var code, next, quote, lines, last, content, escape, nextLine, nextOffset, escaped, escapePos, bad;

  var length = css.length;
  var offset = -1;
  var line = 1;
  var pos = 0;

  var unclosed = function (what, end) {
    if (input.safe) {
      css += end;
      next = css.length - 1;
    } else {
      input.error("Unclosed " + what, line, pos - offset);
    }
  };

  while (pos < length) {
    code = css.charCodeAt(pos);

    if (code == newline) {
      offset = pos;
      line += 1;
    }

    switch (code) {
      case newline:
      case space:
      case tab:
      case cr:
      case feed:
        next = pos;
        do {
          next += 1;
          code = css.charCodeAt(next);
          if (code == newline) {
            offset = next;
            line += 1;
          }
        } while (code == space || code == newline || code == tab || code == cr || code == feed);

        tokens.push(["space", css.slice(pos, next)]);
        pos = next - 1;
        break;

      case openCurly:
        tokens.push(["{", "{", line, pos - offset]);
        break;

      case closeCurly:
        tokens.push(["}", "}", line, pos - offset]);
        break;

      case colon:
        tokens.push([":", ":", line, pos - offset]);
        break;

      case semicolon:
        tokens.push([";", ";", line, pos - offset]);
        break;

      case openBracket:
        next = css.indexOf(")", pos + 1);
        content = css.slice(pos, next + 1);

        if (next == -1 || badBracket.test(content)) {
          tokens.push(["(", "(", line, pos - offset]);
        } else {
          tokens.push(["brackets", content, line, pos - offset, line, next - offset]);
          pos = next;
        }

        break;

      case closeBracket:
        tokens.push([")", ")", line, pos - offset]);
        break;

      case singleQuote:
      case doubleQuote:
        quote = code == singleQuote ? "'" : "\"";
        next = pos;
        do {
          escaped = false;
          next = css.indexOf(quote, next + 1);
          if (next == -1) unclosed("quote", quote);
          escapePos = next;
          while (css.charCodeAt(escapePos - 1) == backslash) {
            escapePos -= 1;
            escaped = !escaped;
          }
        } while (escaped);

        tokens.push(["string", css.slice(pos, next + 1), line, pos - offset, line, next - offset]);
        pos = next;
        break;

      case at:
        atEnd.lastIndex = pos + 1;
        atEnd.test(css);
        if (atEnd.lastIndex === 0) {
          next = css.length - 1;
        } else {
          next = atEnd.lastIndex - 2;
        }
        tokens.push(["at-word", css.slice(pos, next + 1), line, pos - offset, line, next - offset]);
        pos = next;
        break;

      case backslash:
        next = pos;
        escape = true;
        while (css.charCodeAt(next + 1) == backslash) {
          next += 1;
          escape = !escape;
        }
        code = css.charCodeAt(next + 1);
        if (escape && (code != slash && code != space && code != newline && code != tab && code != cr && code != feed)) {
          next += 1;
        }
        tokens.push(["word", css.slice(pos, next + 1), line, pos - offset, line, next - offset]);
        pos = next;
        break;

      default:
        if (code == slash && css.charCodeAt(pos + 1) == asterisk) {
          next = css.indexOf("*/", pos + 2) + 1;
          if (next === 0) unclosed("comment", "*/");

          content = css.slice(pos, next + 1);
          lines = content.split("\n");
          last = lines.length - 1;

          if (last > 0) {
            nextLine = line + last;
            nextOffset = next - lines[last].length;
          } else {
            nextLine = line;
            nextOffset = offset;
          }

          tokens.push(["comment", content, line, pos - offset, nextLine, next - nextOffset]);

          offset = nextOffset;
          line = nextLine;
          pos = next;
        } else {
          wordEnd.lastIndex = pos + 1;
          wordEnd.test(css);
          if (wordEnd.lastIndex === 0) {
            next = css.length - 1;
          } else {
            next = wordEnd.lastIndex - 2;
          }

          tokens.push(["word", css.slice(pos, next + 1), line, pos - offset, line, next - offset]);
          pos = next;
        }

        break;
    }

    pos++;
  }

  return tokens;
};