void list_devices ()
{
    UsbHandler usb;

    std::vector<device_info> devs;
    try
    {
         devs = usb.get_device_list();
    }
    catch (const std::runtime_error& err)
    {
        std::cout << err.what() << std::endl;

        exit(1);
    }
    unsigned short name_width = 14;
    unsigned short id_width = 9;
    unsigned short serial_width = 12;

    std::cout << std::endl << std::left
              << "Found " << devs.size() << " device(s)." << std::endl << std::endl
              << std::setw(name_width)   << "Name" << " - "
              << std::setw(id_width)     << "ID " << " - "
              << std::setw(serial_width) << "Serialnumber" << std::endl;

    for (auto& d : devs)
    {
        std::cout << std::setw(name_width) << d.product
                  << " - " << std::hex << d.idVendor << ":" << d.idProduct << std::dec
                  << " - " << d.serial<< std::endl;
    }
    std::cout << std::endl;

}
void delete_firmware (std::string& serial_number)
{
    UsbHandler usb;

    auto cam = usb.open_camera(serial_number);

    if (cam == NULL)
    {
        std::cerr << "Unable to find device with serial \""
                  << serial_number << "\"" << std::endl;
        return;
    }
    std::cout << std::endl;

    auto func = [] (int progress)
        {
            std::cout << "\r    " << progress << " %";

            std::cout.flush();
        };

    cam->delete_firmware(func);
    std::cout << std::endl;

}
void print_device_info (const std::string& serial_number)
{
    UsbHandler usb;

    auto cam = usb.open_camera(serial_number);

    if (cam == NULL)
    {
        std::cerr << "Unable to find device with serial \""
                  << serial_number << "\"" << std::endl;
        return;
    }

    auto d = cam->get_device_info();

    std::cout << std::endl
              << "Device manufacturer: " << d.manufacturer << std::endl
              << "Product name:        " << d.product << std::endl
              << "Serial number:       " << d.serial << std::endl
              << "VendorID:ProductID:  " << std::hex << d.idVendor << ":" << d.idProduct << std::dec << std::endl;

    if (cam->get_firmware_version() != -1)
    {
        std::cout << "Firmware version:    " << cam->get_firmware_version() << std::endl;
    }
    else
    {
        std::cout << "Firmware version:    " << cam->get_firmware_version_string() << std::endl;
    }

    auto type = cam->get_camera_type();

    if (type.camera_type == USB2)
    {
        UVC_COMPLIANCE mode = cam->get_mode();

        std::cout << "UVC mode is:         ";
        if (mode == CAMERA_INTERFACE_MODE_UVC)
            std::cout << "on" << std::endl;
        else
            std::cout << "off" << std::endl;

        std::cout << "Camera EEPROM size:  ";
        std::cout << cam->get_eeprom_size();
        std::cout << std::endl;
    }
    else if (type.camera_type == USB3 && cam->get_firmware_version() < 102)
    {
        std::cout << "\n!!! FIRMWARE UPGRADE REQUIRED !!!\n\n";
        std::cout << "To correctly interact with this camera under Linux\n";
        std::cout << "this device requires a firmware upgrade.\n\n";
        std::cout << "Please contact the manufacturer to receive the concerning firmware files." << std::endl;
    }

    std::cout << std::endl;
}
// toggle uvc mode in USB2 cameras
// USB3 cameras do net have this "feature" and have to be ignored
void set_device_mode (const std::string& serial_number, const std::string& mode)
{
    UsbHandler usb;

    auto cam = usb.open_camera(serial_number);

    if (cam == NULL)
    {
        std::cerr << "Unable to find device with serial \""
                  << serial_number << "\"" << std::endl;
        return;
    }
    auto type = cam->get_camera_type();

    if (type.camera_type == USB3)
    {
        std::cerr << "Mode settings only available for USB2 cameras." << std::endl;
        exit(69);
    }

    int ret = -1;
    if (mode.compare("uvc") == 0 || mode.compare("u") == 0)
    {
        std::cout << std::endl << "Setting camera to UVC mode ... ";
        ret = cam->set_mode(CAMERA_INTERFACE_MODE_UVC);
    }
    else if (mode.compare("proprietary") == 0 || mode.compare("p") == 0)
    {
        std::cout << std::endl << "Setting camera to PROPRIETARY mode ... ";
        ret = cam->set_mode(CAMERA_INTERFACE_MODE_PROPRIETARY);
    }
    else
    {
        std::cerr << std::endl
                  << "Unknown mode identifier \"" << mode << "\"." << std::endl
                  << "Please try again with a valid one." << std::endl
                  << "Allowed ones are: 'uvc' and 'proprietary'." << std::endl << std::endl;
        return;
    }

    if (ret >= 0)
    {
        std::cout << "SUCCESS" << std::endl << std::endl;
    }
    else
    {
        std::cout << "FAILED - " << ret << std::endl << std::endl;
    }
}
void print_device_info (const std::string& serial_number)
{
    UsbHandler usb;

    auto cam = usb.open_camera(serial_number);

    if (cam == NULL)
    {
        std::cerr << "Unable to find device with serial \""
                  << serial_number << "\"" << std::endl;
        return;
    }

    auto d = cam->get_device_info();

    std::cout << std::endl
              << "Device manufacturer: " << d.manufacturer << std::endl
              << "Product name:        " << d.product << std::endl
              << "Serial number:       " << d.serial << std::endl
              << "VendorID:ProductID:  " << std::hex << d.idVendor << ":" << d.idProduct << std::dec << std::endl
              << "Firmware version:    " << cam->get_firmware_version() << std::endl;

    auto type = cam->get_camera_type();

    if (type.camera_type == USB2)
    {
        UVC_COMPLIANCE mode = cam->get_mode();

        std::cout << "UVC mode is:         ";
        if (mode == CAMERA_INTERFACE_MODE_UVC)
            std::cout << "on" << std::endl;
        else
            std::cout << "off" << std::endl;

    }

    std::cout << std::endl;
}
void upload_to_device (const std::string& serial_number, const std::string& firmware)
{
    UsbHandler usb;

    auto cam = usb.open_camera(serial_number);

    if (cam == NULL)
    {
        std::cerr << "Unable to find device with serial \""
                  << serial_number << "\"" << std::endl;
        return;
    }
    std::cout << std::endl;

    std::cout << "!!! Attention !!!" << std::endl
              << "This action could break your camera." << std::endl << std::endl
              << "Do you really want to proceed? [y/N] ";

    while (1)
    {
        std::string s;
        std::getline(std::cin, s);
        if (s.compare("y") == 0 || s.compare("Y") == 0 )
            break;
        else if (s.empty() || s.compare("n") == 0 || s.compare("N") == 0 )
        {
            std::cout << "Aborting..." << std::endl;
            return;
        }
        else
        {
            std::cout << "Please answer yes or no." << std::endl;
        }
    }

    auto func = [] (int progress)
        {
            std::cout << "\r    " << progress << " %";

            std::cout.flush();
        };

    device_info dev = cam->get_device_info();
    std::stringstream sstream;
    sstream << std::hex << dev.idProduct;

    std::string fw_name = sstream.str() + ".fw";
    bool success = false;
    try
    {
        success = cam->upload_firmware(firmware, fw_name, func);
    }
    catch (const std::runtime_error& err)
    {
        std::cout << std::endl << "There was a mistake. " << std::endl
                  << err.what() << std::endl;
    }

    // cam->reset

    if (success)
    {
        std::cout << std::endl << std::endl
                  << "Upload successful!" << std::endl;

        if (dev.idVendor != 0x04b4)
        {
            std::cout << "Please reconnect your camera."<< std::endl
                      << std::endl;
        }
        else
        {
            std::cout << std::endl;
        }
    }
    else
    {
        std::cout << std::endl
                  << "Firmware update not successful..." << std::endl
                  << "DO NOT DISCONNECT YOUR CAMERA!" << std::endl
                  << "Please try again." << std::endl
                  << std::endl;
    }
}