// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// Tree data structure for storing marks at (row, col) positions and updating
// them to arbitrary text changes. Derivative work of kbtree in klib, whose
// copyright notice is reproduced below. Also inspired by the design of the
// marker tree data structure of the Atom editor, regarding efficient updates
// to text changes.
//
// Marks are inserted using marktree_put. Text changes are processed using
// marktree_splice. All read and delete operations use the iterator.
// use marktree_itr_get to put an iterator at a given position or
// marktree_lookup to lookup a mark by its id (iterator optional in this case).
// Use marktree_itr_current and marktree_itr_next/prev to read marks in a loop.
// marktree_del_itr deletes the current mark of the iterator and implicitly
// moves the iterator to the next mark.
//
// Work is ongoing to fully support ranges (mark pairs).

// Copyright notice for kbtree (included in heavily modified form):
//
// Copyright 1997-1999, 2001, John-Mark Gurney.
//           2008-2009, Attractive Chaos <attractor@live.co.uk>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// Changes done by by the neovim project follow the Apache v2 license available
// at the repo root.

#include <assert.h>

#include "nvim/garray.h"
#include "nvim/lib/kvec.h"
#include "nvim/marktree.h"

#define T MT_BRANCH_FACTOR
#define ILEN (sizeof(mtnode_t)+(2 * T) * sizeof(void *))

#define ID_INCR (((uint64_t)1) << 2)

#define rawkey(itr) (itr->node->key[itr->i])

static bool pos_leq(mtpos_t a, mtpos_t b)
{
  return a.row < b.row || (a.row == b.row && a.col <= b.col);
}

static void relative(mtpos_t base, mtpos_t *val)
{
  assert(pos_leq(base, *val));
  if (val->row == base.row) {
    val->row = 0;
    val->col -= base.col;
  } else {
    val->row -= base.row;
  }
}

static void unrelative(mtpos_t base, mtpos_t *val)
{
  if (val->row == 0) {
    val->row = base.row;
    val->col += base.col;
  } else {
    val->row += base.row;
  }
}

static void compose(mtpos_t *base, mtpos_t val)
{
  if (val.row == 0) {
    base->col += val.col;
  } else {
    base->row += val.row;
    base->col = val.col;
  }
}

#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "marktree.c.generated.h"
#endif

#define mt_generic_cmp(a, b) (((b) < (a)) - ((a) < (b)))
static int key_cmp(mtkey_t a, mtkey_t b)
{
  int cmp = mt_generic_cmp(a.pos.row, b.pos.row);
  if (cmp != 0) {
    return cmp;
  }
  cmp = mt_generic_cmp(a.pos.col, b.pos.col);
  if (cmp != 0) {
    return cmp;
  }
  // NB: keeping the events at the same pos sorted by id is actually not
  // necessary only make sure that START is before END etc.
  return mt_generic_cmp(a.flags, b.flags);
}

static inline int marktree_getp_aux(const mtnode_t *x, mtkey_t k, int *r)
{
  int tr, *rr, begin = 0, end = x->n;
  if (x->n == 0) {
    return -1;
  }
  rr = r? r : &tr;
  while (begin < end) {
    int mid = (begin + end) >> 1;
    if (key_cmp(x->key[mid], k) < 0) {
      begin = mid + 1;
    } else {
      end = mid;
    }
  }
  if (begin == x->n) {
    *rr = 1; return x->n - 1;
  }
  if ((*rr = key_cmp(k, x->key[begin])) < 0) {
    begin--;
  }
  return begin;
}

static inline void refkey(MarkTree *b, mtnode_t *x, int i)
{
  pmap_put(uint64_t)(b->id2node, mt_lookup_key(x->key[i]), x);
}

// put functions

// x must be an internal node, which is not full
// x->ptr[i] should be a full node, i e x->ptr[i]->n == 2*T-1
static inline void split_node(MarkTree *b, mtnode_t *x, const int i)
{
  mtnode_t *y = x->ptr[i];
  mtnode_t *z;
  z = (mtnode_t *)xcalloc(1, y->level ? ILEN : sizeof(mtnode_t));
  b->n_nodes++;
  z->level = y->level;
  z->n = T - 1;
  memcpy(z->key, &y->key[T], sizeof(mtkey_t) * (T - 1));
  for (int j = 0; j < T-1; j++) {
    refkey(b, z, j);
  }
  if (y->level) {
    memcpy(z->ptr, &y->ptr[T], sizeof(mtnode_t *) * T);
    for (int j = 0; j < T; j++) {
      z->ptr[j]->parent = z;
    }
  }
  y->n = T - 1;
  memmove(&x->ptr[i + 2], &x->ptr[i + 1],
          sizeof(mtnode_t *) * (size_t)(x->n - i));
  x->ptr[i + 1] = z;
  z->parent = x;  // == y->parent
  memmove(&x->key[i + 1], &x->key[i], sizeof(mtkey_t) * (size_t)(x->n - i));

  // move key to internal layer:
  x->key[i] = y->key[T - 1];
  refkey(b, x, i);
  x->n++;

  for (int j = 0; j < T-1; j++) {
    relative(x->key[i].pos, &z->key[j].pos);
  }
  if (i > 0) {
    unrelative(x->key[i-1].pos, &x->key[i].pos);
  }
}

