#include <cstdarg>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <vector>

#define private public
#include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private

#include "emc/rs274ngc/rs274ngc_return.hh"
#include "canon_event_sink.hh"
#include "linuxcnc_hal_adapter.hh"
#include "linuxcnc_runtime_state.hh"
#include "linuxcnc_tool_adapter.hh"
#include "nml_intf/emc.hh"

namespace standalone {

static std::vector<std::string> g_canon_events;
static double g_external_feed_rate = 0.0;
static int g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
static std::string g_parameter_file_name;

void reset_canon_events()
{
    g_canon_events.clear();
}

void push_canon_event(const std::string &event)
{
    g_canon_events.push_back(event);
}

const std::vector<std::string> &canon_events()
{
    return g_canon_events;
}

void reset_external_axis_mask()
{
    g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
}

void set_external_axis_mask(int axis_mask)
{
    g_external_axis_mask = axis_mask == 0
        ? (Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z)
        : axis_mask;
}

int external_axis_mask()
{
    return g_external_axis_mask;
}

void reset_parameter_file_name()
{
    g_parameter_file_name.clear();
}

void set_parameter_file_name(const char *filename)
{
    g_parameter_file_name = filename ? filename : "";
}

void get_parameter_file_name(char *filename, int max_size)
{
    if (!filename || max_size <= 0) {
        return;
    }
    std::snprintf(filename, static_cast<std::size_t>(max_size), "%s",
                  g_parameter_file_name.c_str());
}

}  // namespace standalone

bool GET_BLOCK_DELETE(void) { return false; }
void FINISH(void) { standalone::push_canon_event("FINISH"); }
void ON_RESET(void) { standalone::push_canon_event("ON_RESET"); }

USER_DEFINED_FUNCTION_TYPE USER_DEFINED_FUNCTION[USER_DEFINED_FUNCTION_NUM] = {};

int USER_DEFINED_FUNCTION_ADD(USER_DEFINED_FUNCTION_TYPE func, int num)
{
    if ((num < 0) || (num >= USER_DEFINED_FUNCTION_NUM)) {
        return -1;
    }
    USER_DEFINED_FUNCTION[num] = func;
    return num;
}

