#include <iostream>
#include <stdexcept>
#include <cstddef>

template <typename T>
class CircularQueue {
private:
    T* data;                    // Массив для хранения элементов
    std::size_t capacity;       // Максимальная вместимость
    std::size_t frontIndex;     // Индекс первого элемента
    std::size_t rearIndex;      // Индекс следующей свободной позиции
    std::size_t count;          // Текущее количество элементов

public:
    // Конструктор
    explicit CircularQueue(std::size_t capacity)
        : data(nullptr),
          capacity(capacity),
          frontIndex(0),
          rearIndex(0),
          count(0) {

        if (capacity == 0) {
            throw std::invalid_argument(
                "Capacity must be greater than 0"
            );
        }

        data = new T[capacity];
    }

    // Деструктор
    ~CircularQueue() {
        delete[] data;
    }

    // Запрещаем копирование, чтобы избежать двойного освобождения памяти
    CircularQueue(const CircularQueue&) = delete;
    CircularQueue& operator=(const CircularQueue&) = delete;

    // Проверка на пустоту
    bool isEmpty() const noexcept {
        return count == 0;
    }

    // Проверка на заполненность
    bool isFull() const noexcept {
        return count == capacity;
    }

    // Текущий размер очереди
    std::size_t size() const noexcept {
        return count;
    }

    // Максимальный размер очереди
    std::size_t maxSize() const noexcept {
        return capacity;
    }

    // Добавление элемента в конец очереди
    void enqueue(const T& value) {
        if (isFull()) {
            throw std::overflow_error("Queue is full");
        }

        data[rearIndex] = value;

        // Циклический переход к следующей позиции
        rearIndex = (rearIndex + 1) % capacity;

        ++count;
    }

    // Удаление элемента из начала очереди
    void dequeue() {
        if (isEmpty()) {
            throw std::underflow_error("Queue is empty");
        }

        // Просто перемещаем индекс начала.
        // Сами элементы массива не сдвигаются.
        frontIndex = (frontIndex + 1) % capacity;

        --count;
    }

    // Получение первого элемента
    T& front() {
        if (isEmpty()) {
            throw std::underflow_error("Queue is empty");
        }

        return data[frontIndex];
    }

    const T& front() const {
        if (isEmpty()) {
            throw std::underflow_error("Queue is empty");
        }

        return data[frontIndex];
    }

    // Получение последнего элемента
    T& back() {
        if (isEmpty()) {
            throw std::underflow_error("Queue is empty");
        }

        std::size_t index =
            (rearIndex + capacity - 1) % capacity;

        return data[index];
    }

    const T& back() const {
        if (isEmpty()) {
            throw std::underflow_error("Queue is empty");
        }

        std::size_t index =
            (rearIndex + capacity - 1) % capacity;

        return data[index];
    }

    // Очистка очереди
    void clear() noexcept {
        frontIndex = 0;
        rearIndex = 0;
        count = 0;
    }

    // Вывод содержимого очереди
    void print() const {
        if (isEmpty()) {
            std::cout << "Queue: empty\n";
            return;
        }

        std::cout << "Queue: ";

        for (std::size_t i = 0; i < count; ++i) {
            std::size_t index =
                (frontIndex + i) % capacity;

            std::cout << data[index];

            if (i + 1 < count) {
                std::cout << " ";
            }
        }

        std::cout << '\n';
    }

    // Вывод внутреннего состояния для демонстрации
    // работы циклического массива
    void printState() const {
        std::cout << "frontIndex = " << frontIndex
                  << ", rearIndex = " << rearIndex
                  << ", size = " << count
                  << ", capacity = " << capacity
                  << '\n';
    }
};


