/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#ifndef PRIORITIZEDDEQUE_H
#define PRIORITIZEDDEQUE_H

#include <deque>
#include <memory>
#include <string>
#include <vector>
#include <sstream>
#include <functional>
#include <algorithm>
#include <stdexcept>
#include <functional>

namespace omnistream {

template <typename T>
class PrioritizedDeque {
public:
    PrioritizedDeque();
    ~PrioritizedDeque();

    void addPriorityElement(std::shared_ptr<T> element);
    void add(std::shared_ptr<T> element);
    void add(std::shared_ptr<T> element, bool priority, bool prioritize);
    void prioritize(std::shared_ptr<T> element);
    std::vector<std::shared_ptr<T>> asUnmodifiableCollection() const;
    std::shared_ptr<T> getAndRemove(std::function<bool(const std::shared_ptr<T>&)> preCondition);
    std::shared_ptr<T> poll();
    std::shared_ptr<T> peek() const;
    int getNumPriorityElements() const;
    bool containsPriorityElement(std::shared_ptr<T> element) const;
    int size() const;
    int getNumUnprioritizedElements() const;
    typename std::vector<std::shared_ptr<T>>::const_iterator iterator() const;
    void clear();
    bool isEmpty() const;
    bool contains(std::shared_ptr<T> element) const;
    bool equals(const PrioritizedDeque<T>& other) const;
    int hashCode() const;
    std::string toString() const;
    std::shared_ptr<T> peekLast() const;
private:
    std::deque<std::shared_ptr<T>> deque;
    int numPriorityElements;
};

template <typename T>
PrioritizedDeque<T>::PrioritizedDeque() : numPriorityElements(0) {}

template <typename T>
PrioritizedDeque<T>::~PrioritizedDeque() {}

template <typename T>
void PrioritizedDeque<T>::addPriorityElement(std::shared_ptr<T> element)
{
    if (numPriorityElements == 0) {
        deque.push_front(element);
    } else if (static_cast<size_t>(numPriorityElements) == deque.size()) {
        deque.push_back(element);
    } else {
        std::deque<std::shared_ptr<T>> priorPriority;
        for (int index = 0; index < numPriorityElements; index++) {
            priorPriority.push_front(deque.front());
            deque.pop_front();
        }
        deque.push_front(element);
        for (const auto& priorityEvent : priorPriority) {
            deque.push_front(priorityEvent);
        }
    }
    numPriorityElements++;
}

template <typename T>
void PrioritizedDeque<T>::add(std::shared_ptr<T> element)
{
    deque.push_back(element);
}

template <typename T>
void PrioritizedDeque<T>::add(std::shared_ptr<T> element, bool priority, bool _prioritize)
{
    if (!priority) {
        add(element);
    } else {
        if (_prioritize) {
            prioritize(element);
        } else {
            addPriorityElement(element);
        }
    }
}

template <typename T>
void PrioritizedDeque<T>::prioritize(std::shared_ptr<T> element)
{
    auto it = deque.begin();
    for (int i = 0; i < numPriorityElements && it != deque.end(); ++i, ++it) {
        if (*it == element) {
            return;
        }
    }
    if (it != deque.end() && *it == element) {
        numPriorityElements++;
        return;
    }
    it = std::find(deque.begin(), deque.end(), element);
    if (it != deque.end()) {
        deque.erase(it);
    }
    addPriorityElement(element);
}

template <typename T>
std::vector<std::shared_ptr<T>> PrioritizedDeque<T>::asUnmodifiableCollection() const
{
    return std::vector<std::shared_ptr<T>>(deque.begin(), deque.end());
}

template <typename T>
std::shared_ptr<T> PrioritizedDeque<T>::getAndRemove(std::function<bool(const std::shared_ptr<T>&)> preCondition)
{
    auto it = deque.begin();
    for (int i = 0; it != deque.end(); ++i, ++it) {
        if (preCondition(*it)) {
            if (i < numPriorityElements) {
                numPriorityElements--;
            }
            std::shared_ptr<T> result = *it;
            deque.erase(it);
            return result;
        }
    }
    throw std::out_of_range("Element not found");
}

template <typename T>
std::shared_ptr<T> PrioritizedDeque<T>::poll()
{
    if (deque.empty()) {
        return nullptr;
    }
    std::shared_ptr<T> polled = deque.front();
    deque.pop_front();
    if (numPriorityElements > 0) {
        numPriorityElements--;
    }
    return polled;
}

template <typename T>
std::shared_ptr<T> PrioritizedDeque<T>::peek() const
{
    if (deque.empty()) {
        return nullptr;
    }
    return deque.front();
}

template <typename T>
int PrioritizedDeque<T>::getNumPriorityElements() const
{
    return numPriorityElements;
}

template <typename T>
bool PrioritizedDeque<T>::containsPriorityElement(std::shared_ptr<T> element) const
{
    if (numPriorityElements == 0) {
        return false;
    }
    auto it = deque.begin();
    for (int i = 0; i < numPriorityElements && it != deque.end(); ++i, ++it) {
        if (*it == element) {
            return true;
        }
    }
    return false;
}

template <typename T>
int PrioritizedDeque<T>::size() const
{
    return deque.size();
}

template <typename T>
int PrioritizedDeque<T>::getNumUnprioritizedElements() const
{
    return size() - getNumPriorityElements();
}

template <typename T>
typename std::vector<std::shared_ptr<T>>::const_iterator PrioritizedDeque<T>::iterator() const
{
    return deque.begin();
}

template <typename T>
void PrioritizedDeque<T>::clear()
{
    deque.clear();
    numPriorityElements = 0;
}

template <typename T>
bool PrioritizedDeque<T>::isEmpty() const
{
    return deque.empty();
}

template <typename T>
bool PrioritizedDeque<T>::contains(std::shared_ptr<T> element) const
{
    return std::find(deque.begin(), deque.end(), element) != deque.end();
}

template <typename T>
bool PrioritizedDeque<T>::equals(const PrioritizedDeque<T>& other) const
{
    return numPriorityElements == other.numPriorityElements && deque == other.deque;
}

template <typename T>
int PrioritizedDeque<T>::hashCode() const
{
    size_t seed = 0;
    for (const auto& item : deque) {
        seed ^= std::hash<std::shared_ptr<T>>{}(item) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    }
    seed ^= std::hash<int>{}(numPriorityElements) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    return static_cast<int>(seed);
}

template <typename T>
std::string PrioritizedDeque<T>::toString() const
{
    std::stringstream ss;
    ss << "[";
    for (auto it = deque.begin(); it != deque.end(); ++it) {
        if (it != deque.begin()) {
            ss << ", ";
        }
        if (*it) {
            ss << (**it);
        } else {
            ss << "nullptr";
        }
    }
    ss << "]";
    return ss.str();
}

template <typename T>
std::shared_ptr<T> PrioritizedDeque<T>::peekLast() const
{
    if (deque.empty()) {
        return nullptr;
    }
    return deque.back();
}

} // namespace omnistream

#endif // PRIORITIZEDDE