import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Node extends Thread {
    private int id;
    private Node right, left;
    private BlockingQueue<Message> recibidos;
    private int phase;
    private boolean isLeader;
    private boolean terminated;

    public Node(int id) {
        this.id = id;
        this.right = null;
        this.left = null;
        this.recibidos = new LinkedBlockingQueue<>();
        this.phase = 0;
        this.isLeader = false;
        this.terminated = false;
    }

    // Setter methods
    public void setRight(Node right) {
        this.right = right;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    // Receive a message into this node's inbox
    public void receiveMessage(Message msg) {
        recibidos.offer(msg);
    }

    // Get the opposite direction
    private Message.Direction getOppositeDirection(Message.Direction dir) {
        return dir == Message.Direction.RIGHT ? Message.Direction.LEFT : Message.Direction.RIGHT;
    }

    // Send message in a specific direction
    private void sendInDirection(Message msg, Message.Direction dir) {
        if (dir == Message.Direction.RIGHT) {
            right.receiveMessage(msg);
        } else {
            left.receiveMessage(msg);
        }
    }

    @Override
    public void run() {
        // Phase 0: send initial messages in both directions with alcance = 2^0 = 1
        int alcance = (int) Math.pow(2, phase);
        Message outRight = new Message(id, alcance, false, Message.Direction.RIGHT);
        Message outLeft = new Message(id, alcance, false, Message.Direction.LEFT);
        right.receiveMessage(outRight);
        left.receiveMessage(outLeft);

        try {
            while (!terminated) {
                // Process all incoming messages
                Message msg = recibidos.take();

                // Check for termination token
                if (msg.getId() == -1) {
                    terminated = true;
                    // Forward termination to neighbors
                    right.receiveMessage(new Message(-1, 0, true, Message.Direction.RIGHT));
                    left.receiveMessage(new Message(-1, 0, true, Message.Direction.LEFT));
                    break;
                }

                // Handle the message based on type
                if (msg.isIn()) {
                    // This is a reply coming back - just note it
                    // We need 2 replies (from both directions) before advancing phase
                    // For simplicity, we'll track this implicitly
                } else {
                    // This is an outbound message from another node
                    if (msg.getId() > id) {
                        // Forward or bounce back
                        if (msg.getAlcance() > 1) {
                            // Decrement alcance and forward in SAME direction
                            Message forward = new Message(msg.getId(), msg.getAlcance() - 1, false, msg.getDirection());
                            sendInDirection(forward, msg.getDirection());
                        } else {
                            // alcance reached 0, send back as reply in OPPOSITE direction
                            Message.Direction oppositeDir = getOppositeDirection(msg.getDirection());
                            Message reply = new Message(msg.getId(), 0, true, oppositeDir);
                            sendInDirection(reply, oppositeDir);
                        }
                    } else if (msg.getId() < id) {
                        // Discard smaller ID and send back my ID as reply in OPPOSITE direction
                        Message.Direction oppositeDir = getOppositeDirection(msg.getDirection());
                        Message reply = new Message(id, 0, true, oppositeDir);
                        sendInDirection(reply, oppositeDir);
                    } else {
                        // msg.getId() == id: my message came all the way around!
                        isLeader = true;
                        System.out.println("Node " + id + " becomes LEADER");
                        terminated = true;
                        right.receiveMessage(new Message(-1, 0, true, Message.Direction.RIGHT));
                        left.receiveMessage(new Message(-1, 0, true, Message.Direction.LEFT));
                        break;
                    }
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    @Override
    public String toString() {
        return "Node " + id + " [phase=" + phase + ", isLeader=" + isLeader + ", terminated=" + terminated + "]";
    }
}
