#pragma once

#include <string>
#include <vector>
#include <memory>
#include "glm/vec3.hpp"
#include "string_utils.h"
#include <istream>
#include <regex>
#include <iostream>
#include <array>
#include <cstring>
#include <cmath>
#include "gcode_commands.h"

namespace grbl {

    // direct translation from OpenCNCPilot

    enum class parse_distance_mode {
        absolute,
        incremental
    };

    enum class parse_unit {
        metric,
        imperial
    };

    struct parser_state {
        glm::vec<3, double> position = {0, 0, 0};

        // true if the position for this coordinate was previously specified in absolute terms, to prevent the start point of (0, 0, 0) to influence the output file
        bool position_valid[3] = {false, false, false};

        arc_plane plane = arc_plane::xy;
        double feed = 0;
        parse_distance_mode distance_mode = parse_distance_mode::absolute;
        parse_distance_mode arc_distance_mode = parse_distance_mode::incremental;
        parse_unit unit = parse_unit::metric;
        int last_motion_mode = -1;
    };


    struct parse_exception : public std::exception {
        parse_exception(const std::string &reason, size_t lineNumber);
        const char *what() const noexcept override;

        std::string reason;
        size_t line_number;
    };

    struct grbl_parser {
        parser_state state;
        std::vector<command *> commands;
        std::vector<std::string> warnings;

        grbl_parser();
        void reset();
        void parse(std::istream &in);
        void parse(std::string line, int line_number);

    private:
        static std::string cleanup_line(std::string line, int line_number);
        static bool is_motion_command(double param);
    };

}