// x must not be a full node (even if there might be internal space)
static inline void marktree_putp_aux(MarkTree *b, mtnode_t *x, mtkey_t k)
{
  int i;
  if (x->level == 0) {
    i = marktree_getp_aux(x, k, 0);
    if (i != x->n - 1) {
      memmove(&x->key[i + 2], &x->key[i + 1],
              (size_t)(x->n - i - 1) * sizeof(mtkey_t));
    }
    x->key[i + 1] = k;
    refkey(b, x, i+1);
    x->n++;
  } else {
    i = marktree_getp_aux(x, k, 0) + 1;
    if (x->ptr[i]->n == 2 * T - 1) {
      split_node(b, x, i);
      if (key_cmp(k, x->key[i]) > 0) {
        i++;
      }
    }
    if (i > 0) {
      relative(x->key[i-1].pos, &k.pos);
    }
    marktree_putp_aux(b, x->ptr[i], k);
  }
}

void marktree_put(MarkTree *b, mtkey_t key, int end_row, int end_col, bool end_right)
{
  assert(!(key.flags & ~MT_FLAG_EXTERNAL_MASK));
  if (end_row >= 0) {
    key.flags |= MT_FLAG_PAIRED;
  }

  marktree_put_key(b, key);

  if (end_row >= 0) {
    mtkey_t end_key = key;
    end_key.flags = (uint16_t)((uint16_t)(key.flags & ~MT_FLAG_RIGHT_GRAVITY)
                               |(uint16_t)MT_FLAG_END
                               |(uint16_t)(end_right ? MT_FLAG_RIGHT_GRAVITY : 0));
    end_key.pos = (mtpos_t){ end_row, end_col };
    marktree_put_key(b, end_key);
  }
}

void marktree_put_key(MarkTree *b, mtkey_t k)
{
  k.flags |= MT_FLAG_REAL;  // let's be real.
  if (!b->root) {
    b->root = (mtnode_t *)xcalloc(1, ILEN);
    b->n_nodes++;
  }
  mtnode_t *r, *s;
  b->n_keys++;
  r = b->root;
  if (r->n == 2 * T - 1) {
    b->n_nodes++;
    s = (mtnode_t *)xcalloc(1, ILEN);
    b->root = s; s->level = r->level+1; s->n = 0;
    s->ptr[0] = r;
    r->parent = s;
    split_node(b, s, 0);
    r = s;
  }
  marktree_putp_aux(b, r, k);
}

