/**
 *  BracketTree class:
 *  The BracketTree class represents the brackets for a tournament or
 *  subtree of its brackets.  Each node in the tree represents a single
 *  participant (a leaf with empty subtrees) or a match between
 *  participants and/or winners from a previous round.  The value
 *  associated with the root of the tree is the basic info for a
 *  participant (if this is a leaf), the information for the match winner,
 *  or blank information for the winner if the match has not taken place
 *  yet.
 *
 *  @author Alyce Brady
 *  Creation Date: Fall 2025
 */


public class BracketTree extends K_RecBinaryTree<ParticipantInfo>
{
    /** Creates an empty bracket tree.
     */
    public BracketTree()
    {
        super();    // Calls zero-parameter K_RecBinaryTree constructor.
    }

    /** Creates a leaf node for the bracket tree.
     *    @param value the value to be attached to the new leaf node
     */
    public BracketTree(ParticipantInfo participant)
    {
        super(participant, new BracketTree(), new BracketTree());
    }

    /** Creates a match between two players.
     *    @param p1 one of the two participants in this match
     *    @param p2 the other of the participants in this match
     */
    public BracketTree(ParticipantInfo p1, ParticipantInfo p2)
    {
        this(new BracketTree(p1), new BracketTree(p2));
    }

    /** Creates a tree with the specified value and the specified trees as
     *  left and right subtrees.
     *    @param m1 one of the two matches whose winner meets in this match
     *    @param m2 the other of the matches whose winner meets in this match
     */
    public BracketTree(BracketTree m1, BracketTree m2)
    {
        super(ParticipantInfo.TBD, m1, m2);
    }

    /** Returns the left bracket sub-tree.  Useful when calling methods that
     *  are new in this class, since leftChild() needs to be cast to this
     *  class in order to call new methods.
     *      @return the left bracket sub-tree.
     */
    public BracketTree leftBracket()
    {
        return (BracketTree) leftChild();
    }

    /** Returns the right bracket sub-tree.  Useful when calling methods that
     *  are new in this class, since rightChild() needs to be cast to this
     *  class in order to call new methods.
     *      @return the right bracket sub-tree.
     */
    public BracketTree rightBracket()
    {
        return (BracketTree) rightChild();
    }

    /** Returns true if the match has been played and has a winner.
     *      @return true if the match has been played; false otherwise
     */
    public boolean hasBeenPlayed()
    {
        return getData() != ParticipantInfo.TBD;
    }

    /** Returns true if the match has not yet been played (there isn't a
     * winner yet).
     *      @return true if the match has not yet been played;
     *              false if it has been played
     */
    public boolean notYetPlayed()
    {
        return getData() == ParticipantInfo.TBD;
    }

    /** Returns true if this tree or subtree represents a bye match.
     *  This method should only be called internally when the current node
     *  has a right child; in other words, when it is neither empty nor a
     *  leaf.
     *      @return true if this tree or subtree represents a bye match;
     *              false otherwise
     */
    private boolean isABye()
    {
        return rightChild().getData() == ParticipantInfo.BYE;
    }

    /** Returns the winner of the current (root) match.
     *      @return the winner of the match, or ParticipantInfo.TBD
     *              if the match has not yet been played
     */
    public ParticipantInfo getWinner()
    {
        return getData();
    }

    /** Sets the data at the root of this sub-bracket to be the winning
     *  participant.
     *      @param winner the participant who won the match
     */
    public void markWinner(ParticipantInfo winner)
    {
/* CODE MISSING */
    }

    /** Marks every bye as having been "won" by the participant with the bye.
     */
    public void handleByes()
    {
        // Base case: the current node is empty or a leaf node.
/* CODE MISSING */

        // Byes are automatically considered a win.
        if ( this.isABye() )
        {
            // Participant with the bye is the "winner."
/* CODE MISSING */
            return;
        }

        // Proceed down the left and right subtrees looking for byes.
/* CODE MISSING */

    }

    /** Finds the given participants and updates their most recent scores.
     *    @param p1  the participant with the higher ranking
     *    @param score1 p1's score in the most recent matchup
     *    @param p2  the participant with the lower ranking
     *    @param score2 p2's score in the most recent matchup
     *    @return true if the match was found and updated; false otherwise
     */
    public boolean updateScore(ParticipantInfo p1, int score1,
                               ParticipantInfo p2, int score2)
    {
        // Find the match with the given participants.  There should be
        // such a match and it should not yet have a winner.
        if ( isEmpty() || hasBeenPlayed() ) // match is not in this subtree
            return false;

        // Do the left and right children match the parameter participants?
        if ( leftChild().getData().equals(p1) &&
                rightChild().getData().equals(p2) )
        {
            // Yes.  Update their most recent scores and set the data field
            // in the root of this sub-tree to the winner of the match.
/* CODE MISSING */

            return true;
        }

        // Haven't found the right match.  Keep looking through left and
        // right sub-trees.  (Don't look in right if found in left!)
/* CODE MISSING -- Only look in right subtree if NOT already found in left. */

/* TEMPORARY CODE to allow program to compile. */
return true;
    }

    /** Prints next round of matchups.
     */
    public void printNextMatchups()
    {
        if ( isEmpty() || hasBeenPlayed() )
            return;

        // Print if this root node represents an unplayed match with
        // well-defined children (leaf nodes or played matches)
        // or if this is the lowest round of matchups.
        if ( ( leftBracket().isLeaf() && rightBracket().isLeaf() ) ||
             ( leftBracket().hasBeenPlayed() &&
               rightBracket().hasBeenPlayed() ) )
        {
            System.out.println(this);
            return;
        }

        leftBracket().printNextMatchups();
        rightBracket().printNextMatchups();
    }

    /** Prints scores from recent matches.
     */
    public void printRecentScores()
    {
        if ( isEmpty() || isLeaf() )    // (Should never get here.)
            return;

        // Print if this root node represents a match that has been played
        // or if this is the lowest round of matchups.
        if ( hasBeenPlayed()
                || ( leftChild().isLeaf() && rightChild().isLeaf() ) )
        {
            System.out.println(this);
            return;
        }

        leftBracket().printRecentScores();
        rightBracket().printRecentScores();
    }

    /** {@inheritDoc}
     *  This method creates a string describing the state of the root
     *  matchup between its left and right children.  The format is
     *  different depending on whether the match=up is a bye, an unplayed
     *  matchup (no winner yet or scores), or a played matchup (includes
     *  winner and score).
     */
    @Override
    public String toString()
    {
        if ( isEmpty() )
            return "";

        if ( isLeaf() )
            return getData().toString();

        // The following three cases should never happen.
        if ( getData() == null )
            return "A bracket has null data";
        else if ( leftChild() == null )
            return "A non-null bracket has a null left child.";
        else if ( rightChild() == null )
            return "A non-null bracket has a null right child.";

        // Format byes differently from actual matches.
        if ( this.isABye() )
            return leftChild().toString() + " " + ParticipantInfo.BYE;

        // For unplayed matches, just show the two participants.  If the
        // match has been played, include scores.
        if ( notYetPlayed() )
            return leftChild().getData() + " vs " + rightChild().getData();
        else
            return getWinner() + " won in (" +
                    leftChild().getData().nameAndScore() + " vs " +
                    rightChild().getData().nameAndScore() + ")";
    }

}
