/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* Part of this work is inspired by the original OpenBSD's tree.h */
/* $OpenBSD: tree.h,v 1.31 2023/03/08 04:43:09 guenther Exp $ */
/*
 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
 * All rights reserved.
 *
 * 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 ``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 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.
 */

#include "perfetto/ext/base/intrusive_tree.h"

namespace perfetto::base::internal {

namespace {

void RBSetBlackRed(RBNode* black, RBNode* red) {
  black->color = RBColor::BLACK;
  red->color = RBColor::RED;
}

void RBRotateLeft(RBNode** root, RBNode* elm, RBNode* tmp) {
  tmp = elm->right;
  if ((elm->right = tmp->left)) {
    tmp->left->parent = elm;
  }
  // RB_AUGMENT(elm);
  if ((tmp->parent = elm->parent)) {
    if (elm == elm->parent->left)
      elm->parent->left = tmp;
    else
      elm->parent->right = tmp;
  } else
    *root = tmp;
  tmp->left = elm;
  elm->parent = tmp;
  // RB_AUGMENT(tmp);
  // if ((tmp->parent))
  // RB_AUGMENT(tmp->parent);
}

void RBRotateRight(RBNode** root, RBNode* elm, RBNode* tmp) {
  tmp = elm->left;
  if ((elm->left = tmp->right)) {
    tmp->right->parent = elm;
  }
  // RB_AUGMENT(elm);
  if ((tmp->parent = elm->parent)) {
    if (elm == elm->parent->left)
      elm->parent->left = tmp;
    else
      elm->parent->right = tmp;
  } else
    *root = tmp;
  tmp->right = elm;
  elm->parent = tmp;
  // RB_AUGMENT(tmp);
  // if ((tmp->parent))
  // RB_AUGMENT(tmp->parent);
}

void RBRemoveColor(RBNode** root, RBNode* parent, RBNode* elm) {
  RBNode* tmp;
  while ((elm == nullptr || elm->color == RBColor::BLACK) && elm != *root) {
    if (parent->left == elm) {
      tmp = parent->right;
      if (tmp->color == RBColor::RED) {
        RBSetBlackRed(tmp, parent);
        RBRotateLeft(root, parent, tmp);
        tmp = parent->right;
      }
      if ((tmp->left == nullptr || tmp->left->color == RBColor::BLACK) &&
          (tmp->right == nullptr || tmp->right->color == RBColor::BLACK)) {
        tmp->color = RBColor::RED;
        elm = parent;
        parent = elm->parent;
      } else {
        if (tmp->right == nullptr || tmp->right->color == RBColor::BLACK) {
          RBNode* oleft;
          if ((oleft = tmp->left))
            oleft->color = RBColor::BLACK;
          tmp->color = RBColor::RED;
          RBRotateRight(root, tmp, oleft);
          tmp = parent->right;
        }
        tmp->color = parent->color;
        parent->color = RBColor::BLACK;
        if (tmp->right)
          tmp->right->color = RBColor::BLACK;
        RBRotateLeft(root, parent, tmp);
        elm = *root;
        break;
      }
    } else {
      tmp = parent->left;
      if (tmp->color == RBColor::RED) {
        RBSetBlackRed(tmp, parent);
        RBRotateRight(root, parent, tmp);
        tmp = parent->left;
      }
      if ((tmp->left == nullptr || tmp->left->color == RBColor::BLACK) &&
          (tmp->right == nullptr || tmp->right->color == RBColor::BLACK)) {
        tmp->color = RBColor::RED;
        elm = parent;
        parent = elm->parent;
      } else {
        if (tmp->left == nullptr || tmp->left->color == RBColor::BLACK) {
          RBNode* oright;
          if ((oright = tmp->right))
            oright->color = RBColor::BLACK;
          tmp->color = RBColor::RED;
          RBRotateLeft(root, tmp, oright);
          tmp = parent->left;
        }
        tmp->color = parent->color;
        parent->color = RBColor::BLACK;
        if (tmp->left)
          tmp->left->color = RBColor::BLACK;
        RBRotateRight(root, parent, tmp);
        elm = *root;
        break;
      }
    }
  }
  if (elm)
    elm->color = RBColor::BLACK;
}

}  // namespace

void RBInsertColor(RBNode** root, RBNode* elm) {
  RBNode *parent, *gparent, *tmp;
  while ((parent = elm->parent) && parent->color == RBColor::RED) {
    gparent = parent->parent;
    if (parent == gparent->left) {
      tmp = gparent->right;
      if (tmp && tmp->color == RBColor::RED) {
        tmp->color = RBColor::BLACK;
        RBSetBlackRed(parent, gparent);
        elm = gparent;
        continue;
      }
      if (parent->right == elm) {
        RBRotateLeft(root, parent, tmp);
        tmp = parent;
        parent = elm;
        elm = tmp;
      }
      RBSetBlackRed(parent, gparent);
      RBRotateRight(root, gparent, tmp);
    } else {
      tmp = gparent->left;
      if (tmp && tmp->color == RBColor::RED) {
        tmp->color = RBColor::BLACK;
        RBSetBlackRed(parent, gparent);
        elm = gparent;
        continue;
      }
      if (parent->left == elm) {
        RBRotateRight(root, parent, tmp);
        tmp = parent;
        parent = elm;
        elm = tmp;
      }
      RBSetBlackRed(parent, gparent);
      RBRotateLeft(root, gparent, tmp);
    }
  }
  (*root)->color = RBColor::BLACK;
}

void RBRemove(RBNode** root, RBNode* elm) {
  RBNode* child = elm;
  RBNode* parent = elm;
  RBNode* old = elm;
  RBColor color;

  if (elm->left == nullptr)
    child = elm->right;
  else if (elm->right == nullptr)
    child = elm->left;
  else {
    RBNode* left;
    elm = elm->right;
    while ((left = elm->left))
      elm = left;
    child = elm->right;
    parent = elm->parent;
    color = elm->color;
    if (child)
      child->parent = parent;
    if (parent) {
      if (parent->left == elm) {
        parent->left = child;
      } else {
        parent->right = child;
      }
      // RB_AUGMENT(parent);
    } else {
      *root = child;
    }
    if (elm->parent == old)
      parent = elm;
    *elm = *old;
    if (old->parent) {
      if (old->parent->left == old) {
        old->parent->left = elm;
      } else {
        old->parent->right = elm;
      }
      // RB_AUGMENT(old->parent);
    } else {
      *root = elm;
    }
    old->left->parent = elm;
    if (old->right)
      old->right->parent = elm;
    if (parent) {
      left = parent;
      // do {
      //   RB_AUGMENT(left);
      // } while ((left = left->parent));
    }
    goto color;
  }
  parent = elm->parent;
  color = elm->color;
  if (child)
    child->parent = parent;
  if (parent) {
    if (parent->left == elm)
      parent->left = child;
    else
      parent->right = child;
    // RB_AUGMENT(parent);
  } else {
    *root = child;
  }
color:
  if (color == RBColor::BLACK)
    RBRemoveColor(root, parent, child);
}

// Returns nullptr after reaching the last leaf (the max element).
const RBNode* RBNext(const RBNode* node) {
  if (node->right) {
    node = node->right;
    while (node->left)
      node = node->left;
  } else {
    if (node->parent && node == node->parent->left) {
      node = node->parent;
    } else {
      while (node->parent && node == node->parent->right) {
        node = node->parent;
      }
      node = node->parent;
    }
  }
  return node;
}

}  // namespace perfetto::base::internal