/// INITIATING DELETION PROTOCOL:
///
/// 1. Construct a valid iterator to the node to delete (argument)
/// 2. If an "internal" key. Iterate one step to the left or right,
///     which gives an internal key "auxiliary key".
/// 3. Now delete this internal key (intended or auxiliary).
///    The leaf node X might become undersized.
/// 4. If step two was done: now replace the key that _should_ be
///    deleted with the auxiliary key. Adjust relative
/// 5. Now "repair" the tree as needed. We always start at a leaf node X.
///     - if the node is big enough, terminate
///     - if we can steal from the left, steal
///     - if we can steal from the right, steal
///     - otherwise merge this node with a neighbour. This might make our
///       parent undersized. So repeat 5 for the parent.
/// 6. If 4 went all the way to the root node. The root node
///    might have ended up with size 0. Delete it then.
///
/// NB: ideally keeps the iterator valid. Like point to the key after this
/// if present.
///
/// @param rev should be true if we plan to iterate _backwards_ and delete
///            stuff before this key. Most of the time this is false (the
///            recommended strategy is to always iterate forward)
void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
{
  int adjustment = 0;

  mtnode_t *cur = itr->node;
  int curi = itr->i;
  uint64_t id = mt_lookup_key(cur->key[curi]);
  // fprintf(stderr, "\nDELET %lu\n", id);

  if (itr->node->level) {
    if (rev) {
      abort();
    } else {
      // fprintf(stderr, "INTERNAL %d\n", cur->level);
      // steal previous node
      marktree_itr_prev(b, itr);
      adjustment = -1;
    }
  }

  // 3.
  mtnode_t *x = itr->node;
  assert(x->level == 0);
  mtkey_t intkey = x->key[itr->i];
  if (x->n > itr->i+1) {
    memmove(&x->key[itr->i], &x->key[itr->i+1],
            sizeof(mtkey_t) * (size_t)(x->n - itr->i-1));
  }
  x->n--;

  // 4.
  // if (adjustment == 1) {
  //   abort();
  // }
  if (adjustment == -1) {
    int ilvl = itr->lvl-1;
    const mtnode_t *lnode = x;
    do {
      const mtnode_t *const p = lnode->parent;
      if (ilvl < 0) {
        abort();
      }
      const int i = itr->s[ilvl].i;
      assert(p->ptr[i] == lnode);
      if (i > 0) {
        unrelative(p->key[i-1].pos, &intkey.pos);
      }
      lnode = p;
      ilvl--;
    } while (lnode != cur);

    mtkey_t deleted = cur->key[curi];
    cur->key[curi] = intkey;
    refkey(b, cur, curi);
    relative(intkey.pos, &deleted.pos);
    mtnode_t *y = cur->ptr[curi+1];
    if (deleted.pos.row || deleted.pos.col) {
      while (y) {
        for (int k = 0; k < y->n; k++) {
          unrelative(deleted.pos, &y->key[k].pos);
        }
        y = y->level ? y->ptr[0] : NULL;
      }
    }
    itr->i--;
  }

  b->n_keys--;
  pmap_del(uint64_t)(b->id2node, id);

  // 5.
  bool itr_dirty = false;
  int rlvl = itr->lvl-1;
  int *lasti = &itr->i;
  while (x != b->root) {
    assert(rlvl >= 0);
    mtnode_t *p = x->parent;
    if (x->n >= T-1) {
      // we are done, if this node is fine the rest of the tree will be
      break;
    }
    int pi = itr->s[rlvl].i;
    assert(p->ptr[pi] == x);
    if (pi > 0 && p->ptr[pi-1]->n > T-1) {
      *lasti += 1;
      itr_dirty = true;
      // steal one key from the left neighbour
      pivot_right(b, p, pi-1);
      break;
    } else if (pi < p->n && p->ptr[pi+1]->n > T-1) {
      // steal one key from right neighbour
      pivot_left(b, p, pi);
      break;
    } else if (pi > 0) {
      // fprintf(stderr, "LEFT ");
      assert(p->ptr[pi-1]->n == T-1);
      // merge with left neighbour
      *lasti += T;
      x = merge_node(b, p, pi-1);
      if (lasti == &itr->i) {
        // TRICKY: we merged the node the iterator was on
        itr->node = x;
      }
      itr->s[rlvl].i--;
      itr_dirty = true;
    } else {
      // fprintf(stderr, "RIGHT ");
      assert(pi < p->n && p->ptr[pi+1]->n == T-1);
      merge_node(b, p, pi);
      // no iter adjustment needed
    }
    lasti = &itr->s[rlvl].i;
    rlvl--;
    x = p;
  }

  // 6.
  if (b->root->n == 0) {
    if (itr->lvl > 0) {
      memmove(itr->s, itr->s+1, (size_t)(itr->lvl-1) * sizeof(*itr->s));
      itr->lvl--;
    }
    if (b->root->level) {
      mtnode_t *oldroot = b->root;
      b->root = b->root->ptr[0];
      b->root->parent = NULL;
      xfree(oldroot);
    } else {
      // no items, nothing for iterator to point to
      // not strictly needed, should handle delete right-most mark anyway
      itr->node = NULL;
    }
  }

  if (itr->node && itr_dirty) {
    marktree_itr_fix_pos(b, itr);
  }

  // BONUS STEP: fix the iterator, so that it points to the key afterwards
  // TODO(bfredl): with "rev" should point before
  // if (adjustment == 1) {
  //   abort();
  // }
  if (adjustment == -1) {
    // tricky: we stand at the deleted space in the previous leaf node.
    // But the inner key is now the previous key we stole, so we need
    // to skip that one as well.
    marktree_itr_next(b, itr);
    marktree_itr_next(b, itr);
  } else {
    if (itr->node && itr->i >= itr->node->n) {
      // we deleted the last key of a leaf node
      // go to the inner key after that.
      assert(itr->node->level == 0);
      marktree_itr_next(b, itr);
    }
  }
}

static mtnode_t *merge_node(MarkTree *b, mtnode_t *p, int i)
{
  mtnode_t *x = p->ptr[i], *y = p->ptr[i+1];

  x->key[x->n] = p->key[i];
  refkey(b, x, x->n);
  if (i > 0) {
    relative(p->key[i-1].pos, &x->key[x->n].pos);
  }

  memmove(&x->key[x->n+1], y->key, (size_t)y->n * sizeof(mtkey_t));
  for (int k = 0; k < y->n; k++) {
    refkey(b, x, x->n+1+k);
    unrelative(x->key[x->n].pos, &x->key[x->n+1+k].pos);
  }
  if (x->level) {
    memmove(&x->ptr[x->n+1], y->ptr, ((size_t)y->n + 1) * sizeof(mtnode_t *));
    for (int k = 0; k < y->n+1; k++) {
      x->ptr[x->n+k+1]->parent = x;
    }
  }
  x->n += y->n+1;
  memmove(&p->key[i], &p->key[i + 1], (size_t)(p->n - i - 1) * sizeof(mtkey_t));
  memmove(&p->ptr[i + 1], &p->ptr[i + 2],
          (size_t)(p->n - i - 1) * sizeof(mtkey_t *));
  p->n--;
  xfree(y);
  b->n_nodes--;
  return x;
}

