#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(
                "Размер очереди должен быть больше нуля"
            );
        }

        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("Очередь заполнена");
        }

        data[rearIndex] = value;

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

        ++count;
    }

    // Удаление элемента из начала очереди
    void dequeue() {
        if (isEmpty()) {
            throw std::underflow_error("Очередь пуста");
        }

        // Элементы не сдвигаются, изменяется только индекс начала
        frontIndex = (frontIndex + 1) % capacity;

        --count;
    }

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

        return data[frontIndex];
    }

    const T& front() const {
        if (isEmpty()) {
            throw std::underflow_error("Очередь пуста");
        }

        return data[frontIndex];
    }

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

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

        return data[index];
    }

    const T& back() const {
        if (isEmpty()) {
            throw std::underflow_error("Очередь пуста");
        }

        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 << "Очередь: пуста\n";
            return;
        }

        std::cout << "Очередь: ";

        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 << "Индекс начала (front): " << frontIndex
                  << ", индекс конца (rear): " << rearIndex
                  << ", размер: " << count
                  << ", вместимость: " << capacity
                  << '\n';
    }
};


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

        
        // 1. Начальное состояние
        
        std::cout << "=== 1. Начальное состояние очереди ===\n";

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

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

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

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

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

        std::cout << "Первый элемент: "
                  << queue.front() << '\n';

        std::cout << "Последний элемент: "
                  << queue.back() << '\n';

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

        queue.dequeue();

        std::cout << "После удаления первого элемента:\n";
        queue.print();
        queue.printState();

        // 5. Проверка циклического массива
        std::cout << "\n=== 5. Проверка циклического массива ===\n";

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

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

        std::cout
            << "Освободившаяся позиция массива была использована повторно.\n"
            << "Элементы очереди не сдвигаются.\n";

        // 6. Проверка заполнения очереди
        std::cout << "\n=== 6. Проверка заполнения очереди ===\n";

        queue.print();

        std::cout << "Очередь заполнена: "
                  << (queue.isFull() ? "да" : "нет")
                  << '\n';

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

        try {
            queue.enqueue(70);
        }
        catch (const std::overflow_error& error) {
            std::cout
                << "Переполнение обработано корректно: "
                << error.what() << '\n';
        }

        // 8. Повторное использование освободившихся ячеек
        std::cout
            << "\n=== 8. Повторное использование освободившихся ячеек ===\n";

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

        std::cout << "После удаления двух элементов:\n";
        queue.print();
        queue.printState();

        queue.enqueue(70);
        queue.enqueue(80);

        std::cout << "После добавления новых элементов:\n";
        queue.print();
        queue.printState();

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

        std::cout << "Текущий размер очереди: "
                  << queue.size() << '\n';

        std::cout << "Максимальный размер очереди: "
                  << queue.maxSize() << '\n';

        std::cout << "Очередь пуста: "
                  << (queue.isEmpty() ? "да" : "нет")
                  << '\n';

        std::cout << "Очередь заполнена: "
                  << (queue.isFull() ? "да" : "нет")
                  << '\n';

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

        queue.clear();

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

        std::cout << "Очередь пуста: "
                  << (queue.isEmpty() ? "да" : "нет")
                  << '\n';

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

        try {
            queue.dequeue();
        }
        catch (const std::underflow_error& error) {
            std::cout
                << "Удаление из пустой очереди обработано корректно: "
                << error.what() << '\n';
        }

        // 12. Повторное использование после очистки
        std::cout
            << "\n=== 12. Повторное использование после очистки ===\n";

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

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

        std::cout << "Первый элемент: "
                  << queue.front() << '\n';

        std::cout << "Последний элемент: "
                  << queue.back() << '\n';

        std::cout
            << "\n=== Все проверки успешно завершены ===\n";
    }
    catch (const std::invalid_argument& error) {
        std::cerr
            << "Ошибка при создании очереди: "
            << error.what() << '\n';

        return 1;
    }
    catch (const std::exception& error) {
        std::cerr
            << "Непредвиденная ошибка: "
            << error.what() << '\n';

        return 1;
    }

    return 0;
}