#pragma once

#include <iostream>
#include <stdexcept>
#include "doubly_linked_list.h"

template <typename T>
class Queue {
    private:
        DoublyLinkedList<T> m_data;
    
    public:
        Queue() : m_data() {}

        Queue(const Queue& queue)
        : m_data(queue.items()) {
        }

        ~Queue() {
            m_data.clear();
        }

        Queue& operator=(const Queue& rhs) {
            if (&rhs == this)
                return *this;

            m_data = rhs.items();
            return *this;
        }

        void enqueue(const T& item) {
            m_data.push_back(item);
        }

        T dequeue() {
            if (m_data.size() == 0)
                throw std::out_of_range("cannot dequeue empty queue");

            T res = m_data[0];  
            m_data.remove(0);
            return res;
        }

        T& front() {
            if (m_data.size() == 0)
                throw std::out_of_range("cannot dequeue empty queue");
            
            return m_data[0];
        }

        const DoublyLinkedList<T>& items() const {
            return m_data;
        }

        std::size_t size() const {
            return m_data.size();
        }

        void queue_print() const {
            m_data.print();
        }
};