// TODO(bfredl): as a potential "micro" optimization, pivoting should balance
// the two nodes instead of stealing just one key
static void pivot_right(MarkTree *b, mtnode_t *p, int i)
{
  mtnode_t *x = p->ptr[i], *y = p->ptr[i+1];
  memmove(&y->key[1], y->key, (size_t)y->n * sizeof(mtkey_t));
  if (y->level) {
    memmove(&y->ptr[1], y->ptr, ((size_t)y->n + 1) * sizeof(mtnode_t *));
  }
  y->key[0] = p->key[i];
  refkey(b, y, 0);
  p->key[i] = x->key[x->n - 1];
  refkey(b, p, i);
  if (x->level) {
    y->ptr[0] = x->ptr[x->n];
    y->ptr[0]->parent = y;
  }
  x->n--;
  y->n++;
  if (i > 0) {
    unrelative(p->key[i-1].pos, &p->key[i].pos);
  }
  relative(p->key[i].pos, &y->key[0].pos);
  for (int k = 1; k < y->n; k++) {
    unrelative(y->key[0].pos, &y->key[k].pos);
  }
}

static void pivot_left(MarkTree *b, mtnode_t *p, int i)
{
  mtnode_t *x = p->ptr[i], *y = p->ptr[i+1];

  // reverse from how we "always" do it. but pivot_left
  // is just the inverse of pivot_right, so reverse it literally.
  for (int k = 1; k < y->n; k++) {
    relative(y->key[0].pos, &y->key[k].pos);
  }
  unrelative(p->key[i].pos, &y->key[0].pos);
  if (i > 0) {
    relative(p->key[i-1].pos, &p->key[i].pos);
  }

  x->key[x->n] = p->key[i];
  refkey(b, x, x->n);
  p->key[i] = y->key[0];
  refkey(b, p, i);
  if (x->level) {
    x->ptr[x->n+1] = y->ptr[0];
    x->ptr[x->n+1]->parent = x;
  }
  memmove(y->key, &y->key[1], (size_t)(y->n-1) * sizeof(mtkey_t));
  if (y->level) {
    memmove(y->ptr, &y->ptr[1], (size_t)y->n * sizeof(mtnode_t *));
  }
  x->n++;
  y->n--;
}

/// frees all mem, resets tree to valid empty state
void marktree_clear(MarkTree *b)
{
  if (b->root) {
    marktree_free_node(b->root);
    b->root = NULL;
  }
  if (b->id2node->table.keys) {
    pmap_destroy(uint64_t)(b->id2node);
    pmap_init(uint64_t, b->id2node);
  }
  b->n_keys = 0;
  b->n_nodes = 0;
}

void marktree_free_node(mtnode_t *x)
{
  if (x->level) {
    for (int i = 0; i < x->n+1; i++) {
      marktree_free_node(x->ptr[i]);
    }
  }
  xfree(x);
}

/// NB: caller must check not pair!
void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, mtkey_t key)
{
  // TODO(bfredl): clean up this mess and re-instantiate &= and |= forms
  // once we upgrade to a non-broken version of gcc in functionaltest-lua CI
  rawkey(itr).flags = (uint16_t)(rawkey(itr).flags & (uint16_t) ~MT_FLAG_DECOR_MASK);
  rawkey(itr).flags = (uint16_t)(rawkey(itr).flags
                                 | (uint16_t)(decor_level << MT_FLAG_DECOR_OFFSET)
                                 | (uint16_t)(key.flags & MT_FLAG_DECOR_MASK));
  rawkey(itr).decor_full = key.decor_full;
  rawkey(itr).hl_id = key.hl_id;
  rawkey(itr).priority = key.priority;
}

void marktree_move(MarkTree *b, MarkTreeIter *itr, int row, int col)
{
  mtkey_t key = rawkey(itr);
  // TODO(bfredl): optimize when moving a mark within a leaf without moving it
  // across neighbours!
  marktree_del_itr(b, itr, false);
  key.pos = (mtpos_t){ row, col };


  marktree_put_key(b, key);
  itr->node = NULL;  // itr might become invalid by put
}

// itr functions

// TODO(bfredl): static inline?
bool marktree_itr_get(MarkTree *b, int row, int col, MarkTreeIter *itr)
{
  return marktree_itr_get_ext(b, (mtpos_t){ row, col },
                              itr, false, false, NULL);
}

