#include "RosMatch.hpp"

#include "Log.h"
#include "ecarx_loc.h"

namespace jf {

static const double sc_min_prob = 0.8;

RosMatch::RosMatch() {
    m_mmMatch.Init();
    // 注册回调函数
    m_subGnss = m_nhNodeHandle.subscribe("/roadsim_gps", 100, &RosMatch::ReceiveGnss, this);
    m_subEyeQ4 = m_nhNodeHandle.subscribe("/sensor_abstraction/AduParse", 10, &RosMatch::ReceiveAduData, this);
    m_subLoc = m_nhNodeHandle.subscribe("/localization_result", 10, &RosMatch::ReceiveLocalization, this);
    // 设置发布
    m_pubMatchResult = m_nhNodeHandle.advertise<jc_map_msgs::MatchingResult>("/MatchingResult", 10);
    m_pubLocCanFd = m_nhNodeHandle.advertise<can_msgs::Frame>("/sent_messages", 10);
    // begin run map outputing thread
    m_thdPubMatchResult = new std::thread(&RosMatch::PublishMatchResultProc, this);
}

RosMatch::~RosMatch() {}

void RosMatch::ReceiveGnss(const gnss_imu_msgs::GNSS::ConstPtr& msg) {
    ROS_INFO("RosMatch::ReceiveAduData(), a gnss pkg");
    IGnssLocationData gnss_location_data = {};

    gnss_location_data.timestamp.sec = msg->time_stamp_.time_stamp_s_;
    gnss_location_data.timestamp.nanosec = msg->time_stamp_.time_stamp_ns_;
    gnss_location_data.gnss_point.x = msg->gnss_longitude_;
    gnss_location_data.gnss_point.y = msg->gnss_latitude_;
    gnss_location_data.gnss_point.z = msg->gnss_height_;
    gnss_location_data.heading = msg->gnss_heading_;
    gnss_location_data.speed = msg->gnss_ground_speed_;
    gnss_location_data.coefficient = msg->gnss_pitch_standard_deviation_;
    if (sqrt(pow(msg->gnss_longitude_standard_deviation_, 2) + pow(msg->gnss_latitude_standard_deviation_, 2)) <= 0.01) {
        gnss_location_data.credible = true;
    } else {
        gnss_location_data.credible = false;
    }
    gnss_location_data.credible = true;
    gnss_location_data.gnss_time = 315936000.0 - 18 + msg->gps_time_.gnss_week_ * 7 * 86400 + msg->gps_time_.seconds_from_week_start_ + 8 * 3600;
    gnss_location_data.longitude_deviation = msg->gnss_longitude_standard_deviation_;
    gnss_location_data.latitude_deviation = msg->gnss_latitude_standard_deviation_;
    gnss_location_data.height_deviation = msg->gnss_height_standard_deviation_;

    m_mmMatch.SaveGnssLocData(gnss_location_data);

    return;
}

void RosMatch::ReceiveAduData(const adu_msgs::MeData::ConstPtr& msg) {
    ROS_INFO("RosMatch::ReceiveAduData(), a adu pkg");
    static bool is_fisrt = true;
    static auto start_time = msg->header.stamp;
    if (is_fisrt) {
        // start_time = msg->com_header_.time_stamp_.time_stamp_s_;
        is_fisrt = false;
    }

    IVisionMap vision = {};

    // time stamp
    vision.timestamp.sec = msg->com_header_.time_stamp_.time_stamp_s_;
    vision.timestamp.nanosec = msg->com_header_.time_stamp_.time_stamp_ns_;

    // crossing
    if (true == msg->lane_info_.left_crossing_) {
        vision.crossing = MarkingCrossing::EDGE_CROSSING_LEFT_CROSSING;
        // ROS_WARN("RosInterface::ReceiveAduData(), left crossing");
    } else if (true == msg->lane_info_.right_crossing_) {
        vision.crossing = MarkingCrossing::EDGE_CROSSING_RIGHT_CROSSING;
        // ROS_WARN("RosInterface::ReceiveAduData(), right crossing");
    } else {
        vision.crossing = MarkingCrossing::EDGE_CROSSING_NO_CROSSING;
    }

    // lane marks
    for (int i = 0; i < msg->lanes_mark_info_.size(); i++) {
        const adu_msgs::LanesMarkInfo& lane = msg->lanes_mark_info_[i];
        // invalid type
        if (0 == lane.line_type_) {
            ROS_WARN("RosInterface::ReceiveAduData(), invalid type: %d", lane.line_type_);
            continue;
        }
        // lane edge
        else if (1 == lane.line_type_) {
            if (lane.line_exist_prob_ >= sc_min_prob) {
                ILaneMarking lane_edge = {};
                if (false == ConvertLanePos(lane.line_side_, lane_edge.pos)) {
                    ROS_WARN("RosInterface::ReceiveAduData(), invalid side type: %d", lane.line_side_);
                    continue;
                }
                if (false == ConvertLaneColor(lane.line_color_, lane_edge.lane_marking_color)) {
                    ROS_WARN("RosInterface::ReceiveAduData(), invalid color type: %d", lane.line_color_);
                    continue;
                }
                // lane_edge.color_prob = lane.line_color_prob_;
                if (false == ConvertLaneCross(lane.line_dlm_type_, lane.line_lane_mark_type_, lane_edge.cross_type)) {
                    ROS_WARN("RosInterface::ReceiveAduData(), invalid cross type: dlm = %d, mark = %d", lane.line_dlm_type_, lane.line_lane_mark_type_);
                    continue;
                }
                lane_edge.angle = lane.line_c1_;
                lane_edge.dis = lane.line_c0_;
                lane_edge.exist_prob = lane.line_exist_prob_;
                vision.lane_markings.push_back(lane_edge);
            }
        }
        // road edge
        else if (2 == lane.line_type_) {
            if (lane.line_exist_prob_ >= sc_min_prob) {
                ILinkMarking link_edge = {};
                if (false == ConvertLinkPos(lane.line_side_, link_edge.pos)) {
                    continue;
                }
                if (false == ConvertLaneCross(lane.line_dlm_type_, lane.line_lane_mark_type_, link_edge.type)) {
                    continue;
                }
                link_edge.angle = lane.line_c1_;
                link_edge.dis = lane.line_c0_;
                link_edge.exist_prob = lane.line_exist_prob_;
                if (LinkMarkingSide::LINK_MARKING_SIDE_LEFT == link_edge.pos) {
                    vision.left_link_marking.push_back(link_edge);
                } else {
                    vision.right_link_marking.push_back(link_edge);
                }
                // ROS_WARN("RosInterface::ReceiveAduData(), time: %d seconds, link pos = %d, lane1 prob = %d:%d", msg->header.stamp - start_time, link_edge.pos, lane.line_color_prob_, lane.line_exist_prob_);
            }
        }
        // invalid type
        else {
            ROS_WARN("RosInterface::ReceiveAduData(), invalid type: %d", lane.line_type_);
            continue;
        }
    }

    if (true == vision.lane_markings.empty()) {
        ROS_WARN("RosInterface::ReceiveAduData(), no valid lane");
        return;
    }

    if (0 != msg->tsr_info_.tsr_shape_) {
        ITrafsignMap trafsign = {};
        trafsign.lat_dis = msg->tsr_info_.tsr_sign_lat_dis_;
        trafsign.lon_dis = msg->tsr_info_.tsr_sign_long_dis_;
        trafsign.traffic_sign_shape = msg->tsr_info_.tsr_shape_;
        trafsign.exist_prob = msg->tsr_info_.tsr_prob_;
        vision.traffic_signs.push_back(trafsign);
        ConvertTsr(msg->tsr_info_, trafsign);
        // ROS_WARN("RosInterface::ReceiveAduData(), time: %d seconds, shape = %d, lat dis = %f, long dis = %f, tsr prob = %d", msg->header.stamp - start_time, msg->tsr_info_.tsr_shape_, msg->tsr_info_.tsr_sign_lat_dis_,
        // msg->tsr_info_.tsr_sign_long_dis_,
        //          msg->tsr_info_.tsr_prob_);
    }

    m_mmMatch.SaveVisionMapData(vision);

    return;
}

void RosMatch::ReceiveLocalization(const localization_msgs::Localization::ConstPtr& msg) {
    auto func_wraps2pi_pi = [](double& rad) {
        while (rad > pi) rad -= 2 * pi;
        while (rad < (-pi)) rad += 2 * pi;
        return rad;
    };

    auto func_Quat2Euler = [&func_wraps2pi_pi](double q0, double q1, double q2, double q3, double& roll, double& pitch, double& heading) {
        double gamma = atan2(2.0 * (q0 * q1 + q2 * q3), 1.0 - 2.0 * (q1 * q1 + q2 * q2));
        double theta = asin(2.0 * (q0 * q2 - q3 * q1));
        double psi = atan2(2.0 * (q0 * q3 + q1 * q2), 1.0 - 2.0 * (q2 * q2 + q3 * q3));
        roll = func_wraps2pi_pi(gamma);
        pitch = func_wraps2pi_pi(theta);
        heading = func_wraps2pi_pi(psi);
    };

    ILocalization loc = {};

    loc.timestamp.sec = msg->com_header_.time_stamp_.time_stamp_s_;
    loc.timestamp.nanosec = msg->com_header_.time_stamp_.time_stamp_ns_;

    loc.loc_point.x = msg->pose_.position_.x_;
    loc.loc_point.y = msg->pose_.position_.y_;
    loc.loc_point.z = msg->pose_.position_.z_;
    loc.gnss_available = msg->gnss_available;
    func_Quat2Euler(msg->pose_.orientation_.quaternion_x_, msg->pose_.orientation_.quaternion_y_, msg->pose_.orientation_.quaternion_z_, msg->pose_.orientation_.quaternion_w_, loc.roll, loc.pitch, loc.heading);
    loc.speed = pow((pow(msg->velocity_.linear_velocity_.x_, 2) + pow(msg->velocity_.angular_velocity_.y_, 2)), 0.5);
    loc.longitude_accuracy = msg->acceleration_.std_linear_acceleration_.x_;
    loc.latitude_accuracy = msg->acceleration_.std_linear_acceleration_.y_;

    PublishLocCanFd(msg, loc.speed);

    m_mmMatch.SaveEkfResultData(loc);
}

void RosMatch::PublishLocCanFd(const localization_msgs::Localization::ConstPtr& msg, double speed) {
    static int s_nLogCnt = 0;
    ecarx_loc_localization_timestamp_t tsTimeStamp = {};
    tsTimeStamp.localization_timestamp = ecarx_loc_localization_timestamp_localization_timestamp_encode(msg->com_header_.time_stamp_.time_stamp_s_ + msg->com_header_.time_stamp_.time_stamp_ns_ * 1e-9);
    can_msgs::Frame msgFrame400 = {};
    msgFrame400.header = msg->header;
    msgFrame400.id = 0x400;
    msgFrame400.is_rtr = false;
    msgFrame400.is_extended = false;
    msgFrame400.is_error = false;
    msgFrame400.dlc = 64;
    msgFrame400.data.resize(msgFrame400.dlc);
    ecarx_loc_localization_timestamp_pack((uint8_t*)(msgFrame400.data.data()), &tsTimeStamp, msgFrame400.dlc);

    // if (s_nLogCnt % 10 == 0) {
    //     JFLOG(JFINFO, "RosMatch::PublishLocCanFd(), Msg = 400, timestamp = %d", tsTimeStamp.localization_timestamp);
    // }

    ecarx_loc_localization_result_t lrLocRst = {};
    lrLocRst.localization_acceleration = ecarx_loc_localization_result_localization_acceleration_encode(msg->acceleration_.linear_acceleration_.y_);
    lrLocRst.localization_altitude = ecarx_loc_localization_result_localization_altitude_encode(0);  // 法律规定，高度为0./*msg->pose_.position_.z_*/
    lrLocRst.localization_confidence = ecarx_loc_localization_result_localization_confidence_encode(1.0);
    lrLocRst.localization_latitude = ecarx_loc_localization_result_localization_latitude_encode(msg->pose_.position_.y_);
    lrLocRst.localization_longitude = ecarx_loc_localization_result_localization_longitude_encode(msg->pose_.position_.x_);
    lrLocRst.localization_pitch = ecarx_loc_localization_result_localization_pitch_encode(msg->attitude_.pitch_ / 180 * PI);
    lrLocRst.localization_roll = ecarx_loc_localization_result_localization_roll_encode(msg->attitude_.roll_ / 180 * PI);
    lrLocRst.localization_velocity = ecarx_loc_localization_result_localization_velocity_encode(speed);
    lrLocRst.localization_yaw = ecarx_loc_localization_result_localization_yaw_encode(msg->attitude_.yaw_ / 180 * PI);
    can_msgs::Frame msgFrame401 = {};
    msgFrame401.header = msg->header;
    msgFrame401.id = 0x401;
    msgFrame401.is_rtr = false;
    msgFrame401.is_extended = false;
    msgFrame401.is_error = false;
    msgFrame401.dlc = 64;
    msgFrame401.data.resize(msgFrame401.dlc);
    ecarx_loc_localization_result_pack((uint8_t*)(msgFrame401.data.data()), &lrLocRst, msgFrame401.dlc);

    // if (s_nLogCnt % 10 == 0) {
    //     JFLOG(JFINFO, "RosMatch::PublishLocCanFd(), Msg = 401, acc = %d, lat = %d, lon = %d, pitch = %d, roll = %d, velocity = %d, yaw = %d", lrLocRst.localization_acceleration, lrLocRst.localization_latitude, lrLocRst.localization_longitude,
    //           lrLocRst.localization_pitch, lrLocRst.localization_roll, lrLocRst.localization_velocity, lrLocRst.localization_yaw);
    // }

    ecarx_loc_localization_result_std_sts_t lsLocSts = {};
    lsLocSts.localization_acceleration_std = ecarx_loc_localization_result_std_sts_localization_acceleration_std_encode(msg->acceleration_.std_linear_acceleration_.y_);
    lsLocSts.localization_altitude_std = ecarx_loc_localization_result_std_sts_localization_altitude_std_encode(msg->pose_.std_position_.z_);
    lsLocSts.localization_init_status = ecarx_loc_localization_result_std_sts_localization_init_status_encode(0);
    lsLocSts.localization_status = ecarx_loc_localization_result_std_sts_localization_status_encode(0);
    lsLocSts.localization_latitude_std = ecarx_loc_localization_result_std_sts_localization_latitude_std_encode(msg->pose_.std_position_.y_);
    lsLocSts.localization_longitude_std = ecarx_loc_localization_result_std_sts_localization_longitude_std_encode(msg->pose_.std_position_.x_);
    lsLocSts.localization_pitch_std = ecarx_loc_localization_result_std_sts_localization_pitch_std_encode(msg->attitude_.std_pitch_ / 180 * PI);
    lsLocSts.localization_roll_std = ecarx_loc_localization_result_std_sts_localization_roll_std_encode(msg->attitude_.std_roll_ / 180 * PI);
    lsLocSts.localization_velocity_std = ecarx_loc_localization_result_std_sts_localization_velocity_std_encode(msg->velocity_.std_linear_velocity_.y_);
    lsLocSts.localization_yaw_std = ecarx_loc_localization_result_std_sts_localization_yaw_std_encode(msg->attitude_.std_yaw_ / 180 * PI);
    can_msgs::Frame msgFrame402 = {};
    msgFrame402.header = msg->header;
    msgFrame402.id = 0x402;
    msgFrame402.is_rtr = false;
    msgFrame402.is_extended = false;
    msgFrame402.is_error = false;
    msgFrame402.dlc = 64;
    msgFrame402.data.resize(msgFrame402.dlc);
    ecarx_loc_localization_result_std_sts_pack((uint8_t*)(msgFrame402.data.data()), &lsLocSts, msgFrame402.dlc);

    // if (s_nLogCnt % 10 == 0) {
    //     JFLOG(JFINFO, "RosMatch::PublishLocCanFd(), Msg = 402, acc_std = %d, alt_std = %d, lat_std = %d, lon_std = %d, pitch_std = %d, roll_std = %d, velocity_std = %d, yaw_std = %d", lsLocSts.localization_acceleration_std,
    //           lsLocSts.localization_altitude_std, lsLocSts.localization_latitude_std, lsLocSts.localization_longitude_std, lsLocSts.localization_pitch_std, lsLocSts.localization_roll_std, lsLocSts.localization_velocity_std,
    //           lsLocSts.localization_yaw_std);
    // }

    s_nLogCnt++;

    m_pubLocCanFd.publish(msgFrame400);
    m_pubLocCanFd.publish(msgFrame401);
    m_pubLocCanFd.publish(msgFrame402);
    ROS_INFO("RosMatch::PublishLocCanFd(), canfd is sent");
    return;
}

void RosMatch::PublishMatchResultProc() {
    ros::Rate rate(20);  // 20 hz
    while (ros::ok()) {
        IGnssFootLocation data = {};
        if (true == m_mmMatch.GetMatchResultData(data)) {
            this->PublishMatchResult(data);
        }
        rate.sleep();
    }
}

void RosMatch::PublishMatchResult(const IGnssFootLocation& data) {
    jc_map_msgs::MatchingResult msg = {};

    msg.header.stamp = ros::Time::now();
    msg.time_stamp_.time_stamp_s_ = data.timestamp.sec;
    msg.time_stamp_.time_stamp_ns_ = data.timestamp.nanosec;

    msg.time_stamp_.time_stamp_s_ = data.timestamp.sec;
    msg.time_stamp_.time_stamp_ns_ = data.timestamp.nanosec;
    msg.change_lanes_ = data.change_lanes;
    msg.lane_seq_no_ = data.lane_seq_no;
    msg.lane_num_ = data.lane_num;
    msg.road_type_ = data.road_type;
    msg.angle_with_lane_ = data.angle_with_lane;
    msg.special_type_ = data.special_type;
    msg.special_type_begin_dis_ = data.special_type_begin_dis;
    msg.special_type_end_dis_ = data.special_type_end_dis;
    msg.in_road_crossing_ = data.in_road_crossing;
    msg.left_lane_edge_dis_ = data.left_lane_edge_dis;
    msg.right_lane_edge_dis_ = data.right_lane_edge_dis;
    msg.foot_point_.x_ = data.foot_point.x;
    msg.foot_point_.y_ = data.foot_point.y;
    msg.foot_point_.z_ = data.foot_point.z;
    msg.foot_prob_ = data.foot_prob;
    msg.vision_horizontal_point_.x_ = data.vision_horizontal_point.x;
    msg.vision_horizontal_point_.y_ = data.vision_horizontal_point.y;
    msg.vision_horizontal_point_.z_ = data.vision_horizontal_point.z;
    msg.vision_vertical_point_.x_ = data.vision_vertical_point.x;
    msg.vision_vertical_point_.y_ = data.vision_vertical_point.y;
    msg.vision_vertical_point_.z_ = data.vision_vertical_point.z;
    msg.gnss_time_ = data.gnss_time;
    msg.gnss_longitude_ = data.gnss_point.x;
    msg.gnss_latitude_ = data.gnss_point.y;
    msg.gnss_height_ = data.gnss_point.z;
    msg.gnss_heading_ = data.gnss_heading;
    msg.gnss_heading_ = data.gnss_heading;
    msg.gnss_ground_speed_ = data.gnss_speed;
    msg.gnss_longitude_standard_deviation_ = data.gnss_longitude_deviation;
    msg.gnss_latitude_standard_deviation_ = data.gnss_latitude_deviation;
    msg.gnss_height_standard_deviation_ = data.gnss_height_deviation;

    m_pubMatchResult.publish(msg);

    return;
}

bool RosMatch::ConvertLanePos(int src, LaneMarkingSide& dst) {
    switch (src) {
        case 1:  //
            dst = LaneMarkingSide::LANE_MARKING_SIDE_LEFT;
            return true;
        case 2:  // right
            dst = LaneMarkingSide::LANE_MARKING_SIDE_RIGHT;
            return true;
        case 3:  //
            dst = LaneMarkingSide::LANE_MARKING_SIDE_LEFT_LEFT;
            return true;
        case 4:  //
            dst = LaneMarkingSide::LANE_MARKING_SIDE_RIGHT_RIGHT;
            return true;
        default:
            return false;
    }

    return false;
}

bool RosMatch::ConvertLaneColor(int src, HMC::LaneMarkingColor& dst) {
    switch (src) {
        case 1:
            dst = HMC::LaneMarkingColor::HDM_LANE_MARKING_COLOR_WHITE;
            return true;
        case 2:  //
            dst = HMC::LaneMarkingColor::HDM_LANE_MARKING_COLOR_YELLOW;
            return true;
        case 3:  //
            dst = HMC::LaneMarkingColor::HDM_LANE_MARKING_COLOR_BLUE;
            return true;
        default:
            dst = HMC::LaneMarkingColor::HDM_LANE_MARKING_COLOR_WHITE;
            return false;
    };

    return false;
}

bool RosMatch::ConvertLaneCross(int src_double, int src_single, HMC::LaneMarkingType& dst) {
    if (0 != src_double) {
        switch (src_double) {
            case 1:  //
                dst = HMC::LaneMarkingType::HDM_LANE_MARKING_TYPE_LEFT_SOLID_RIGHT_DASHED;
                return true;
            case 2:  //
                dst = HMC::LaneMarkingType::HDM_LANE_MARKING_TYPE_RIGHT_SOLID_LEFT_DASHED;
                return true;
            case 3:  //
                dst = HMC::LaneMarkingType::HDM_LANE_MARKING_TYPE_DOUBLE_SOLID;
                return true;
            case 4:  //
                dst = HMC::LaneMarkingType::HDM_LANE_MARKING_TYPE_DOUBLE_DASHED;
                return true;
            default:
                return false;
        };
    } else {
        switch (src_single) {
            case 1:  //
                dst = HMC::LaneMarkingType::HDM_LANE_MARKING_TYPE_SINGLE_SOLID;
                return true;
            case 2:  //
                dst = HMC::LaneMarkingType::HDM_LANE_MARKING_TYPE_SINGLE_DASHED;
                return true;
            case 3:  //
                return false;
            case 4:  //
                dst = HMC::LaneMarkingType::HDM_LANE_MARKING_TYPE_SHORT_THICK_DASHED;
                return true;
            default:
                return false;
        }
    }

    return false;
}

bool RosMatch::ConvertLinkPos(int src, LinkMarkingSide& dst) {
    switch (src) {
        case 1:  //
        case 3:  //
            dst = LinkMarkingSide::LINK_MARKING_SIDE_LEFT;
            return true;
        case 2:  //
        case 4:  //
            dst = LinkMarkingSide::LINK_MARKING_SIDE_RIGHT;
            return true;
        default:
            return false;
    }

    return false;
}

bool RosMatch::ConvertTsr(const adu_msgs::TsrInfo& src, ITrafsignMap& dst) {
    switch (src.tsr_sign_name_) {
        case 0:   // Regular 10
        case 28:  // Electronic 10
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 10;
            break;
        case 1:   // Regular 20
        case 29:  // Electronic 20
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 20;
            break;
        case 2:   // Regular 30
        case 30:  // Electronic 30
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 30;
            break;
        case 3:   // Regular 40
        case 31:  // Electronic 40
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 40;
            break;
        case 4:   // Regular 50
        case 32:  // Electronic 50
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 50;
            break;
        case 5:   // Regular 60
        case 33:  // Electronic 60
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 60;
            break;
        case 6:   // Regular 70
        case 34:  // Electronic 70
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 70;
            break;
        case 7:   // Regular 80
        case 35:  // Electronic 80
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 80;
            break;
        case 8:   // Regular 90
        case 36:  // Electronic 90
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 90;
            break;
        case 9:   // Regular 100
        case 37:  // Electronic 100
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 100;
            break;
        case 10:  // Regular 110
        case 38:  // Electronic 110
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 110;
            break;
        case 11:  // Regular 120
        case 39:  // Electronic 120
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 120;
            break;
        case 12:  // Regular 130
        case 40:  // Electronic 130
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 130;
            break;
        case 41:  // Electronic 140
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 140;
        case 23:  // Car Limit
            break;
        case 25:  // Minimum Speed Limits
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 14;
            break;
        case 26:  // Motorcycle Limit
            break;
        case 79:  // End of Speed 2 digits
            break;
        case 80:  // End of Speed 3 digits
            break;
        case 100:  // Regular 5
        case 115:  // Electronic 5
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 5;
            break;
        case 101:  // Regular 15
        case 116:  // Electronic 15
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 15;
            break;
        case 102:  // Regular 25
        case 117:  // Electronic 25
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 25;
            break;
        case 103:  // Regular 35
        case 118:  // Electronic 35
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 35;
            break;
        case 104:  // Regular 45
        case 119:  // Electronic 45
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 45;
            break;
        case 105:  // Regular 55
        case 120:  // Electronic 55
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 55;
            break;
        case 106:  // Regular 65
        case 121:  // Electronic 65
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 65;
            break;
        case 107:  // Regular 75
        case 122:  // Electronic 75
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 75;
            break;
        case 108:  // Regular 85
        case 123:  // Electronic 85
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 85;
            break;
        case 109:  // Regular 95
        case 124:  // Electronic 95
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 95;
            break;
        case 110:  // Regular 105
        case 125:  // Electronic 105
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 105;
            break;
        case 111:  // Regular 115
        case 126:  // Electronic 115
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 115;
            break;
        case 112:  // Regular 125
        case 127:  // Electronic 125
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 125;
            break;
        case 113:  // Regular 135
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 135;
            break;
        case 114:  // Regular 145
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 11;
            dst.speed_limit_value = 145;
            break;
        case 152:  // Right Merge
            dst.traffic_sign_class = 1;
            dst.traffic_sign_sub_class = 49;
            break;
        case 207:  // No stopping
            break;
        case 256:  // Bicycle Lane
            break;
        case 260:  // Height Limit
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 8;
            break;
        case 266:  // Weight Limit
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 9;
            break;
        case 267:  // Width Limit
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 13;
            break;
        default:
            break;
    }

    switch (src.tsr_sup1_sign_name_) {
        case 0:  // None
            break;
        case 1:  // Rain
            break;
        case 2:  // Snow
            break;
        case 3:  // Trailer
            break;
        case 4:  // Time
            break;
        case 5:  // Arrow_left
            dst.traffic_sign_class = 4;
            dst.traffic_sign_sub_class = 3;
        case 6:  // Arrow_right
            dst.traffic_sign_class = 4;
            dst.traffic_sign_sub_class = 4;
        case 7:  // BendArrow_left
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 5;
        case 8:  // BendArrow_right
            dst.traffic_sign_class = 5;
            dst.traffic_sign_sub_class = 6;
        case 9:  // Truck
            break;
        case 10:  // Distance_arrow(distance_for)
            break;
        case 11:  // Weight
            break;
        case 12:  // Distance_in
            break;
        case 13:  // Tractor
            break;
        case 14:  // Snow_rain
            break;
        case 15:  // School
            dst.traffic_sign_class = 6;
            break;
        case 16:  // Rain_cloud
            break;
        case 17:  // Fog
            break;
        case 18:  // Hazerdous_materials
            break;
        case 19:  // Night
            break;
        case 20:  // Supp_sign_generic
            break;
        case 21:  // e_rappel
            break;
        case 22:  // e_zone
            break;
        case 23:  // Ramp
            dst.traffic_sign_class = 2;
            dst.traffic_sign_sub_class = 9;
            dst.traffic_sign_sub_class = 10;
            break;
        case 24:  // End
            break;
        case 25:  // Exit
            break;
        case 26:  // Reserved
            break;
        case 27:  // Minimum
            break;
        case 34:  // Arrow_bidirectional
            break;
        case 35:  // Work_Zone
            break;
        case 255:  //= Invalid_supp;
            break;
        default:
            break;
    }

    return true;
}

}  // namespace jf