// SPDX-License-Identifier: GPL-2.0-or-later
/** @file
 * TODO: insert short description here
 *//*
 * Authors:
 * see git history
 * Fred
 *
 * Copyright (C) 2018 Authors
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include "AVL.h"

/*
 * the algorithm explanation for this code comes from purists.org, which seems to have disappeared since
 * it's a classic AVL tree rebalancing, nothing fancy
 */

AVLTree::AVLTree()
{
    MakeNew();
}

AVLTree::~AVLTree()
{
    MakeDelete();
}

void AVLTree::MakeNew()
{
    for (int i = 0; i < 2; i++)
    {
        elem[i] = nullptr;
        child[i] = nullptr;
    }

    parent = nullptr;
    balance = 0;
}

void AVLTree::MakeDelete()
{
    for (int i = 0; i < 2; i++) {
        if (elem[i]) {
            elem[i]->elem[1 - i] = elem[1 - i];
        }
        elem[i] = nullptr;
    }
}

AVLTree *AVLTree::Leftmost()
{
    return leafFromParent(nullptr, LEFT);
}

AVLTree *AVLTree::leaf(AVLTree *from, Side s)
{
    if (from == child[1 - s]) {
	if (child[s]) {
	    return child[s]->leafFromParent(this, s);
	}
	else if (parent) {
	    return parent->leaf(this, s);
	}
    }
    else if (from == child[s]) {
	if (parent) {
	    return parent->leaf(this, s);
	}
    }

    return nullptr;
}

AVLTree *AVLTree::leafFromParent(AVLTree */*from*/, Side s)
{
    if (child[s]) {
	return child[s]->leafFromParent(this, s);
    }

    return this;
}