bool marktree_itr_get_ext(MarkTree *b, mtpos_t p, MarkTreeIter *itr, bool last, bool gravity,
                          mtpos_t *oldbase)
{
  if (b->n_keys == 0) {
    itr->node = NULL;
    return false;
  }

  mtkey_t k = { .pos = p, .flags = gravity ? MT_FLAG_RIGHT_GRAVITY : 0 };
  if (last && !gravity) {
    k.flags = MT_FLAG_LAST;
  }
  itr->pos = (mtpos_t){ 0, 0 };
  itr->node = b->root;
  itr->lvl = 0;
  if (oldbase) {
    oldbase[itr->lvl] = itr->pos;
  }
  while (true) {
    itr->i = marktree_getp_aux(itr->node, k, 0)+1;

    if (itr->node->level == 0) {
      break;
    }

    itr->s[itr->lvl].i = itr->i;
    itr->s[itr->lvl].oldcol = itr->pos.col;

    if (itr->i > 0) {
      compose(&itr->pos, itr->node->key[itr->i-1].pos);
      relative(itr->node->key[itr->i-1].pos, &k.pos);
    }
    itr->node = itr->node->ptr[itr->i];
    itr->lvl++;
    if (oldbase) {
      oldbase[itr->lvl] = itr->pos;
    }
  }

  if (last) {
    return marktree_itr_prev(b, itr);
  } else if (itr->i >= itr->node->n) {
    return marktree_itr_next(b, itr);
  }
  return true;
}

bool marktree_itr_first(MarkTree *b, MarkTreeIter *itr)
{
  itr->node = b->root;
  if (b->n_keys == 0) {
    return false;
  }

  itr->i = 0;
  itr->lvl = 0;
  itr->pos = (mtpos_t){ 0, 0 };
  while (itr->node->level > 0) {
    itr->s[itr->lvl].i = 0;
    itr->s[itr->lvl].oldcol = 0;
    itr->lvl++;
    itr->node = itr->node->ptr[0];
  }
  return true;
}

// gives the first key that is greater or equal to p
int marktree_itr_last(MarkTree *b, MarkTreeIter *itr)
{
  if (b->n_keys == 0) {
    itr->node = NULL;
    return false;
  }
  itr->pos = (mtpos_t){ 0, 0 };
  itr->node = b->root;
  itr->lvl = 0;
  while (true) {
    itr->i = itr->node->n;

    if (itr->node->level == 0) {
      break;
    }

    itr->s[itr->lvl].i = itr->i;
    itr->s[itr->lvl].oldcol = itr->pos.col;

    assert(itr->i > 0);
    compose(&itr->pos, itr->node->key[itr->i-1].pos);

    itr->node = itr->node->ptr[itr->i];
    itr->lvl++;
  }
  itr->i--;
  return true;
}

// TODO(bfredl): static inline
bool marktree_itr_next(MarkTree *b, MarkTreeIter *itr)
{
  return marktree_itr_next_skip(b, itr, false, NULL);
}

static bool marktree_itr_next_skip(MarkTree *b, MarkTreeIter *itr, bool skip, mtpos_t oldbase[])
{
  if (!itr->node) {
    return false;
  }
  itr->i++;
  if (itr->node->level == 0 || skip) {
    if (itr->i < itr->node->n) {
      // TODO(bfredl): this is the common case,
      // and could be handled by inline wrapper
      return true;
    }
    // we ran out of non-internal keys. Go up until we find an internal key
    while (itr->i >= itr->node->n) {
      itr->node = itr->node->parent;
      if (itr->node == NULL) {
        return false;
      }
      itr->lvl--;
      itr->i = itr->s[itr->lvl].i;
      if (itr->i > 0) {
        itr->pos.row -= itr->node->key[itr->i-1].pos.row;
        itr->pos.col = itr->s[itr->lvl].oldcol;
      }
    }
  } else {
    // we stood at an "internal" key. Go down to the first non-internal
    // key after it.
    while (itr->node->level > 0) {
      // internal key, there is always a child after
      if (itr->i > 0) {
        itr->s[itr->lvl].oldcol = itr->pos.col;
        compose(&itr->pos, itr->node->key[itr->i-1].pos);
      }
      if (oldbase && itr->i == 0) {
        oldbase[itr->lvl+1] = oldbase[itr->lvl];
      }
      itr->s[itr->lvl].i = itr->i;
      assert(itr->node->ptr[itr->i]->parent == itr->node);
      itr->node = itr->node->ptr[itr->i];
      itr->i = 0;
      itr->lvl++;
    }
  }
  return true;
}