void INIT_CANON() { standalone::push_canon_event("INIT_CANON"); }
void SET_G5X_OFFSET(int index,
                    double x, double y, double z,
                    double a, double b, double c,
                    double u, double v, double w)
{
    std::ostringstream oss;
    oss << "SET_G5X_OFFSET index=" << index
        << " x=" << x
        << " y=" << y
        << " z=" << z
        << " a=" << a
        << " b=" << b
        << " c=" << c
        << " u=" << u
        << " v=" << v
        << " w=" << w;
    standalone::push_canon_event(oss.str());
}
void SET_G92_OFFSET(double x, double y, double z,
                    double a, double b, double c,
                    double u, double v, double w)
{
    std::ostringstream oss;
    oss << "SET_G92_OFFSET"
        << " x=" << x
        << " y=" << y
        << " z=" << z
        << " a=" << a
        << " b=" << b
        << " c=" << c
        << " u=" << u
        << " v=" << v
        << " w=" << w;
    standalone::push_canon_event(oss.str());
}
void SET_XY_ROTATION(double rotation)
{
    std::ostringstream oss;
    oss << "SET_XY_ROTATION rotation=" << rotation;
    standalone::push_canon_event(oss.str());
}
void CANON_UPDATE_END_POINT(double x, double y, double z,
                            double a, double b, double c,
                            double u, double v, double w)
{
    std::ostringstream oss;
    oss << "CANON_UPDATE_END_POINT"
        << " x=" << x
        << " y=" << y
        << " z=" << z
        << " a=" << a
        << " b=" << b
        << " c=" << c
        << " u=" << u
        << " v=" << v
        << " w=" << w;
    standalone::push_canon_event(oss.str());
}
void USE_LENGTH_UNITS(CANON_UNITS units)
{
    std::ostringstream oss;
    oss << "USE_LENGTH_UNITS units=" << static_cast<int>(units);
    standalone::push_canon_event(oss.str());
}
void SELECT_PLANE(CANON_PLANE plane)
{
    std::ostringstream oss;
    oss << "SELECT_PLANE plane=" << static_cast<int>(plane);
    standalone::push_canon_event(oss.str());
}
void SET_TRAVERSE_RATE(double rate)
{
    std::ostringstream oss;
    oss << "SET_TRAVERSE_RATE rate=" << rate;
    standalone::push_canon_event(oss.str());
}
void SET_FEED_REFERENCE(CANON_FEED_REFERENCE reference)
{
    std::ostringstream oss;
    oss << "SET_FEED_REFERENCE reference=" << static_cast<int>(reference);
    standalone::push_canon_event(oss.str());
}
void SET_FEED_MODE(int spindle, int mode)
{
    std::ostringstream oss;
    oss << "SET_FEED_MODE spindle=" << spindle << " mode=" << mode;
    standalone::push_canon_event(oss.str());
}
void SET_MOTION_CONTROL_MODE(CANON_MOTION_MODE mode, double tolerance)
{
    std::ostringstream oss;
    oss << "SET_MOTION_CONTROL_MODE mode=" << mode << " tolerance=" << tolerance;
    standalone::push_canon_event(oss.str());
}
void SET_NAIVECAM_TOLERANCE(double tolerance)
{
    std::ostringstream oss;
    oss << "SET_NAIVECAM_TOLERANCE tolerance=" << tolerance;
    standalone::push_canon_event(oss.str());
}
void SET_CUTTER_RADIUS_COMPENSATION(double) {}
void START_CUTTER_RADIUS_COMPENSATION(int) {}
void STOP_CUTTER_RADIUS_COMPENSATION() {}
void START_SPEED_FEED_SYNCH(int spindle, double feed_per_revolution, bool velocity_mode)
{
    std::ostringstream oss;
    oss << "START_SPEED_FEED_SYNCH spindle=" << spindle
        << " feed_per_revolution=" << feed_per_revolution
        << " velocity_mode=" << velocity_mode;
    standalone::push_canon_event(oss.str());
}
void STOP_SPEED_FEED_SYNCH() { standalone::push_canon_event("STOP_SPEED_FEED_SYNCH"); }
void RIGID_TAP(int lineno, double x, double y, double z, double scale)
{
    std::ostringstream oss;
    oss << "RIGID_TAP line=" << lineno
        << " x=" << x
        << " y=" << y
        << " z=" << z
        << " scale=" << scale;
    standalone::push_canon_event(oss.str());
}
void STRAIGHT_PROBE(int lineno,
                    double x, double y, double z,
                    double a, double b, double c,
                    double u, double v, double w,
                    unsigned char probe_type)
{
    std::ostringstream oss;
    oss << "STRAIGHT_PROBE line=" << lineno
        << " x=" << x
        << " y=" << y
        << " z=" << z
        << " a=" << a
        << " b=" << b
        << " c=" << c
        << " u=" << u
        << " v=" << v
        << " w=" << w
        << " probe_type=" << static_cast<int>(probe_type);
    standalone::push_canon_event(oss.str());
}
void STOP() {}
void DWELL(double seconds)
{
    std::ostringstream oss;
    oss << "DWELL seconds=" << seconds;
    standalone::push_canon_event(oss.str());
}
void SET_SPINDLE_MODE(int spindle, double css_max)
{
    std::ostringstream oss;
    oss << "SET_SPINDLE_MODE spindle=" << spindle << " css_max=" << css_max;
    standalone::push_canon_event(oss.str());
}
void SPINDLE_RETRACT_TRAVERSE() {}
void START_SPINDLE_CLOCKWISE(int spindle, int wait_for_at_speed)
{
    std::ostringstream oss;
    oss << "START_SPINDLE_CLOCKWISE spindle=" << spindle
        << " wait_for_at_speed=" << wait_for_at_speed;
    standalone::push_canon_event(oss.str());
}
void START_SPINDLE_COUNTERCLOCKWISE(int spindle, int wait_for_at_speed)
{
    std::ostringstream oss;
    oss << "START_SPINDLE_COUNTERCLOCKWISE spindle=" << spindle
        << " wait_for_at_speed=" << wait_for_at_speed;
    standalone::push_canon_event(oss.str());
}
void SET_SPINDLE_SPEED(int spindle, double speed)
{
    std::ostringstream oss;
    oss << "SET_SPINDLE_SPEED spindle=" << spindle << " speed=" << speed;
    standalone::push_canon_event(oss.str());
}
void STOP_SPINDLE_TURNING(int spindle)
{
    std::ostringstream oss;
    oss << "STOP_SPINDLE_TURNING spindle=" << spindle;
    standalone::push_canon_event(oss.str());
}
void SPINDLE_RETRACT() {}
void ORIENT_SPINDLE(int spindle, double orientation, int mode)
{
    std::ostringstream oss;
    oss << "ORIENT_SPINDLE spindle=" << spindle
        << " orientation=" << orientation
        << " mode=" << mode;
    standalone::push_canon_event(oss.str());
}
void WAIT_SPINDLE_ORIENT_COMPLETE(int spindle, double timeout)
{
    std::ostringstream oss;
    oss << "WAIT_SPINDLE_ORIENT_COMPLETE spindle=" << spindle
        << " timeout=" << timeout;
    standalone::push_canon_event(oss.str());
}
void LOCK_SPINDLE_Z() {}
void USE_SPINDLE_FORCE() {}
void USE_NO_SPINDLE_FORCE() {}
void SET_TOOL_TABLE_ENTRY(int pocket, int toolno, const EmcPose &offset, double diameter,
                          double frontangle, double backangle, int orientation)
{
    std::ostringstream oss;
    oss << "SET_TOOL_TABLE_ENTRY pocket=" << pocket
        << " toolno=" << toolno
        << " x=" << offset.tran.x
        << " y=" << offset.tran.y
        << " z=" << offset.tran.z
        << " a=" << offset.a
        << " b=" << offset.b
        << " c=" << offset.c
        << " u=" << offset.u
        << " v=" << offset.v
        << " w=" << offset.w
        << " diameter=" << diameter
        << " frontangle=" << frontangle
        << " backangle=" << backangle
        << " orientation=" << orientation;
    standalone::push_canon_event(oss.str());
}
void USE_TOOL_LENGTH_OFFSET(const EmcPose &offset)
{
    std::ostringstream oss;
    oss << "USE_TOOL_LENGTH_OFFSET"
        << " x=" << offset.tran.x
        << " y=" << offset.tran.y
        << " z=" << offset.tran.z
        << " a=" << offset.a
        << " b=" << offset.b
        << " c=" << offset.c
        << " u=" << offset.u
        << " v=" << offset.v
        << " w=" << offset.w;
    standalone::push_canon_event(oss.str());
}
void CHANGE_TOOL()
{
    standalone::change_selected_tool();
    standalone::push_canon_event("CHANGE_TOOL");
}
void SELECT_TOOL(int tool)
{
    standalone::select_tool(tool);
    std::ostringstream oss;
    oss << "SELECT_TOOL tool=" << tool;
    standalone::push_canon_event(oss.str());
}
void CHANGE_TOOL_NUMBER(int pocket)
{
    standalone::change_tool_number(pocket);
    std::ostringstream oss;
    oss << "CHANGE_TOOL_NUMBER pocket=" << pocket;
    standalone::push_canon_event(oss.str());
}
void RELOAD_TOOLDATA(void) { standalone::push_canon_event("RELOAD_TOOLDATA"); }
void CLAMP_AXIS(CANON_AXIS) {}
void DISABLE_ADAPTIVE_FEED() { standalone::push_canon_event("DISABLE_ADAPTIVE_FEED"); }
void ENABLE_ADAPTIVE_FEED() { standalone::push_canon_event("ENABLE_ADAPTIVE_FEED"); }
void DISABLE_FEED_OVERRIDE() { standalone::push_canon_event("DISABLE_FEED_OVERRIDE"); }
void ENABLE_FEED_OVERRIDE() { standalone::push_canon_event("ENABLE_FEED_OVERRIDE"); }
void DISABLE_SPEED_OVERRIDE(int spindle)
{
    std::ostringstream oss;
    oss << "DISABLE_SPEED_OVERRIDE spindle=" << spindle;
    standalone::push_canon_event(oss.str());
}
void ENABLE_SPEED_OVERRIDE(int spindle)
{
    std::ostringstream oss;
    oss << "ENABLE_SPEED_OVERRIDE spindle=" << spindle;
    standalone::push_canon_event(oss.str());
}
void DISABLE_FEED_HOLD() { standalone::push_canon_event("DISABLE_FEED_HOLD"); }
void ENABLE_FEED_HOLD() { standalone::push_canon_event("ENABLE_FEED_HOLD"); }
void FLOOD_OFF() { standalone::push_canon_event("FLOOD_OFF"); }
void FLOOD_ON() { standalone::push_canon_event("FLOOD_ON"); }
void MIST_OFF() { standalone::push_canon_event("MIST_OFF"); }
void MIST_ON() { standalone::push_canon_event("MIST_ON"); }
void PALLET_SHUTTLE() { standalone::push_canon_event("PALLET_SHUTTLE"); }
void TURN_PROBE_OFF() { standalone::push_canon_event("TURN_PROBE_OFF"); }
void TURN_PROBE_ON() { standalone::push_canon_event("TURN_PROBE_ON"); }
void UNCLAMP_AXIS(CANON_AXIS) {}
void NURB_KNOT_VECTOR() {}
void NURB_CONTROL_POINT(int, double, double, double, double) {}
void NURB_FEED(double, double) {}
void SET_BLOCK_DELETE(bool) {}
void OPTIONAL_PROGRAM_STOP() { standalone::push_canon_event("OPTIONAL_PROGRAM_STOP"); }
void SET_OPTIONAL_PROGRAM_STOP(bool) {}
bool GET_OPTIONAL_PROGRAM_STOP() { return false; }
void PROGRAM_END() { standalone::push_canon_event("PROGRAM_END"); }
void PROGRAM_STOP() { standalone::push_canon_event("PROGRAM_STOP"); }
void SET_MOTION_OUTPUT_BIT(int index)
{
    std::ostringstream oss;
    oss << "SET_MOTION_OUTPUT_BIT index=" << index;
    standalone::push_canon_event(oss.str());
}
void CLEAR_MOTION_OUTPUT_BIT(int index)
{
    std::ostringstream oss;
    oss << "CLEAR_MOTION_OUTPUT_BIT index=" << index;
    standalone::push_canon_event(oss.str());
}
void SET_AUX_OUTPUT_BIT(int index)
{
    std::ostringstream oss;
    oss << "SET_AUX_OUTPUT_BIT index=" << index;
    standalone::push_canon_event(oss.str());
}
void CLEAR_AUX_OUTPUT_BIT(int index)
{
    std::ostringstream oss;
    oss << "CLEAR_AUX_OUTPUT_BIT index=" << index;
    standalone::push_canon_event(oss.str());
}
void SET_MOTION_OUTPUT_VALUE(int index, double value)
{
    std::ostringstream oss;
    oss << "SET_MOTION_OUTPUT_VALUE index=" << index << " value=" << value;
    standalone::push_canon_event(oss.str());
}
void SET_AUX_OUTPUT_VALUE(int index, double value)
{
    std::ostringstream oss;
    oss << "SET_AUX_OUTPUT_VALUE index=" << index << " value=" << value;
    standalone::push_canon_event(oss.str());
    standalone::sync_motion_analog_output(index, value);
}
int WAIT(int index, int input_type, int wait_type, double timeout)
{
    std::ostringstream oss;
    oss << "WAIT index=" << index
        << " input_type=" << input_type
        << " wait_type=" << wait_type
        << " timeout=" << timeout;
    standalone::push_canon_event(oss.str());
    return 0;
}
int UNLOCK_ROTARY(int line_no, int joint_num)
{
    std::ostringstream oss;
    oss << "UNLOCK_ROTARY line=" << line_no << " joint=" << joint_num;
    standalone::push_canon_event(oss.str());
    return 0;
}
int LOCK_ROTARY(int line_no, int joint_num)
{
    std::ostringstream oss;
    oss << "LOCK_ROTARY line=" << line_no << " joint=" << joint_num;
    standalone::push_canon_event(oss.str());
    return 0;
}
void UPDATE_TAG(const StateTag &tag)
{
    std::ostringstream oss;
    oss << "UPDATE_TAG"
        << " line=" << tag.fields[GM_FIELD_LINE_NUMBER]
        << " g0=" << tag.fields[GM_FIELD_G_MODE_0]
        << " motion=" << tag.fields[GM_FIELD_MOTION_MODE]
        << " plane=" << tag.fields[GM_FIELD_PLANE]
        << " origin=" << tag.fields[GM_FIELD_ORIGIN]
        << " feed=" << tag.fields_float[GM_FIELD_FLOAT_FEED]
        << " speed=" << tag.fields_float[GM_FIELD_FLOAT_SPEED]
        << " flags=" << tag.flags.to_ulong();
    standalone::push_canon_event(oss.str());
}

