#pragma once
#include <exception>
#include <optional>
#include "type.hpp"
#include "location.hh"
#include "parse_driver.hpp"

using maybe_location = std::optional<yy::location>;

class compiler_error : std::exception {
    private:
        std::string description;
        maybe_location loc;

    public:
        compiler_error(std::string d, maybe_location l = std::nullopt)
            : description(std::move(d)), loc(std::move(l)) {}

        const char* what() const noexcept override;

        void print_about(std::ostream& to);
        void print_location(std::ostream& to, file_mgr& fm, bool highlight = false);

        void pretty_print(std::ostream& to, file_mgr& fm);
};

class type_error : compiler_error {
    private:

    public:
        type_error(std::string d, maybe_location l = std::nullopt)
            : compiler_error(std::move(d), std::move(l)) {}

        const char* what() const noexcept override;
        void pretty_print(std::ostream& to, file_mgr& fm);
};

class unification_error : public type_error {
    private:
        type_ptr left;
        type_ptr right;

    public:
        unification_error(type_ptr l, type_ptr r, maybe_location loc = std::nullopt)
            : left(std::move(l)), right(std::move(r)), 
            type_error("failed to unify types", std::move(loc)) {}

        void pretty_print(std::ostream& to, file_mgr& fm, type_mgr& mgr);
};