bool marktree_itr_prev(MarkTree *b, MarkTreeIter *itr)
{
  if (!itr->node) {
    return false;
  }
  if (itr->node->level == 0) {
    itr->i--;
    if (itr->i >= 0) {
      // TODO(bfredl): this is the common case,
      // and could be handled by inline wrapper
      return true;
    }
    // we ran out of non-internal keys. Go up until we find a non-internal key
    while (itr->i < 0) {
      itr->node = itr->node->parent;
      if (itr->node == NULL) {
        return false;
      }
      itr->lvl--;
      itr->i = itr->s[itr->lvl].i-1;
      if (itr->i >= 0) {
        itr->pos.row -= itr->node->key[itr->i].pos.row;
        itr->pos.col = itr->s[itr->lvl].oldcol;
      }
    }
  } else {
    // we stood at an "internal" key. Go down to the last non-internal
    // key before it.
    while (itr->node->level > 0) {
      // internal key, there is always a child before
      if (itr->i > 0) {
        itr->s[itr->lvl].oldcol = itr->pos.col;
        compose(&itr->pos, itr->node->key[itr->i-1].pos);
      }
      itr->s[itr->lvl].i = itr->i;
      assert(itr->node->ptr[itr->i]->parent == itr->node);
      itr->node = itr->node->ptr[itr->i];
      itr->i = itr->node->n;
      itr->lvl++;
    }
    itr->i--;
  }
  return true;
}

void marktree_itr_rewind(MarkTree *b, MarkTreeIter *itr)
{
  if (!itr->node) {
    return;
  }
  if (itr->node->level) {
    marktree_itr_prev(b, itr);
  }
  itr->i = 0;
}

bool marktree_itr_node_done(MarkTreeIter *itr)
{
  return !itr->node || itr->i == itr->node->n-1;
}


mtpos_t marktree_itr_pos(MarkTreeIter *itr)
{
  mtpos_t pos = rawkey(itr).pos;
  unrelative(itr->pos, &pos);
  return pos;
}

mtkey_t marktree_itr_current(MarkTreeIter *itr)
{
  if (itr->node) {
    mtkey_t key = rawkey(itr);
    key.pos = marktree_itr_pos(itr);
    return key;
  }
  return MT_INVALID_KEY;
}

static bool itr_eq(MarkTreeIter *itr1, MarkTreeIter *itr2)
{
  return (&rawkey(itr1) == &rawkey(itr2));
}

static void itr_swap(MarkTreeIter *itr1, MarkTreeIter *itr2)
{
  mtkey_t key1 = rawkey(itr1);
  mtkey_t key2 = rawkey(itr2);
  rawkey(itr1) = key2;
  rawkey(itr1).pos = key1.pos;
  rawkey(itr2) = key1;
  rawkey(itr2).pos = key2.pos;
}

bool marktree_splice(MarkTree *b, int start_line, int start_col, int old_extent_line,
                     int old_extent_col, int new_extent_line, int new_extent_col)
{
  mtpos_t start = { start_line, start_col };
  mtpos_t old_extent = { old_extent_line, old_extent_col };
  mtpos_t new_extent = { new_extent_line, new_extent_col };

  bool may_delete = (old_extent.row != 0 || old_extent.col != 0);
  bool same_line = old_extent.row == 0 && new_extent.row == 0;
  unrelative(start, &old_extent);
  unrelative(start, &new_extent);
  MarkTreeIter itr[1] = { 0 };
  MarkTreeIter enditr[1] = { 0 };

  mtpos_t oldbase[MT_MAX_DEPTH] = { 0 };

  marktree_itr_get_ext(b, start, itr, false, true, oldbase);
  if (!itr->node) {
    // den e FÄRDIG
    return false;
  }
  mtpos_t delta = { new_extent.row - old_extent.row,
                    new_extent.col-old_extent.col };

  if (may_delete) {
    mtpos_t ipos = marktree_itr_pos(itr);
    if (!pos_leq(old_extent, ipos)
        || (old_extent.row == ipos.row && old_extent.col == ipos.col
            && !mt_right(rawkey(itr)))) {
      marktree_itr_get_ext(b, old_extent, enditr, true, true, NULL);
      assert(enditr->node);
      // "assert" (itr <= enditr)
    } else {
      may_delete = false;
    }
  }

  bool past_right = false;
  bool moved = false;

  // Follow the general strategy of messing things up and fix them later
  // "oldbase" carries the information needed to calculate old position of
  // children.
  if (may_delete) {
    while (itr->node && !past_right) {
      mtpos_t loc_start = start;
      mtpos_t loc_old = old_extent;
      relative(itr->pos, &loc_start);

      relative(oldbase[itr->lvl], &loc_old);

continue_same_node:
      // NB: strictly should be less than the right gravity of loc_old, but
      // the iter comparison below will already break on that.
      if (!pos_leq(rawkey(itr).pos, loc_old)) {
        break;
      }

      if (mt_right(rawkey(itr))) {
        while (!itr_eq(itr, enditr)
               && mt_right(rawkey(enditr))) {
          marktree_itr_prev(b, enditr);
        }
        if (!mt_right(rawkey(enditr))) {
          itr_swap(itr, enditr);
          refkey(b, itr->node, itr->i);
          refkey(b, enditr->node, enditr->i);
        } else {
          past_right = true;  // NOLINT
          (void)past_right;
          break;
        }
      }

      if (itr_eq(itr, enditr)) {
        // actually, will be past_right after this key
        past_right = true;
      }

      moved = true;
      if (itr->node->level) {
        oldbase[itr->lvl+1] = rawkey(itr).pos;
        unrelative(oldbase[itr->lvl], &oldbase[itr->lvl+1]);
        rawkey(itr).pos = loc_start;
        marktree_itr_next_skip(b, itr, false, oldbase);
      } else {
        rawkey(itr).pos = loc_start;
        if (itr->i < itr->node->n-1) {
          itr->i++;
          if (!past_right) {
            goto continue_same_node;
          }
        } else {
          marktree_itr_next(b, itr);
        }
      }
    }
    while (itr->node) {
      mtpos_t loc_new = new_extent;
      relative(itr->pos, &loc_new);
      mtpos_t limit = old_extent;

      relative(oldbase[itr->lvl], &limit);

past_continue_same_node:

      if (pos_leq(limit, rawkey(itr).pos)) {
        break;
      }

      mtpos_t oldpos = rawkey(itr).pos;
      rawkey(itr).pos = loc_new;
      moved = true;
      if (itr->node->level) {
        oldbase[itr->lvl+1] = oldpos;
        unrelative(oldbase[itr->lvl], &oldbase[itr->lvl+1]);

        marktree_itr_next_skip(b, itr, false, oldbase);
      } else {
        if (itr->i < itr->node->n-1) {
          itr->i++;
          goto past_continue_same_node;
        } else {
          marktree_itr_next(b, itr);
        }
      }
    }
  }


  while (itr->node) {
    unrelative(oldbase[itr->lvl], &rawkey(itr).pos);
    int realrow = rawkey(itr).pos.row;
    assert(realrow >= old_extent.row);
    bool done = false;
    if (realrow == old_extent.row) {
      if (delta.col) {
        rawkey(itr).pos.col += delta.col;
        moved = true;
      }
    } else {
      if (same_line) {
        // optimization: column only adjustment can skip remaining rows
        done = true;
      }
    }
    if (delta.row) {
      rawkey(itr).pos.row += delta.row;
      moved = true;
    }
    relative(itr->pos, &rawkey(itr).pos);
    if (done) {
      break;
    }
    marktree_itr_next_skip(b, itr, true, NULL);
  }
  return moved;
}