int
AVLTree::RestoreBalances (AVLTree * from, AVLTree * &racine)
{
  if (from == nullptr)
    {
      if (parent)
	return parent->RestoreBalances (this, racine);
    }
  else
    {
      if (balance == 0)
	{
	  if (from == child[LEFT])
	    balance = 1;
	  if (from == child[RIGHT])
	    balance = -1;
	  if (parent)
	    return parent->RestoreBalances (this, racine);
	  return avl_no_err;
	}
      else if (balance > 0)
	{
	  if (from == child[RIGHT])
	    {
	      balance = 0;
	      return avl_no_err;
	    }
	  if (child[LEFT] == nullptr)
	    {
//                              cout << "mierda\n";
	      return avl_bal_err;
	    }
	  AVLTree *a = this;
	  AVLTree *b = child[LEFT];
	  AVLTree *e = child[RIGHT];
	  AVLTree *c = child[LEFT]->child[LEFT];
	  AVLTree *d = child[LEFT]->child[RIGHT];
	  if (child[LEFT]->balance > 0)
	    {
	      AVLTree *r = parent;

	      a->parent = b;
	      b->child[RIGHT] = a;
	      a->child[RIGHT] = e;
	      if (e)
		e->parent = a;
	      a->child[LEFT] = d;
	      if (d)
		d->parent = a;
	      b->child[LEFT] = c;
	      if (c)
		c->parent = b;
	      b->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = b;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = b;
		}
	      if (racine == a)
		racine = b;

	      a->balance = 0;
	      b->balance = 0;
	      return avl_no_err;
	    }
	  else
	    {
	      if (child[LEFT]->child[RIGHT] == nullptr)
		{
		  //                              cout << "mierda\n";
		  return avl_bal_err;
		}
	      AVLTree *f = child[LEFT]->child[RIGHT]->child[LEFT];
	      AVLTree *g = child[LEFT]->child[RIGHT]->child[RIGHT];
	      AVLTree *r = parent;

	      a->parent = d;
	      d->child[RIGHT] = a;
	      b->parent = d;
	      d->child[LEFT] = b;
	      a->child[LEFT] = g;
	      if (g)
		g->parent = a;
	      a->child[RIGHT] = e;
	      if (e)
		e->parent = a;
	      b->child[LEFT] = c;
	      if (c)
		c->parent = b;
	      b->child[RIGHT] = f;
	      if (f)
		f->parent = b;

	      d->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = d;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = d;
		}
	      if (racine == a)
		racine = d;

	      int old_bal = d->balance;
	      d->balance = 0;
	      if (old_bal == 0)
		{
		  a->balance = 0;
		  b->balance = 0;
		}
	      else if (old_bal > 0)
		{
		  a->balance = -1;
		  b->balance = 0;
		}
	      else if (old_bal < 0)
		{
		  a->balance = 0;
		  b->balance = 1;
		}
	      return avl_no_err;
	    }
	}
      else if (balance < 0)
	{
	  if (from == child[LEFT])
	    {
	      balance = 0;
	      return avl_no_err;
	    }
	  if (child[RIGHT] == nullptr)
	    {
//                              cout << "mierda\n";
	      return avl_bal_err;
	    }
	  AVLTree *a = this;
	  AVLTree *b = child[RIGHT];
	  AVLTree *e = child[LEFT];
	  AVLTree *c = child[RIGHT]->child[RIGHT];
	  AVLTree *d = child[RIGHT]->child[LEFT];
	  AVLTree *r = parent;
	  if (child[RIGHT]->balance < 0)
	    {

	      a->parent = b;
	      b->child[LEFT] = a;
	      a->child[LEFT] = e;
	      if (e)
		e->parent = a;
	      a->child[RIGHT] = d;
	      if (d)
		d->parent = a;
	      b->child[RIGHT] = c;
	      if (c)
		c->parent = b;
	      b->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = b;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = b;
		}
	      if (racine == a)
		racine = b;
	      a->balance = 0;
	      b->balance = 0;
	      return avl_no_err;
	    }
	  else
	    {
	      if (child[RIGHT]->child[LEFT] == nullptr)
		{
//                                      cout << "mierda\n";
		  return avl_bal_err;
		}
	      AVLTree *f = child[RIGHT]->child[LEFT]->child[RIGHT];
	      AVLTree *g = child[RIGHT]->child[LEFT]->child[LEFT];

	      a->parent = d;
	      d->child[LEFT] = a;
	      b->parent = d;
	      d->child[RIGHT] = b;
	      a->child[RIGHT] = g;
	      if (g)
		g->parent = a;
	      a->child[LEFT] = e;
	      if (e)
		e->parent = a;
	      b->child[RIGHT] = c;
	      if (c)
		c->parent = b;
	      b->child[LEFT] = f;
	      if (f)
		f->parent = b;

	      d->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = d;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = d;
		}
	      if (racine == a)
		racine = d;
	      int old_bal = d->balance;
	      d->balance = 0;
	      if (old_bal == 0)
		{
		  a->balance = 0;
		  b->balance = 0;
		}
	      else if (old_bal > 0)
		{
		  a->balance = 0;
		  b->balance = -1;
		}
	      else if (old_bal < 0)
		{
		  a->balance = 1;
		  b->balance = 0;
		}
	      return avl_no_err;
	    }
	}
    }
  return avl_no_err;
}