int main() {
    try {
        // Создание циклической очереди вместимостью 5 элементов
        CircularQueue<int> queue(5);

        std::cout << "=== 1. Initial state ===\n";
        queue.print();
        queue.printState();

        // ---------------------------------------------------------
        // 2. Добавление элементов
        // ---------------------------------------------------------
        std::cout << "\n=== 2. Enqueue ===\n";

        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);

        queue.print();
        queue.printState();

        // ---------------------------------------------------------
        // 3. Просмотр первого и последнего элемента
        // ---------------------------------------------------------
        std::cout << "\n=== 3. Front / Back ===\n";

        std::cout << "Front: " << queue.front() << '\n';
        std::cout << "Back: " << queue.back() << '\n';

        // ---------------------------------------------------------
        // 4. Удаление элементов
        // ---------------------------------------------------------
        std::cout << "\n=== 4. Dequeue ===\n";

        queue.dequeue();

        std::cout << "After dequeue:\n";
        queue.print();
        queue.printState();

        // ---------------------------------------------------------
        // 5. Демонстрация циклического использования массива
        // ---------------------------------------------------------
        std::cout << "\n=== 5. Circular array test ===\n";

        queue.enqueue(40);
        queue.enqueue(50);
        queue.enqueue(60);

        queue.print();
        queue.printState();

        /*
         * После удаления первого элемента освободилась первая
         * позиция массива. Благодаря оператору %
         * rearIndex продолжает движение циклически и использует
         * освободившееся место, не сдвигая остальные элементы.
         */

        // ---------------------------------------------------------
        // 6. Заполнение очереди до максимальной вместимости
        // ---------------------------------------------------------
        std::cout << "\n=== 6. Full queue test ===\n";

        // Сейчас в очереди 5 элементов
        queue.print();

        std::cout << "Is full: "
                  << (queue.isFull() ? "yes" : "no")
                  << '\n';

        // ---------------------------------------------------------
        // 7. Проверка переполнения
        // ---------------------------------------------------------
        std::cout << "\n=== 7. Overflow test ===\n";

        try {
            queue.enqueue(70);
        }
        catch (const std::overflow_error& error) {
            std::cout << "Overflow handled correctly: "
                      << error.what() << '\n';
        }

        // ---------------------------------------------------------
        // 8. Проверка удаления и повторного циклического добавления
        // ---------------------------------------------------------
        std::cout << "\n=== 8. Reuse freed position ===\n";

        queue.dequeue();
        queue.dequeue();

        queue.print();
        queue.printState();

        // Добавляем элементы в освободившиеся позиции
        queue.enqueue(70);
        queue.enqueue(80);

        queue.print();
        queue.printState();

        // ---------------------------------------------------------
        // 9. Проверка размера очереди
        // ---------------------------------------------------------
        std::cout << "\n=== 9. Size test ===\n";

        std::cout << "Current size: "
                  << queue.size() << '\n';

        std::cout << "Maximum size: "
                  << queue.maxSize() << '\n';

        // ---------------------------------------------------------
        // 10. Очистка очереди
        // ---------------------------------------------------------
        std::cout << "\n=== 10. Clear ===\n";

        queue.clear();

        queue.print();
        queue.printState();

        std::cout << "Is empty: "
                  << (queue.isEmpty() ? "yes" : "no")
                  << '\n';

        // ---------------------------------------------------------
        // 11. Проверка удаления из пустой очереди
        // ---------------------------------------------------------
        std::cout << "\n=== 11. Underflow test ===\n";

        try {
            queue.dequeue();
        }
        catch (const std::underflow_error& error) {
            std::cout << "Underflow handled correctly: "
                      << error.what() << '\n';
        }

        // ---------------------------------------------------------
        // 12. Проверка добавления после очистки
        // ---------------------------------------------------------
        std::cout << "\n=== 12. Reuse after clear ===\n";

        queue.enqueue(100);
        queue.enqueue(200);

        queue.print();

        std::cout << "Front: " << queue.front() << '\n';
        std::cout << "Back: " << queue.back() << '\n';

        std::cout << "\n=== All tests completed successfully ===\n";
    }
    catch (const std::invalid_argument& error) {
        std::cerr << "Invalid argument: "
                  << error.what() << '\n';

        return 1;
    }
    catch (const std::exception& error) {
        std::cerr << "Unexpected error: "
                  << error.what() << '\n';

        return 1;
    }

    return 0;
}