// See the file "COPYING" in the main distribution directory for copyright.

#pragma once

#include <cmath>
#include <cstdint>

namespace zeek::detail {

class PriorityQueue;

class PQ_Element {
public:
    explicit PQ_Element(double t) : time(t) {}
    virtual ~PQ_Element() = default;

    double Time() const { return time; }

    int Offset() const { return offset; }
    void SetOffset(int off) { offset = off; }

    void MinimizeTime() { time = -HUGE_VAL; }

protected:
    PQ_Element() = default;
    double time = 0.0;
    int offset = -1;
};

class PriorityQueue {
public:
    explicit PriorityQueue(int initial_size = 16);
    ~PriorityQueue();

    // Returns the top of queue, or nil if the queue is empty.
    PQ_Element* Top() const {
        if ( heap_size == 0 )
            return nullptr;

        return heap[0];
    }

    // Removes (and returns) top of queue.  Returns nil if the queue
    // is empty.
    PQ_Element* Remove();

    // Removes element e.  Returns e, or nullptr if e wasn't in the queue.
    // Note that e will be modified via MinimizeTime().
    PQ_Element* Remove(PQ_Element* e);

    // Add a new element to the queue.  Returns false on failure (not enough
    // memory to add the element), true on success.
    bool Add(PQ_Element* e);

    int Size() const { return heap_size; }
    int PeakSize() const { return peak_heap_size; }
    uint64_t CumulativeNum() const { return cumulative_num; }

protected:
    bool Resize(int new_size);

    void BubbleUp(int bin);
    void BubbleDown(int bin);

    int Parent(int bin) const { return bin >> 1; }

    int LeftChild(int bin) const { return bin << 1; }

    int RightChild(int bin) const { return LeftChild(bin) + 1; }

    void SetElement(int bin, PQ_Element* e) {
        heap[bin] = e;
        e->SetOffset(bin);
    }

    void Swap(int bin1, int bin2) {
        PQ_Element* t = heap[bin1];
        SetElement(bin1, heap[bin2]);
        SetElement(bin2, t);
    }

    PQ_Element** heap = nullptr;
    int heap_size = 0;
    int peak_heap_size = 0;
    int max_heap_size = 0;
    uint64_t cumulative_num = 0;
};

} // namespace zeek::detail