int
AVLTree::RestoreBalances (int diff, AVLTree * &racine)
{
  if (balance > 0)
    {
      if (diff < 0)
	{
	  balance = 0;
	  if (parent)
	    {
	      if (this == parent->child[RIGHT])
		return parent->RestoreBalances (1, racine);
	      if (this == parent->child[LEFT])
		return parent->RestoreBalances (-1, racine);
	    }
	  return avl_no_err;
	}
      else if (diff == 0)
	{
	}
      else if (diff > 0)
	{
	  if (child[LEFT] == nullptr)
	    {
//                              cout << "un probleme\n";
	      return avl_bal_err;
	    }
	  AVLTree *r = parent;
	  AVLTree *a = this;
	  AVLTree *b = child[RIGHT];
	  AVLTree *e = child[LEFT];
	  AVLTree *f = e->child[RIGHT];
	  AVLTree *g = e->child[LEFT];
	  if (e->balance > 0)
	    {
	      e->child[RIGHT] = a;
	      e->child[LEFT] = g;
	      a->child[RIGHT] = b;
	      a->child[LEFT] = f;
	      if (a)
		a->parent = e;
	      if (g)
		g->parent = e;
	      if (b)
		b->parent = a;
	      if (f)
		f->parent = a;
	      e->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = e;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = e;
		}
	      if (racine == this)
		racine = e;
	      e->balance = 0;
	      a->balance = 0;
	      if (r)
		{
		  if (e == r->child[RIGHT])
		    return r->RestoreBalances (1, racine);
		  if (e == r->child[LEFT])
		    return r->RestoreBalances (-1, racine);
		}
	      return avl_no_err;
	    }
	  else if (e->balance == 0)
	    {
	      e->child[RIGHT] = a;
	      e->child[LEFT] = g;
	      a->child[RIGHT] = b;
	      a->child[LEFT] = f;
	      if (a)
		a->parent = e;
	      if (g)
		g->parent = e;
	      if (b)
		b->parent = a;
	      if (f)
		f->parent = a;
	      e->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = e;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = e;
		}
	      if (racine == this)
		racine = e;
	      e->balance = -1;
	      a->balance = 1;
	      return avl_no_err;
	    }
	  else if (e->balance < 0)
	    {
	      if (child[LEFT]->child[RIGHT] == nullptr)
		{
//                                      cout << "un probleme\n";
		  return avl_bal_err;
		}
	      AVLTree *i = child[LEFT]->child[RIGHT]->child[RIGHT];
	      AVLTree *j = child[LEFT]->child[RIGHT]->child[LEFT];

	      f->child[RIGHT] = a;
	      f->child[LEFT] = e;
	      a->child[RIGHT] = b;
	      a->child[LEFT] = i;
	      e->child[RIGHT] = j;
	      e->child[LEFT] = g;
	      if (b)
		b->parent = a;
	      if (i)
		i->parent = a;
	      if (g)
		g->parent = e;
	      if (j)
		j->parent = e;
	      if (a)
		a->parent = f;
	      if (e)
		e->parent = f;
	      f->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = f;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = f;
		}
	      if (racine == this)
		racine = f;
	      int oBal = f->balance;
	      f->balance = 0;
	      if (oBal > 0)
		{
		  a->balance = -1;
		  e->balance = 0;
		}
	      else if (oBal == 0)
		{
		  a->balance = 0;
		  e->balance = 0;
		}
	      else if (oBal < 0)
		{
		  a->balance = 0;
		  e->balance = 1;
		}
	      if (r)
		{
		  if (f == r->child[RIGHT])
		    return r->RestoreBalances (1, racine);
		  if (f == r->child[LEFT])
		    return r->RestoreBalances (-1, racine);
		}
	      return avl_no_err;
	    }
	}
    }
  else if (balance == 0)
    {
      if (diff < 0)
	{
	  balance = -1;
	}
      else if (diff == 0)
	{
	}
      else if (diff > 0)
	{
	  balance = 1;
	}
      return avl_no_err;
    }
  else if (balance < 0)
    {
      if (diff < 0)
	{
	  if (child[RIGHT] == nullptr)
	    {
//                              cout << "un probleme\n";
	      return avl_bal_err;
	    }
	  AVLTree *r = parent;
	  AVLTree *a = this;
	  AVLTree *b = child[LEFT];
	  AVLTree *e = child[RIGHT];
	  AVLTree *f = e->child[LEFT];
	  AVLTree *g = e->child[RIGHT];
	  if (e->balance < 0)
	    {
	      e->child[LEFT] = a;
	      e->child[RIGHT] = g;
	      a->child[LEFT] = b;
	      a->child[RIGHT] = f;
	      if (a)
		a->parent = e;
	      if (g)
		g->parent = e;
	      if (b)
		b->parent = a;
	      if (f)
		f->parent = a;
	      e->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = e;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = e;
		}
	      if (racine == this)
		racine = e;
	      e->balance = 0;
	      a->balance = 0;
	      if (r)
		{
		  if (e == r->child[RIGHT])
		    return r->RestoreBalances (1, racine);
		  if (e == r->child[LEFT])
		    return r->RestoreBalances (-1, racine);
		}
	      return avl_no_err;
	    }
	  else if (e->balance == 0)
	    {
	      e->child[LEFT] = a;
	      e->child[RIGHT] = g;
	      a->child[LEFT] = b;
	      a->child[RIGHT] = f;
	      if (a)
		a->parent = e;
	      if (g)
		g->parent = e;
	      if (b)
		b->parent = a;
	      if (f)
		f->parent = a;
	      e->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = e;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = e;
		}
	      if (racine == this)
		racine = e;
	      e->balance = 1;
	      a->balance = -1;
	      return avl_no_err;
	    }
	  else if (e->balance > 0)
	    {
	      if (child[RIGHT]->child[LEFT] == nullptr)
		{
//                                      cout << "un probleme\n";
		  return avl_bal_err;
		}
	      AVLTree *i = child[RIGHT]->child[LEFT]->child[LEFT];
	      AVLTree *j = child[RIGHT]->child[LEFT]->child[RIGHT];

	      f->child[LEFT] = a;
	      f->child[RIGHT] = e;
	      a->child[LEFT] = b;
	      a->child[RIGHT] = i;
	      e->child[LEFT] = j;
	      e->child[RIGHT] = g;
	      if (b)
		b->parent = a;
	      if (i)
		i->parent = a;
	      if (g)
		g->parent = e;
	      if (j)
		j->parent = e;
	      if (a)
		a->parent = f;
	      if (e)
		e->parent = f;
	      f->parent = r;
	      if (r)
		{
		  if (r->child[LEFT] == a)
		    r->child[LEFT] = f;
		  if (r->child[RIGHT] == a)
		    r->child[RIGHT] = f;
		}
	      if (racine == this)
		racine = f;
	      int oBal = f->balance;
	      f->balance = 0;
	      if (oBal > 0)
		{
		  a->balance = 0;
		  e->balance = -1;
		}
	      else if (oBal == 0)
		{
		  a->balance = 0;
		  e->balance = 0;
		}
	      else if (oBal < 0)
		{
		  a->balance = 1;
		  e->balance = 0;
		}
	      if (r)
		{
		  if (f == r->child[RIGHT])
		    return r->RestoreBalances (1, racine);
		  if (f == r->child[LEFT])
		    return r->RestoreBalances (-1, racine);
		}
	      return avl_no_err;
	    }
	}
      else if (diff == 0)
	{
	}
      else if (diff > 0)
	{
	  balance = 0;
	  if (parent)
	    {
	      if (this == parent->child[RIGHT])
		return parent->RestoreBalances (1, racine);
	      if (this == parent->child[LEFT])
		return parent->RestoreBalances (-1, racine);
	    }
	  return avl_no_err;
	}
    }
  return avl_no_err;
}