void marktree_move_region(MarkTree *b, int start_row, colnr_T start_col, int extent_row,
                          colnr_T extent_col, int new_row, colnr_T new_col)
{
  mtpos_t start = { start_row, start_col }, size = { extent_row, extent_col };
  mtpos_t end = size;
  unrelative(start, &end);
  MarkTreeIter itr[1] = { 0 };
  marktree_itr_get_ext(b, start, itr, false, true, NULL);
  kvec_t(mtkey_t) saved = KV_INITIAL_VALUE;
  while (itr->node) {
    mtkey_t k = marktree_itr_current(itr);
    if (!pos_leq(k.pos, end) || (k.pos.row == end.row && k.pos.col == end.col
                                 && mt_right(k))) {
      break;
    }
    relative(start, &k.pos);
    kv_push(saved, k);
    marktree_del_itr(b, itr, false);
  }

  marktree_splice(b, start.row, start.col, size.row, size.col, 0, 0);
  mtpos_t new = { new_row, new_col };
  marktree_splice(b, new.row, new.col,
                  0, 0, size.row, size.col);

  for (size_t i = 0; i < kv_size(saved); i++) {
    mtkey_t item = kv_A(saved, i);
    unrelative(new, &item.pos);
    marktree_put_key(b, item);
  }
  kv_destroy(saved);
}

/// @param itr OPTIONAL. set itr to pos.
mtkey_t marktree_lookup_ns(MarkTree *b, uint32_t ns, uint32_t id, bool end, MarkTreeIter *itr)
{
  return marktree_lookup(b, mt_lookup_id(ns, id, end), itr);
}

/// @param itr OPTIONAL. set itr to pos.
mtkey_t marktree_lookup(MarkTree *b, uint64_t id, MarkTreeIter *itr)
{
  mtnode_t *n = pmap_get(uint64_t)(b->id2node, id);
  if (n == NULL) {
    if (itr) {
      itr->node = NULL;
    }
    return MT_INVALID_KEY;
  }
  int i = 0;
  for (i = 0; i < n->n; i++) {
    if (mt_lookup_key(n->key[i]) == id) {
      goto found;
    }
  }
  abort();
found: {}
  mtkey_t key = n->key[i];
  if (itr) {
    itr->i = i;
    itr->node = n;
    itr->lvl = b->root->level - n->level;
  }
  while (n->parent != NULL) {
    mtnode_t *p = n->parent;
    for (i = 0; i < p->n+1; i++) {
      if (p->ptr[i] == n) {
        goto found_node;
      }
    }
    abort();
found_node:
    if (itr) {
      itr->s[b->root->level-p->level].i = i;
    }
    if (i > 0) {
      unrelative(p->key[i-1].pos, &key.pos);
    }
    n = p;
  }
  if (itr) {
    marktree_itr_fix_pos(b, itr);
  }
  return key;
}

