import g from "./declaration.js";
import b from "./tokenize.js";
import y from "./comment.js";
import F from "./at-rule.js";
import x from "./root.js";
import z from "./rule.js";
let C = g, S = b, P = y, A = F, T = x, m = z;
const w = {
  empty: !0,
  space: !0
};
function O(p) {
  for (let e = p.length - 1; e >= 0; e--) {
    let t = p[e], i = t[3] || t[2];
    if (i)
      return i;
  }
}
class v {
  constructor(e) {
    this.input = e, this.root = new T(), this.current = this.root, this.spaces = "", this.semicolon = !1, this.customProperty = !1, this.createTokenizer(), this.root.source = { input: e, start: { offset: 0, line: 1, column: 1 } };
  }
  createTokenizer() {
    this.tokenizer = S(this.input);
  }
  parse() {
    let e;
    for (; !this.tokenizer.endOfFile(); )
      switch (e = this.tokenizer.nextToken(), e[0]) {
        case "space":
          this.spaces += e[1];
          break;
        case ";":
          this.freeSemicolon(e);
          break;
        case "}":
          this.end(e);
          break;
        case "comment":
          this.comment(e);
          break;
        case "at-word":
          this.atrule(e);
          break;
        case "{":
          this.emptyRule(e);
          break;
        default:
          this.other(e);
          break;
      }
    this.endFile();
  }
  comment(e) {
    let t = new P();
    this.init(t, e[2]), t.source.end = this.getPosition(e[3] || e[2]);
    let i = e[1].slice(2, -2);
    if (/^\s*$/.test(i))
      t.text = "", t.raws.left = i, t.raws.right = "";
    else {
      let s = i.match(/^(\s*)([^]*\S)(\s*)$/);
      t.text = s[2], t.raws.left = s[1], t.raws.right = s[3];
    }
  }
  emptyRule(e) {
    let t = new m();
    this.init(t, e[2]), t.selector = "", t.raws.between = "", this.current = t;
  }
  other(e) {
    let t = !1, i = null, s = !1, r = null, o = [], h = e[1].startsWith("--"), n = [], l = e;
    for (; l; ) {
      if (i = l[0], n.push(l), i === "(" || i === "[")
        r || (r = l), o.push(i === "(" ? ")" : "]");
      else if (h && s && i === "{")
        r || (r = l), o.push("}");
      else if (o.length === 0)
        if (i === ";")
          if (s) {
            this.decl(n, h);
            return;
          } else
            break;
        else if (i === "{") {
          this.rule(n);
          return;
        } else if (i === "}") {
          this.tokenizer.back(n.pop()), t = !0;
          break;
        } else
          i === ":" && (s = !0);
      else
        i === o[o.length - 1] && (o.pop(), o.length === 0 && (r = null));
      l = this.tokenizer.nextToken();
    }
    if (this.tokenizer.endOfFile() && (t = !0), o.length > 0 && this.unclosedBracket(r), t && s) {
      if (!h)
        for (; n.length && (l = n[n.length - 1][0], !(l !== "space" && l !== "comment")); )
          this.tokenizer.back(n.pop());
      this.decl(n, h);
    } else
      this.unknownWord(n);
  }
  rule(e) {
    e.pop();
    let t = new m();
    this.init(t, e[0][2]), t.raws.between = this.spacesAndCommentsFromEnd(e), this.raw(t, "selector", e), this.current = t;
  }
  decl(e, t) {
    let i = new C();
    this.init(i, e[0][2]);
    let s = e[e.length - 1];
    for (s[0] === ";" && (this.semicolon = !0, e.pop()), i.source.end = this.getPosition(
      s[3] || s[2] || O(e)
    ); e[0][0] !== "word"; )
      e.length === 1 && this.unknownWord(e), i.raws.before += e.shift()[1];
    for (i.source.start = this.getPosition(e[0][2]), i.prop = ""; e.length; ) {
      let l = e[0][0];
      if (l === ":" || l === "space" || l === "comment")
        break;
      i.prop += e.shift()[1];
    }
    i.raws.between = "";
    let r;
    for (; e.length; )
      if (r = e.shift(), r[0] === ":") {
        i.raws.between += r[1];
        break;
      } else
        r[0] === "word" && /\w/.test(r[1]) && this.unknownWord([r]), i.raws.between += r[1];
    (i.prop[0] === "_" || i.prop[0] === "*") && (i.raws.before += i.prop[0], i.prop = i.prop.slice(1));
    let o = [], h;
    for (; e.length && (h = e[0][0], !(h !== "space" && h !== "comment")); )
      o.push(e.shift());
    this.precheckMissedSemicolon(e);
    for (let l = e.length - 1; l >= 0; l--) {
      if (r = e[l], r[1].toLowerCase() === "!important") {
        i.important = !0;
        let c = this.stringFrom(e, l);
        c = this.spacesFromEnd(e) + c, c !== " !important" && (i.raws.important = c);
        break;
      } else if (r[1].toLowerCase() === "important") {
        let c = e.slice(0), f = "";
        for (let a = l; a > 0; a--) {
          let u = c[a][0];
          if (f.trim().indexOf("!") === 0 && u !== "space")
            break;
          f = c.pop()[1] + f;
        }
        f.trim().indexOf("!") === 0 && (i.important = !0, i.raws.important = f, e = c);
      }
      if (r[0] !== "space" && r[0] !== "comment")
        break;
    }
    e.some((l) => l[0] !== "space" && l[0] !== "comment") && (i.raws.between += o.map((l) => l[1]).join(""), o = []), this.raw(i, "value", o.concat(e), t), i.value.includes(":") && !t && this.checkMissedSemicolon(e);
  }
  atrule(e) {
    let t = new A();
    t.name = e[1].slice(1), t.name === "" && this.unnamedAtrule(t, e), this.init(t, e[2]);
    let i, s, r, o = !1, h = !1, n = [], l = [];
    for (; !this.tokenizer.endOfFile(); ) {
      if (e = this.tokenizer.nextToken(), i = e[0], i === "(" || i === "[" ? l.push(i === "(" ? ")" : "]") : i === "{" && l.length > 0 ? l.push("}") : i === l[l.length - 1] && l.pop(), l.length === 0)
        if (i === ";") {
          t.source.end = this.getPosition(e[2]), this.semicolon = !0;
          break;
        } else if (i === "{") {
          h = !0;
          break;
        } else if (i === "}") {
          if (n.length > 0) {
            for (r = n.length - 1, s = n[r]; s && s[0] === "space"; )
              s = n[--r];
            s && (t.source.end = this.getPosition(s[3] || s[2]));
          }
          this.end(e);
          break;
        } else
          n.push(e);
      else
        n.push(e);
      if (this.tokenizer.endOfFile()) {
        o = !0;
        break;
      }
    }
    t.raws.between = this.spacesAndCommentsFromEnd(n), n.length ? (t.raws.afterName = this.spacesAndCommentsFromStart(n), this.raw(t, "params", n), o && (e = n[n.length - 1], t.source.end = this.getPosition(e[3] || e[2]), this.spaces = t.raws.between, t.raws.between = "")) : (t.raws.afterName = "", t.params = ""), h && (t.nodes = [], this.current = t);
  }
  end(e) {
    this.current.nodes && this.current.nodes.length && (this.current.raws.semicolon = this.semicolon), this.semicolon = !1, this.current.raws.after = (this.current.raws.after || "") + this.spaces, this.spaces = "", this.current.parent ? (this.current.source.end = this.getPosition(e[2]), this.current = this.current.parent) : this.unexpectedClose(e);
  }
  endFile() {
    this.current.parent && this.unclosedBlock(), this.current.nodes && this.current.nodes.length && (this.current.raws.semicolon = this.semicolon), this.current.raws.after = (this.current.raws.after || "") + this.spaces;
  }
  freeSemicolon(e) {
    if (this.spaces += e[1], this.current.nodes) {
      let t = this.current.nodes[this.current.nodes.length - 1];
      t && t.type === "rule" && !t.raws.ownSemicolon && (t.raws.ownSemicolon = this.spaces, this.spaces = "");
    }
  }
  getPosition(e) {
    let t = this.input.fromOffset(e);
    return {
      offset: e,
      line: t.line,
      column: t.col
    };
  }
  init(e, t) {
    this.current.push(e), e.source = {
      start: this.getPosition(t),
      input: this.input
    }, e.raws.before = this.spaces, this.spaces = "", e.type !== "comment" && (this.semicolon = !1);
  }
  raw(e, t, i, s) {
    let r, o, h = i.length, n = "", l = !0, c, f;
    for (let a = 0; a < h; a += 1)
      r = i[a], o = r[0], o === "space" && a === h - 1 && !s ? l = !1 : o === "comment" ? (f = i[a - 1] ? i[a - 1][0] : "empty", c = i[a + 1] ? i[a + 1][0] : "empty", !w[f] && !w[c] ? n.slice(-1) === "," ? l = !1 : n += r[1] : l = !1) : n += r[1];
    if (!l) {
      let a = i.reduce((u, d) => u + d[1], "");
      e.raws[t] = { value: n, raw: a };
    }
    e[t] = n;
  }
  spacesAndCommentsFromEnd(e) {
    let t, i = "";
    for (; e.length && (t = e[e.length - 1][0], !(t !== "space" && t !== "comment")); )
      i = e.pop()[1] + i;
    return i;
  }
  spacesAndCommentsFromStart(e) {
    let t, i = "";
    for (; e.length && (t = e[0][0], !(t !== "space" && t !== "comment")); )
      i += e.shift()[1];
    return i;
  }
  spacesFromEnd(e) {
    let t, i = "";
    for (; e.length && (t = e[e.length - 1][0], t === "space"); )
      i = e.pop()[1] + i;
    return i;
  }
  stringFrom(e, t) {
    let i = "";
    for (let s = t; s < e.length; s++)
      i += e[s][1];
    return e.splice(t, e.length - t), i;
  }
  colon(e) {
    let t = 0, i, s, r;
    for (let [o, h] of e.entries()) {
      if (i = h, s = i[0], s === "(" && (t += 1), s === ")" && (t -= 1), t === 0 && s === ":")
        if (!r)
          this.doubleColon(i);
        else {
          if (r[0] === "word" && r[1] === "progid")
            continue;
          return o;
        }
      r = i;
    }
    return !1;
  }
  unclosedBracket(e) {
    throw this.input.error(
      "Unclosed bracket",
      { offset: e[2] },
      { offset: e[2] + 1 }
    );
  }
  unknownWord(e) {
    throw this.input.error(
      "Unknown word",
      { offset: e[0][2] },
      { offset: e[0][2] + e[0][1].length }
    );
  }
  unexpectedClose(e) {
    throw this.input.error(
      "Unexpected }",
      { offset: e[2] },
      { offset: e[2] + 1 }
    );
  }
  unclosedBlock() {
    let e = this.current.source.start;
    throw this.input.error("Unclosed block", e.line, e.column);
  }
  doubleColon(e) {
    throw this.input.error(
      "Double colon",
      { offset: e[2] },
      { offset: e[2] + e[1].length }
    );
  }
  unnamedAtrule(e, t) {
    throw this.input.error(
      "At-rule without name",
      { offset: t[2] },
      { offset: t[2] + t[1].length }
    );
  }
  precheckMissedSemicolon() {
  }
  checkMissedSemicolon(e) {
    let t = this.colon(e);
    if (t === !1)
      return;
    let i = 0, s;
    for (let r = t - 1; r >= 0 && (s = e[r], !(s[0] !== "space" && (i += 1, i === 2))); r--)
      ;
    throw this.input.error(
      "Missed semicolon",
      s[0] === "word" ? s[3] + 1 : s[2]
    );
  }
}
var U = v;
export {
  U as default
};
//# sourceMappingURL=parser.js.map
