/** @file

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you 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.
 */

#pragma once

namespace ts
{
namespace detail
{
  /** A node in a red/black tree.

      This class provides only the basic tree operations. The client
      must provide the search and decision logic. This enables this
      class to be a base class for templated nodes with much less code
      duplication.
  */
  struct RBNode {
    typedef RBNode self; ///< self reference type

    /// Node colors
    typedef enum {
      RED,
      BLACK,
    } Color;

    /// Directional constants
    typedef enum {
      NONE,
      LEFT,
      RIGHT,
    } Direction;

    /// Get a child by direction.
    /// @return The child in the direction @a d if it exists,
    /// @c NULL if not.
    self *getChild(Direction d //!< The direction of the desired child
                   ) const;

    /** Determine which child a node is
        @return @c LEFT if @a n is the left child,
        @c RIGHT if @a n is the right child,
        @c NONE if @a n is not a child
    */
    Direction
    getChildDirection(self *const &n //!< The presumed child node
                      ) const
    {
      return (n == _left) ? LEFT : (n == _right) ? RIGHT : NONE;
    }

    /** Get the parent node.
        @return A Node* to the parent node or a @c nil Node* if no parent.
    */
    self *
    getParent() const
    {
      return const_cast<self *>(_parent);
    }

    /// @return The color of the node.
    Color
    getColor() const
    {
      return _color;
    }

    self *
    leftmostDescendant() const
    {
      const self *n = this;
      while (n->_left)
        n = n->_left;

      return const_cast<self *>(n);
    }

    /** Reverse a direction
        @return @c LEFT if @a d is @c RIGHT, @c RIGHT if @a d is @c LEFT,
        @c NONE otherwise.
    */
    Direction
    flip(Direction d)
    {
      return LEFT == d ? RIGHT : RIGHT == d ? LEFT : NONE;
    }

    /** Perform internal validation checks.
        @return 0 on failure, black height of the tree on success.
    */
    int validate();

    /// Default constructor.
    RBNode() : _color(RED), _parent(nullptr), _left(nullptr), _right(nullptr), _next(nullptr), _prev(nullptr) {}
    /// Destructor (force virtual).
    virtual ~RBNode() {}
    /** Rotate the subtree rooted at this node.
        The node is rotated in to the position of one of its children.
        Which child is determined by the direction parameter @a d. The
        child in the other direction becomes the new root of the subtree.

        If the parent pointer is set, then the child pointer of the original
        parent is updated so that the tree is left in a consistent state.

        @note If there is no child in the other direction, the rotation
        fails and the original node is returned. It is @b not required
        that a child exist in the direction specified by @a d.

        @return The new root node for the subtree.
    */
    self *rotate(Direction d //!< The direction to rotate
    );

    /** Set the child node in direction @a d to @a n.
        The @a d child is set to the node @a n. The pointers in this
        node and @a n are set correctly. This can only be called if
        there is no child node already present.

        @return @a n.
    */
    self *setChild(self *n,    //!< The node to set as the child
                   Direction d //!< The direction of the child
    );

    /** Remove this node from the tree.
        The tree is rebalanced after removal.
        @return The new root node.
    */
    self *remove();

    void
    clearChild(Direction dir)
    {
      if (LEFT == dir)
        _left = nullptr;
      else if (RIGHT == dir)
        _right = nullptr;
    }

    /** @name Subclass hook methods */
    //@{
    /** Structural change notification.
        This method is called if the structure of the subtree rooted at
        this node was changed.

        This is intended a hook. The base method is empty so that subclasses
        are not required to override.
    */
    virtual void
    structureFixup()
    {
    }

    /** Called from @c validate to perform any additional validation checks.
        Clients should chain this if they wish to perform additional checks.
        @return @c true if the validation is successful, @c false otherwise.
        @note The base method simply returns @c true.
    */
    virtual bool
    structureValidate()
    {
      return true;
    }
    //@}

    /** Replace this node with another node.
        This is presumed to be non-order modifying so the next reference
        is @b not updated.
    */
    void replaceWith(self *n //!< Node to put in place of this node.
    );

    //! Rebalance the tree starting at this node
    /** The tree is rebalanced so that all of the invariants are
        true. The (potentially new) root of the tree is returned.

        @return The root node of the tree after the rebalance.
    */
    self *rebalanceAfterInsert();

    /** Rebalance the tree after a deletion.
        Called on the lowest modified node.
        @return The new root of the tree.
    */
    self *rebalanceAfterRemove(Color c,    //!< The color of the removed node.
                               Direction d //!< Direction of removed node from parent
    );

    //! Invoke @c structure_fixup() on this node and all of its ancestors.
    self *rippleStructureFixup();

    Color _color;  ///< node color
    self *_parent; ///< parent node (needed for rotations)
    self *_left;   ///< left child
    self *_right;  ///< right child
    self *_next;   ///< Next node.
    self *_prev;   ///< Previous node.
  };

} /* namespace detail */

} /* namespace ts */