void MESSAGE(char *s) { standalone::push_canon_event(std::string("MESSAGE: ") + (s ? s : "")); }
void LOG(char *s)
{
    standalone::push_canon_event(std::string("LOG: ") + (s ? s : ""));
}
void LOGOPEN(char *s)
{
    standalone::push_canon_event(std::string("LOGOPEN: ") + (s ? s : ""));
}
void LOGAPPEND(char *s)
{
    standalone::push_canon_event(std::string("LOGAPPEND: ") + (s ? s : ""));
}
void LOGCLOSE()
{
    standalone::push_canon_event("LOGCLOSE");
}

void CANON_ERROR(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    fputc('\n', stderr);
    va_end(ap);
}

double GET_EXTERNAL_FEED_RATE() { return standalone::g_external_feed_rate; }
int GET_EXTERNAL_FLOOD() { return 0; }
CANON_UNITS GET_EXTERNAL_LENGTH_UNIT_TYPE()
{
    return (emcStatus->motion.traj.linearUnits > 0.5) ? CANON_UNITS_MM : CANON_UNITS_INCHES;
}
double GET_EXTERNAL_LENGTH_UNITS() { return emcStatus->motion.traj.linearUnits; }
double GET_EXTERNAL_ANGLE_UNITS() { return 1.0; }
int GET_EXTERNAL_MIST() { return 0; }
CANON_MOTION_MODE GET_EXTERNAL_MOTION_CONTROL_MODE() { return CANON_EXACT_STOP; }
double GET_EXTERNAL_MOTION_CONTROL_TOLERANCE() { return 0.0; }
double GET_EXTERNAL_MOTION_CONTROL_NAIVECAM_TOLERANCE() { return 0.0; }
double GET_EXTERNAL_ORIGIN_A() { return 0.0; }
double GET_EXTERNAL_ORIGIN_B() { return 0.0; }
double GET_EXTERNAL_ORIGIN_C() { return 0.0; }
double GET_EXTERNAL_ORIGIN_X() { return 0.0; }
double GET_EXTERNAL_ORIGIN_Y() { return 0.0; }
double GET_EXTERNAL_ORIGIN_Z() { return 0.0; }
void GET_EXTERNAL_PARAMETER_FILE_NAME(char *filename, int max_size)
{
    standalone::get_parameter_file_name(filename, max_size);
}
void SET_PARAMETER_FILE_NAME(const char *filename)
{
    standalone::set_parameter_file_name(filename);
}
CANON_PLANE GET_EXTERNAL_PLANE() { return CANON_PLANE::XY; }
double GET_EXTERNAL_POSITION_A() { return 0.0; }
double GET_EXTERNAL_POSITION_B() { return 0.0; }
double GET_EXTERNAL_POSITION_C() { return 0.0; }
double GET_EXTERNAL_POSITION_X() { return 0.0; }
double GET_EXTERNAL_POSITION_Y() { return 0.0; }
double GET_EXTERNAL_POSITION_Z() { return 0.0; }
double GET_EXTERNAL_POSITION_U() { return 0.0; }
double GET_EXTERNAL_POSITION_V() { return 0.0; }
double GET_EXTERNAL_POSITION_W() { return 0.0; }
double GET_EXTERNAL_PROBE_POSITION_A() { return 0.0; }
double GET_EXTERNAL_PROBE_POSITION_B() { return 0.0; }
double GET_EXTERNAL_PROBE_POSITION_C() { return 0.0; }
double GET_EXTERNAL_PROBE_POSITION_X() { return 0.0; }
double GET_EXTERNAL_PROBE_POSITION_Y() { return 0.0; }
double GET_EXTERNAL_PROBE_POSITION_Z() { return 0.0; }
double GET_EXTERNAL_PROBE_POSITION_U() { return 0.0; }
double GET_EXTERNAL_PROBE_POSITION_V() { return 0.0; }
double GET_EXTERNAL_PROBE_POSITION_W() { return 0.0; }
double GET_EXTERNAL_PROBE_VALUE() { return 0.0; }
int GET_EXTERNAL_PROBE_TRIPPED_VALUE() { return 0; }
int GET_EXTERNAL_QUEUE_EMPTY() { return 1; }
double GET_EXTERNAL_SPEED(int) { return 0.0; }
CANON_DIRECTION GET_EXTERNAL_SPINDLE(int) { return CANON_STOPPED; }
double GET_EXTERNAL_TOOL_LENGTH_XOFFSET() { return 0.0; }
double GET_EXTERNAL_TOOL_LENGTH_YOFFSET() { return 0.0; }
double GET_EXTERNAL_TOOL_LENGTH_ZOFFSET() { return 0.0; }
double GET_EXTERNAL_TOOL_LENGTH_AOFFSET() { return 0.0; }
double GET_EXTERNAL_TOOL_LENGTH_BOFFSET() { return 0.0; }
double GET_EXTERNAL_TOOL_LENGTH_COFFSET() { return 0.0; }
double GET_EXTERNAL_TOOL_LENGTH_UOFFSET() { return 0.0; }
double GET_EXTERNAL_TOOL_LENGTH_VOFFSET() { return 0.0; }
double GET_EXTERNAL_TOOL_LENGTH_WOFFSET() { return 0.0; }
int GET_EXTERNAL_TOOL_SLOT() { return standalone::current_tool_index(); }
int GET_EXTERNAL_SELECTED_TOOL_SLOT() { return standalone::selected_tool_index(); }
CANON_TOOL_TABLE GET_EXTERNAL_TOOL_TABLE(int index)
{
    CANON_TOOL_TABLE tool = standalone::tool_entry_init();
    standalone::get_tool_entry(&tool, index);
    return tool;
}
int GET_EXTERNAL_TC_FAULT() { return 0; }
int GET_EXTERNAL_TC_REASON() { return 0; }
double GET_EXTERNAL_TRAVERSE_RATE() { return 0.0; }
int GET_EXTERNAL_FEED_OVERRIDE_ENABLE() { return 1; }
int GET_EXTERNAL_SPINDLE_OVERRIDE_ENABLE(int) { return 1; }
int GET_EXTERNAL_ADAPTIVE_FEED_ENABLE() { return 1; }
int GET_EXTERNAL_FEED_HOLD_ENABLE() { return 1; }
int GET_EXTERNAL_DIGITAL_INPUT(int index, int def)
{
    return standalone::external_digital_input(index, def);
}
double GET_EXTERNAL_ANALOG_INPUT(int index, double def)
{
    return standalone::external_analog_input(index, def);
}
int GET_EXTERNAL_AXIS_MASK() { return standalone::external_axis_mask(); }
int GET_EXTERNAL_OFFSET_APPLIED() { return 0; }
EmcPose GET_EXTERNAL_OFFSETS() { return {{0, 0, 0}, 0, 0, 0, 0, 0, 0}; }