/*
 * removal
 */
int
AVLTree::Remove (AVLTree * &racine, bool rebalance)
{
  AVLTree *startNode = nullptr;
  int remDiff = 0;
  int res = Remove (racine, startNode, remDiff);
  if (res == avl_no_err && rebalance && startNode)
    res = startNode->RestoreBalances (remDiff, racine);
  return res;
}

int
AVLTree::Remove (AVLTree * &racine, AVLTree * &startNode, int &diff)
{
  if (elem[LEFT])
    elem[LEFT]->elem[RIGHT] = elem[RIGHT];
  if (elem[RIGHT])
    elem[RIGHT]->elem[LEFT] = elem[LEFT];
  elem[LEFT] = elem[RIGHT] = nullptr;

  if (child[LEFT] && child[RIGHT])
    {
      AVLTree *newMe = child[LEFT]->leafFromParent(this, RIGHT);
      if (newMe == nullptr || newMe->child[RIGHT])
	{
//                      cout << "pas normal\n";
	  return avl_rm_err;
	}
      if (newMe == child[LEFT])
	{
	  startNode = newMe;
	  diff = -1;
	  newMe->child[RIGHT] = child[RIGHT];
	  child[RIGHT]->parent = newMe;
	  newMe->parent = parent;
	  if (parent)
	    {
	      if (parent->child[LEFT] == this)
		parent->child[LEFT] = newMe;
	      if (parent->child[RIGHT] == this)
		parent->child[RIGHT] = newMe;
	    }
	}
      else
	{
	  AVLTree *oParent = newMe->parent;
	  startNode = oParent;
	  diff = 1;

	  oParent->child[RIGHT] = newMe->child[LEFT];
	  if (newMe->child[LEFT])
	    newMe->child[LEFT]->parent = oParent;

	  newMe->parent = parent;
	  newMe->child[LEFT] = child[LEFT];
	  newMe->child[RIGHT] = child[RIGHT];
	  if (parent)
	    {
	      if (parent->child[LEFT] == this)
		parent->child[LEFT] = newMe;
	      if (parent->child[RIGHT] == this)
		parent->child[RIGHT] = newMe;
	    }
	  if (child[LEFT])
	    child[LEFT]->parent = newMe;
	  if (child[RIGHT])
	    child[RIGHT]->parent = newMe;
	}
      newMe->balance = balance;
      if (racine == this)
	racine = newMe;
    }
  else if (child[LEFT])
    {
      startNode = parent;
      diff = 0;
      if (parent)
	{
	  if (this == parent->child[LEFT])
	    diff = -1;
	  if (this == parent->child[RIGHT])
	    diff = 1;
	}
      if (parent)
	{
	  if (parent->child[LEFT] == this)
	    parent->child[LEFT] = child[LEFT];
	  if (parent->child[RIGHT] == this)
	    parent->child[RIGHT] = child[LEFT];
	}
      if (child[LEFT]->parent == this)
	child[LEFT]->parent = parent;
      if (racine == this)
	racine = child[LEFT];
    }
  else if (child[RIGHT])
    {
      startNode = parent;
      diff = 0;
      if (parent)
	{
	  if (this == parent->child[LEFT])
	    diff = -1;
	  if (this == parent->child[RIGHT])
	    diff = 1;
	}
      if (parent)
	{
	  if (parent->child[LEFT] == this)
	    parent->child[LEFT] = child[RIGHT];
	  if (parent->child[RIGHT] == this)
	    parent->child[RIGHT] = child[RIGHT];
	}
      if (child[RIGHT]->parent == this)
	child[RIGHT]->parent = parent;
      if (racine == this)
	racine = child[RIGHT];
    }
  else
    {
      startNode = parent;
      diff = 0;
      if (parent)
	{
	  if (this == parent->child[LEFT])
	    diff = -1;
	  if (this == parent->child[RIGHT])
	    diff = 1;
	}
      if (parent)
	{
	  if (parent->child[LEFT] == this)
	    parent->child[LEFT] = nullptr;
	  if (parent->child[RIGHT] == this)
	    parent->child[RIGHT] = nullptr;
	}
      if (racine == this)
	racine = nullptr;
    }
  parent = child[RIGHT] = child[LEFT] = nullptr;
  balance = 0;
  return avl_no_err;
}

