import {style, Pos} from "../model"

import {TransformResult, Transform} from "./transform"
import {defineStep, Step} from "./step"
import {copyInline, copyStructure, forSpansBetween} from "./tree"

defineStep("addStyle", {
  apply(doc, step) {
    return new TransformResult(copyStructure(doc, step.from, step.to, (node, from, to) => {
      if (node.type.plainText) return node
      return copyInline(node, from, to, node => {
        return node.styled(style.add(node.styles, step.param))
      })
    }))
  },
  invert(step, _oldDoc, map) {
    return new Step("removeStyle", step.from, map.map(step.to).pos, null, step.param)
  }
})


Transform.prototype.addStyle = function(from, to, st) {
  let removed = [], added = [], removing = null, adding = null
  forSpansBetween(this.doc, from, to, (span, path, start, end) => {
    if (style.contains(span.styles, st)) {
      adding = removing = null
    } else {
      path = path.slice()
      let rm = style.containsType(span.styles, st.type)
      if (rm) {
        if (removing && style.same(removing.param, rm)) {
          removing.to = new Pos(path, end)
        } else {
          removing = new Step("removeStyle", new Pos(path, start), new Pos(path, end), null, rm)
          removed.push(removing)
        }
      } else if (removing) {
        removing = null
      }
      if (adding) {
        adding.to = new Pos(path, end)
      } else {
        adding = new Step("addStyle", new Pos(path, start), new Pos(path, end), null, st)
        added.push(adding)
      }
    }
  })
  removed.forEach(s => this.step(s))
  added.forEach(s => this.step(s))
  return this
}

defineStep("removeStyle", {
  apply(doc, step) {
    return new TransformResult(copyStructure(doc, step.from, step.to, (node, from, to) => {
      return copyInline(node, from, to, node => {
        return node.styled(style.remove(node.styles, step.param))
      })
    }))
  },
  invert(step, _oldDoc, map) {
    return new Step("addStyle", step.from, map.map(step.to).pos, null, step.param)
  }
})

Transform.prototype.removeStyle = function(from, to, st = null) {
  let matched = [], step = 0
  forSpansBetween(this.doc, from, to, (span, path, start, end) => {
    step++
    let toRemove = null
    if (typeof st == "string") {
      let found = style.containsType(span.styles, st)
      if (found) toRemove = [found]
    } else if (st) {
      if (style.contains(span.styles, st)) toRemove = [st]
    } else {
      toRemove = span.styles
    }
    if (toRemove && toRemove.length) {
      path = path.slice()
      for (let i = 0; i < toRemove.length; i++) {
        let rm = toRemove[i], found
        for (let j = 0; j < matched.length; j++) {
          let m = matched[j]
          if (m.step == step - 1 && style.same(rm, matched[j].style)) found = m
        }
        if (found) {
          found.to = new Pos(path, end)
          found.step = step
        } else {
          matched.push({style: rm, from: new Pos(path, start), to: new Pos(path, end), step: step})
        }
      }
    }
  })
  matched.forEach(m => this.step("removeStyle", m.from, m.to, null, m.style))
  return this
}

Transform.prototype.clearMarkup = function(from, to) {
  let steps = []
  forSpansBetween(this.doc, from, to, (span, path, start, end) => {
    if (!span.isText) {
      path = path.slice()
      let from = new Pos(path, start)
      steps.unshift(new Step("replace", from, new Pos(path, end), from))
    }
  })
  this.removeStyle(from.to)
  steps.forEach(s => this.step(s))
  return this
}

@@ -5,18 +5,26 @@
 rt %7B
-style, Pos
+Pos, containsStyle
 %7D fr
@@ -464,39 +464,37 @@
 d(st
-yle.add(node.styles, step.param
+ep.param.addToSet(node.styles
 ))%0A 
@@ -833,36 +833,32 @@
 %7B%0A    if (st
-yle.contains
+.isInSet
 (span.styles
@@ -857,20 +857,16 @@
 n.styles
-, st
 )) %7B%0A   
@@ -947,30 +947,24 @@
 et rm = 
-style.
 contains
 Type(spa
@@ -947,35 +947,36 @@
 et rm = contains
-Typ
+Styl
 e(span.styles, s
@@ -1023,27 +1023,16 @@
 ving && 
-style.same(
 removing
@@ -1037,18 +1037,20 @@
 ng.param
-, 
+.eq(
 rm)) %7B%0A 
@@ -1832,42 +1832,42 @@
 d(st
-yle.remove(node.styles, step.param
+ep.param.removeFromSet(node.styles
 ))%0A 
@@ -2258,30 +2258,24 @@
 found = 
-style.
 contains
 Type(spa
@@ -2270,11 +2270,12 @@
 ains
-Typ
+Styl
 e(sp
@@ -2362,20 +2362,16 @@
  (st
-yle.contains
+.isInSet
 (spa
@@ -2378,20 +2378,16 @@
 n.styles
-, st
 )) toRem
@@ -2716,23 +2716,14 @@
  && 
-style.same(rm, 
+rm.eq(
 matc

Make styles part of the schema