mtpos_t marktree_get_altpos(MarkTree *b, mtkey_t mark, MarkTreeIter *itr)
{
  return marktree_get_alt(b, mark, itr).pos;
}

mtkey_t marktree_get_alt(MarkTree *b, mtkey_t mark, MarkTreeIter *itr)
{
  mtkey_t end = MT_INVALID_KEY;
  if (mt_paired(mark)) {
    end = marktree_lookup_ns(b, mark.ns, mark.id, !mt_end(mark), itr);
  }
  return end;
}

static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr)
{
  itr->pos = (mtpos_t){ 0, 0 };
  mtnode_t *x = b->root;
  for (int lvl = 0; lvl < itr->lvl; lvl++) {
    itr->s[lvl].oldcol = itr->pos.col;
    int i = itr->s[lvl].i;
    if (i > 0) {
      compose(&itr->pos, x->key[i-1].pos);
    }
    assert(x->level);
    x = x->ptr[i];
  }
  assert(x == itr->node);
}

// for unit test
void marktree_put_test(MarkTree *b, uint32_t id, int row, int col, bool right_gravity)
{
  mtkey_t key = { { row, col }, UINT32_MAX, id, 0,
                  mt_flags(right_gravity, 0), 0, NULL };
  marktree_put(b, key, -1, -1, false);
}

// for unit test
bool mt_right_test(mtkey_t key)
{
  return mt_right(key);
}

void marktree_check(MarkTree *b)
{
#ifndef NDEBUG
  if (b->root == NULL) {
    assert(b->n_keys == 0);
    assert(b->n_nodes == 0);
    assert(b->id2node == NULL || map_size(b->id2node) == 0);
    return;
  }

  mtpos_t dummy;
  bool last_right = false;
  size_t nkeys = check_node(b, b->root, &dummy, &last_right);
  assert(b->n_keys == nkeys);
  assert(b->n_keys == map_size(b->id2node));
#else
  // Do nothing, as assertions are required
  (void)b;
#endif
}

#ifndef NDEBUG
static size_t check_node(MarkTree *b, mtnode_t *x, mtpos_t *last, bool *last_right)
{
  assert(x->n <= 2 * T - 1);
  // TODO(bfredl): too strict if checking "in repair" post-delete tree.
  assert(x->n >= (x != b->root ? T-1 : 0));
  size_t n_keys = (size_t)x->n;

  for (int i = 0; i < x->n; i++) {
    if (x->level) {
      n_keys += check_node(b, x->ptr[i], last, last_right);
    } else {
      *last = (mtpos_t) { 0, 0 };
    }
    if (i > 0) {
      unrelative(x->key[i-1].pos, last);
    }
    assert(pos_leq(*last, x->key[i].pos));
    if (last->row == x->key[i].pos.row && last->col == x->key[i].pos.col) {
      assert(!*last_right || mt_right(x->key[i]));
    }
    *last_right = mt_right(x->key[i]);
    assert(x->key[i].pos.col >= 0);
    assert(pmap_get(uint64_t)(b->id2node, mt_lookup_key(x->key[i])) == x);
  }

  if (x->level) {
    n_keys += check_node(b, x->ptr[x->n], last, last_right);
    unrelative(x->key[x->n-1].pos, last);

    for (int i = 0; i < x->n+1; i++) {
      assert(x->ptr[i]->parent == x);
      assert(x->ptr[i]->level == x->level-1);
      // PARANOIA: check no double node ref
      for (int j = 0; j < i; j++) {
        assert(x->ptr[i] != x->ptr[j]);
      }
    }
  } else {
    *last = x->key[x->n-1].pos;
  }
  return n_keys;
}
#endif

char *mt_inspect_rec(MarkTree *b)
{
  garray_T ga;
  ga_init(&ga, (int)sizeof(char), 80);
  mtpos_t p = { 0, 0 };
  mt_inspect_node(b, &ga, b->root, p);
  return ga.ga_data;
}

void mt_inspect_node(MarkTree *b, garray_T *ga, mtnode_t *n, mtpos_t off)
{
  static char buf[1024];
  ga_concat(ga, "[");
  if (n->level) {
    mt_inspect_node(b, ga, n->ptr[0], off);
  }
  for (int i = 0; i < n->n; i++) {
    mtpos_t p = n->key[i].pos;
    unrelative(off, &p);
    snprintf((char *)buf, sizeof(buf), "%d/%d", p.row, p.col);
    ga_concat(ga, buf);
    if (n->level) {
      mt_inspect_node(b, ga, n->ptr[i+1], p);
    } else {
      ga_concat(ga, ",");
    }
  }
  ga_concat(ga, "]");
}