void NURBS_G5_FEED(int lineno,
                   const std::vector<NURBS_CONTROL_POINT> &control_points,
                   unsigned int order,
                   CANON_PLANE plane)
{
    std::ostringstream oss;
    oss << "NURBS_G5_FEED line=" << lineno
        << " order=" << order
        << " plane=" << static_cast<int>(plane)
        << " points=" << control_points.size();
    for (std::size_t index = 0; index < control_points.size(); ++index) {
        const auto &point = control_points[index];
        oss << " p" << index
            << "=(" << point.NURBS_X
            << "," << point.NURBS_Y
            << "," << point.NURBS_W << ")";
    }
    standalone::push_canon_event(oss.str());
}
void NURBS_G6_FEED(int lineno,
                   const std::vector<NURBS_G6_CONTROL_POINT> &control_points,
                   unsigned int k,
                   double feedrate,
                   int interpolation_mode,
                   CANON_PLANE plane)
{
    std::ostringstream oss;
    oss << "NURBS_G6_FEED line=" << lineno
        << " k=" << k
        << " feedrate=" << feedrate
        << " interpolation_mode=" << interpolation_mode
        << " plane=" << static_cast<int>(plane)
        << " points=" << control_points.size();
    for (std::size_t index = 0; index < control_points.size(); ++index) {
        const auto &point = control_points[index];
        oss << " p" << index
            << "=(" << point.NURBS_X
            << "," << point.NURBS_Y
            << "," << point.NURBS_R
            << "," << point.NURBS_K << ")";
    }
    standalone::push_canon_event(oss.str());
}