/*
 * insertion
 */
int
AVLTree::Insert (AVLTree * &racine, int insertType, AVLTree * insertL,
		 AVLTree * insertR, bool rebalance)
{
  int res = Insert (racine, insertType, insertL, insertR);
  if (res == avl_no_err && rebalance)
    res = RestoreBalances ((AVLTree *) nullptr, racine);
  return res;
}

int
AVLTree::Insert (AVLTree * &racine, int insertType, AVLTree * insertL,
		 AVLTree * insertR)
{
  if (racine == nullptr)
    {
      racine = this;
      return avl_no_err;
    }
  else
    {
      if (insertType == not_found)
	{
//                      cout << "pb avec l'arbre de raster\n";
	  return avl_ins_err;
	}
      else if (insertType == found_on_left)
	{
	  if (insertR == nullptr || insertR->child[LEFT])
	    {
//                              cout << "ngou?\n";
	      return avl_ins_err;
	    }
	  insertR->child[LEFT] = this;
	  parent = insertR;
	  insertOn(LEFT, insertR);
	}
      else if (insertType == found_on_right)
	{
	  if (insertL == nullptr || insertL->child[RIGHT])
	    {
//                              cout << "ngou?\n";
	      return avl_ins_err;
	    }
	  insertL->child[RIGHT] = this;
	  parent = insertL;
	  insertOn(RIGHT, insertL);
	}
      else if (insertType == found_between)
	{
	  if (insertR == nullptr || insertL == nullptr
	      || (insertR->child[LEFT] != nullptr && insertL->child[RIGHT] != nullptr))
	    {
//                              cout << "ngou?\n";
	      return avl_ins_err;
	    }
	  if (insertR->child[LEFT] == nullptr)
	    {
	      insertR->child[LEFT] = this;
	      parent = insertR;
	    }
	  else if (insertL->child[RIGHT] == nullptr)
	    {
	      insertL->child[RIGHT] = this;
	      parent = insertL;
	    }
	  insertBetween (insertL, insertR);
	}
      else if (insertType == found_exact)
	{
	  if (insertL == nullptr)
	    {
//                              cout << "ngou?\n";
	      return avl_ins_err;
	    }
	  // et on insere

	  if (insertL->child[RIGHT])
	    {
		insertL = insertL->child[RIGHT]->leafFromParent(insertL, LEFT);
	      if (insertL->child[LEFT])
		{
//                                      cout << "ngou?\n";
		  return avl_ins_err;
		}
	      insertL->child[LEFT] = this;
	      this->parent = insertL;
	      insertBetween (insertL->elem[LEFT], insertL);
	    }
	  else
	    {
	      insertL->child[RIGHT] = this;
	      parent = insertL;
	      insertBetween (insertL, insertL->elem[RIGHT]);
	    }
	}
      else
	{
	  //                      cout << "code incorrect\n";
	  return avl_ins_err;
	}
    }
  return avl_no_err;
}

void
AVLTree::Relocate (AVLTree * to)
{
  if (elem[LEFT])
    elem[LEFT]->elem[RIGHT] = to;
  if (elem[RIGHT])
    elem[RIGHT]->elem[LEFT] = to;
  to->elem[LEFT] = elem[LEFT];
  to->elem[RIGHT] = elem[RIGHT];
    
  if (parent)
    {
      if (parent->child[LEFT] == this)
	parent->child[LEFT] = to;
      if (parent->child[RIGHT] == this)
	parent->child[RIGHT] = to;
    }
  if (child[RIGHT])
    {
      child[RIGHT]->parent = to;
    }
  if (child[LEFT])
    {
      child[LEFT]->parent = to;
    }
  to->parent = parent;
  to->child[RIGHT] = child[RIGHT];
  to->child[LEFT] = child[LEFT];
}


void AVLTree::insertOn(Side s, AVLTree *of)
{
  elem[1 - s] = of;
  if (of)
    of->elem[s] = this;
}

void AVLTree::insertBetween(AVLTree *l, AVLTree *r)
{
  if (l)
    l->elem[RIGHT] = this;
  if (r)
    r->elem[LEFT] = this;
  elem[LEFT] = l;
  elem[RIGHT] = r;
}

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
