#include "device_config.h"

#include <nlohmann/json.hpp>

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>

namespace app {
namespace {

using json = nlohmann::json;

bool parse_resolution(std::string value, int &width, int &height){
    std::transform(value.begin(), value.end(), value.begin(),
                   [](unsigned char c){ return static_cast<char>(std::tolower(c)); });
    if(value == "1080p"){ width = 1920; height = 1080; return true; }
    if(value == "720p") { width = 1280; height = 720; return true; }
    if(value == "360p") { width = 640;  height = 360; return true; }

    int parsed_width = 0;
    int parsed_height = 0;
    char trailing = '\0';
    if(std::sscanf(value.c_str(), "%dx%d%c",
                   &parsed_width, &parsed_height, &trailing) != 2 ||
       parsed_width <= 0 || parsed_height <= 0 ||
       (parsed_width & 1) || (parsed_height & 1)){
        return false;
    }
    width = parsed_width;
    height = parsed_height;
    return true;
}

bool take_value(int argc, char **argv, int &index, const std::string &option,
                std::string &value){
    if(index + 1 >= argc){
        std::cerr << "missing value for " << option << "\n";
        return false;
    }
    value = argv[++index];
    return true;
}

bool take_int(int argc, char **argv, int &index, const std::string &option,
              int &value){
    std::string text;
    if(!take_value(argc, argv, index, option, text)) return false;
    char *end = nullptr;
    const long parsed = std::strtol(text.c_str(), &end, 10);
    if(!end || *end != '\0'){
        std::cerr << "invalid integer for " << option << ": " << text << "\n";
        return false;
    }
    value = static_cast<int>(parsed);
    return true;
}

} // namespace

void load_device_config(const std::string &path,
                        webrtcpush::StreamConfig &config){
    std::ifstream input(path);
    if(!input.good()){
        std::cerr << "[config] " << path << " not found; using defaults\n";
        return;
    }

    try {
        const json root = json::parse(input);
        if(!root.is_object()) throw std::runtime_error("root must be a JSON object");
        webrtcpush::StreamConfig loaded = config;

        if(root.contains("deviceid")){
            if(root["deviceid"].is_string() &&
               !root["deviceid"].get<std::string>().empty()){
                loaded.room = root["deviceid"].get<std::string>();
            }else{
                std::cerr << "[config] invalid deviceid; using " << loaded.room << "\n";
            }
        }

        auto positive_integer = [&](const char *key, int &target){
            if(!root.contains(key)) return;
            if(root[key].is_number_integer() && root[key].get<int>() > 0)
                target = root[key].get<int>();
            else
                std::cerr << "[config] invalid " << key << "; using " << target << "\n";
        };
        positive_integer("push_frame", loaded.fps);
        positive_integer("bitrate", loaded.bitrate);
        positive_integer("gop", loaded.gop);

        if(root.contains("resolution")){
            int width = loaded.width;
            int height = loaded.height;
            if(root["resolution"].is_string() &&
               parse_resolution(root["resolution"].get<std::string>(), width, height)){
                loaded.width = width;
                loaded.height = height;
            }else{
                std::cerr << "[config] invalid resolution; use 1080p, 720p, 360p or WIDTHxHEIGHT\n";
            }
        }

        config = loaded;
        std::cerr << "[config] loaded " << path << "\n";
    }catch(const std::exception &e){
        std::cerr << "[config] cannot parse " << path << ": " << e.what()
                  << "; using defaults\n";
    }
}

ParseResult apply_command_line(int argc, char **argv,
                               webrtcpush::StreamConfig &config){
    for(int i = 1; i < argc; ++i){
        const std::string option = argv[i];
        std::string value;
        if(option == "--room"){
            if(!take_value(argc, argv, i, option, config.room)) return ParseResult::Error;
        }else if(option == "--signal"){
            if(!take_value(argc, argv, i, option, config.signal_url)) return ParseResult::Error;
        }else if(option == "--source"){
            if(!take_value(argc, argv, i, option, config.source)) return ParseResult::Error;
        }else if(option == "--device"){
            if(!take_value(argc, argv, i, option, config.device)) return ParseResult::Error;
        }else if(option == "--format"){
            if(!take_value(argc, argv, i, option, config.format)) return ParseResult::Error;
        }else if(option == "--profile"){
            if(!take_value(argc, argv, i, option, config.profile)) return ParseResult::Error;
        }else if(option == "--iq-dir"){
            if(!take_value(argc, argv, i, option, config.iq_dir)) return ParseResult::Error;
        }else if(option == "--src-width"){
            if(!take_int(argc, argv, i, option, config.src_w)) return ParseResult::Error;
        }else if(option == "--src-height"){
            if(!take_int(argc, argv, i, option, config.src_h)) return ParseResult::Error;
        }else if(option == "--width"){
            if(!take_int(argc, argv, i, option, config.width)) return ParseResult::Error;
        }else if(option == "--height"){
            if(!take_int(argc, argv, i, option, config.height)) return ParseResult::Error;
        }else if(option == "--fps"){
            if(!take_int(argc, argv, i, option, config.fps)) return ParseResult::Error;
        }else if(option == "--bitrate"){
            if(!take_int(argc, argv, i, option, config.bitrate)) return ParseResult::Error;
        }else if(option == "--gop"){
            if(!take_int(argc, argv, i, option, config.gop)) return ParseResult::Error;
        }else if(option == "--camera-id"){
            if(!take_int(argc, argv, i, option, config.camera_id)) return ParseResult::Error;
        }else if(option == "--no-scaler"){
            config.use_scaler = false;
        }else if(option == "--no-isp"){
            config.use_isp = false;
        }else if(option == "-h" || option == "--help"){
            print_usage();
            return ParseResult::Help;
        }else{
            std::cerr << "unknown option: " << option << "\n";
            print_usage();
            return ParseResult::Error;
        }
    }
    return ParseResult::Ok;
}

bool validate_config(const webrtcpush::StreamConfig &config){
    if(config.room.empty()){
        std::cerr << "deviceid/--room must be non-empty\n";
        return false;
    }
    if(config.source != "test" && config.source != "camera"){
        std::cerr << "invalid source: " << config.source << "\n";
        return false;
    }
    if(config.format != "NV12" && config.format != "YUYV"){
        std::cerr << "invalid format: " << config.format << "\n";
        return false;
    }
    if(config.profile != "baseline" && config.profile != "main" &&
       config.profile != "high"){
        std::cerr << "invalid profile: " << config.profile << "\n";
        return false;
    }
    if(config.src_w <= 0 || config.src_h <= 0 ||
       config.width <= 0 || config.height <= 0 ||
       config.fps <= 0 || config.bitrate <= 0 || config.gop <= 0 ||
       (config.src_w & 1) || (config.src_h & 1) ||
       (config.width & 1) || (config.height & 1)){
        std::cerr << "dimensions must be positive/even; fps, bitrate and gop must be positive\n";
        return false;
    }
    if(config.source == "camera" && !config.use_scaler &&
       (config.src_w != config.width || config.src_h != config.height)){
        std::cerr << "--no-scaler requires source and encoder dimensions to match\n";
        return false;
    }
    if(config.source == "camera" && config.use_isp &&
       (config.iq_dir.empty() || config.camera_id < 0)){
        std::cerr << "iq-dir must be non-empty and camera-id must be non-negative\n";
        return false;
    }
    return true;
}

void print_usage(){
    std::cerr
        << "webrtcpush [--room <roomId>] [--source test|camera] [--device /dev/video11]\n"
        << "  [--src-width 2304 --src-height 1296]\n"
        << "  [--width 1080 --height 720 --fps 24 --bitrate 3000 --gop 48]\n"
        << "  [--profile high|main|baseline] [--no-scaler]\n"
        << "  [--iq-dir /oem/usr/share/iqfiles] [--camera-id 0] [--no-isp]\n"
        << "  [--signal wss://signal.yd-ss.com/websocket]\n";
}

} // namespace app