void COMMENT(const char *s)
{
    standalone::push_canon_event(std::string("COMMENT: ") + (s ? s : ""));
}

void STRAIGHT_TRAVERSE(int lineno,
                       double x, double y, double z,
                       double a, double b, double c,
                       double u, double v, double w)
{
    std::ostringstream oss;
    oss << "STRAIGHT_TRAVERSE line=" << lineno
        << " x=" << x
        << " y=" << y
        << " z=" << z
        << " a=" << a
        << " b=" << b
        << " c=" << c
        << " u=" << u
        << " v=" << v
        << " w=" << w;
    standalone::push_canon_event(oss.str());
}

void STRAIGHT_FEED(int lineno,
                   double x, double y, double z,
                   double a, double b, double c,
                   double u, double v, double w)
{
    std::ostringstream oss;
    oss << "STRAIGHT_FEED line=" << lineno
        << " x=" << x
        << " y=" << y
        << " z=" << z
        << " a=" << a
        << " b=" << b
        << " c=" << c
        << " u=" << u
        << " v=" << v
        << " w=" << w;
    standalone::push_canon_event(oss.str());
}

void ARC_FEED(int lineno,
              double first_end, double second_end,
              double first_axis, double second_axis, int rotation,
              double axis_end_point,
              double a, double b, double c,
              double u, double v, double w)
{
    std::ostringstream oss;
    oss << "ARC_FEED line=" << lineno
        << " first_end=" << first_end
        << " second_end=" << second_end
        << " first_axis=" << first_axis
        << " second_axis=" << second_axis
        << " rotation=" << rotation
        << " axis_end_point=" << axis_end_point
        << " a=" << a
        << " b=" << b
        << " c=" << c
        << " u=" << u
        << " v=" << v
        << " w=" << w;
    standalone::push_canon_event(oss.str());
}

void SET_FEED_RATE(double rate)
{
    standalone::g_external_feed_rate = rate;
    std::ostringstream oss;
    oss << "SET_FEED_RATE rate=" << rate;
    standalone::push_canon_event(oss.str());
}
