#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <thread>
#include "trro_field.h"
#include <map>
#include <mutex>
#include <condition_variable>
#include <chrono>

static std::map<int, std::string> g_Record_Files;
std::mutex retry_mtx; 
std::condition_variable retry_cv;
#define MAX_RETRY 30 // maximum retry times for license check error 

static inline long long GetTime2() {

	auto now = std::chrono::system_clock::now();
	auto duration = now.time_since_epoch();
	auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();

	return millis;
}

void onControlData(void * context, const char* controlid, const char* msg, int len, int cmd) {
	
	printf("received controller msg: %s\n len %d qos:%d\n", msg, len, cmd);
	std::string out(msg);
	auto pos = out.find_last_of(':');
    if (pos != std::string::npos) {
        std::string old = out.substr(pos + 1, out.length());
        long long oldtime = std::atoll(old.c_str());
        long long newtime = GetTime2();
    } 

	// 将收到的消息回传给发送方（controlid 即来源设备ID）
	int ret = TRROField_sendControlDataV2(controlid, msg, len, cmd, 0, 0);
	if (ret != TRRO_SUCCEED) {
		const char* err_msg = TRROField_getErrorMsg(-ret);
		printf("sendControlDataV2 failed: %s (ret=%d)\n", err_msg, ret);
	}
}

void OnConnectState(void* context, int stream_id, int state) {
	printf("stream_id: %d, state: %d\n", stream_id, state);
}

void externalOutYuvBuffer(const char* filename, int id) {
	int data_size = 1280*720*3/2;
	int data_width = 1280;
	int data_height = 720;
	FILE *yuv_file = fopen(filename, "rb+");
	char *data = (char*)malloc(data_size);
	memset(data, 0, data_size);
	while(true) {
		if(yuv_file == nullptr) {
			return;
		}
		
		int size = fread(data, 1, data_size, yuv_file);
		if (size < 1280) {
			fseek(yuv_file, 0 , SEEK_SET);
			fread(data, 1, data_size, yuv_file);
		}
		auto start = std::chrono::high_resolution_clock::now();
		int ret = TRROField_externalVideoDataWithText(id, data, data_width, data_height, 0, 0, nullptr);
		if (ret != TRRO_SUCCEED) {
			// TRRO_SESSION_INACTIVE: 会话未激活，跳过本帧，等待激活
			// TRRO_INIT_ERROR: SDK 已停止，退出循环
			if (ret == -TRRO_INIT_ERROR) {
				printf("externalVideoData: SDK stopped, exit capture loop\n");
				break;
			}
			// 其他错误（如 TRRO_INIT_PRM_ERROR）打印后继续
			printf("externalVideoData failed: ret=%d\n", ret);
		}
		auto end = std::chrono::high_resolution_clock::now();
		auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
		printf("TRROField_externalVideoDataWithText cost: %ld us\n", duration);
		
		int remaining_us = 33000 - duration; // 33ms per frame for ~30fps
		if (remaining_us > 0) {
			usleep(remaining_us);
		}
	}
	if (yuv_file) {
		fclose(yuv_file);
	}
	free(data);
}

void signal_callback(void *context, SignalState state) {
	if(state == kTrroReady) {
		printf("TRRO_init >> init success \n");
		TRROField_registerControlDataCallback(nullptr, onControlData);
		TRROField_registerOnState(nullptr, OnConnectState);

			// 注册异步错误事件回调，处理 SDK 内部异步错误
			TRROField_registerOnErrorEvent(nullptr, [](void* context, int error_code, const char* error_msg) {
				printf("async error_code=%d, error_msg=%s\n", error_code, error_msg);

				// error_code 为负值，取反后与错误码宏比较
				int code = -error_code;

				if (code == TRRO_ERROR_CALLBACK_CAMERA) {
					// 相机采集失败：设备异常或未连接
					// 处理建议：检查摄像头设备，或切换为外部视频输入模式
					printf("[ERROR] Camera capture failed. Check camera device or switch to external video input.\n");

				} else if (code == TRRO_ERROR_CALLBACK_MIC) {
					// 麦克风采集失败：设备异常或未连接
					// 处理建议：检查麦克风设备，或切换为外部音频输入模式
					printf("[ERROR] Microphone capture failed. Check mic device or switch to external audio input.\n");

				} else if (code == TRRO_ERROR_CALLBACK_BANDWIDTH_LIMIT) {
					// 网络带宽不足，无法满足所有流的最低码率需求
					// 处理建议：降低配置中各流的 min_bitrate，或减少推流路数
					printf("[WARN] Network bandwidth cannot meet minimum bitrate. Consider reducing min_bitrate or stream count.\n");

				} else if (code == TRRO_ERROR_CALLBACK_RESERVE_DEGRADE) {
					// reserve 带宽降级（告警性，不影响推流）
					// 处理建议：记录日志，持续出现时考虑降低码率配置
					printf("[WARN] Reserve bandwidth degraded due to insufficient network bandwidth.\n");

				} else if (code == TRRO_ERROR_CALLBACK_JOIN_AUTH_FAILED) {
					// RTC 进房鉴权失败（后台黑白名单限制、UserSig 非法或已过期）
					// 处理建议：检查后台房间权限配置及 UserSig 是否有效
					printf("[ERROR] RTC join room auth failed. Check room permission or UserSig. detail: %s\n", error_msg);

				} else if (code == TRRO_MESSAGE_CHANNEL) {
					// 数据通道异常（通道未找到/未连接/SCTP发送失败）
					// 处理建议：等待连接恢复后重试发送
					printf("[ERROR] Data channel error. Wait for reconnection before retrying.\n");

				} else if (code == TRRO_MESSAGE_BLOCK) {
					// 数据通道发送缓冲区已满
					// 处理建议：降低发送频率，等待缓冲区释放
					printf("[WARN] Data channel send buffer full. Reduce send frequency.\n");

				} else if (code == TRRO_MESSAGE_BYTE_EXCEED) {
					// 单条消息超出数据通道字节限制
					// 处理建议：拆分消息后重新发送
					printf("[ERROR] Data channel message too large. Split the message and retry.\n");

				} else if (code == TRRO_MESSAGE_RATE_EXCEED) {
					// 数据通道发送频率过高
					// 处理建议：增加发送间隔
					printf("[WARN] Data channel send rate too high. Increase send interval.\n");

				} else if (code == TRRO_CONFIG_CERTIFICATE_MD5_INVALID) {
					// 证书文件 MD5 校验失败（证书被篡改或路径错误）
					// 处理建议：检查 config.json 中 certificate 字段指向的证书文件
					printf("[WARN] Certificate MD5 mismatch. Check the certificate file in config.json.\n");

				} else {
					// 未知异步错误，记录日志
					printf("[ERROR] Unknown async error: code=%d, msg=%s\n", code, error_msg);
				}
			});

			TRROField_registerVideoCaptureCallback(nullptr, [](void *context, const char* data, int width, int height, int type, int stream_id) {});
			TRROField_registerLatencyCallback(nullptr, [](void *context, int stream_id, int vcct){
					printf("latency stream id %d, vcct %d\n", stream_id, vcct);
				});

			TRROField_registerAudioMediaState(nullptr, [](void* context, int stream_id, int fps, int bps, int rtt, long long lost, long long packets_send, int stun) {
					printf("audio stream %d, fps %d, bps %d, rtt %d, lost %lld, packets_send %lld, stun %d\n", stream_id, fps, bps, rtt, lost, packets_send, stun);
				});

			TRROField_registerMediaState(nullptr, [](void* context, int stream_id, int fps, int bps, int rtt, long long lost, long long packets_send, int stun) {
					printf("stream %d, fps %d, bps %d, rtt %d, lost %lld, packets_send %lld, stun %d\n", stream_id, fps, bps, rtt, lost, packets_send, stun);
				});
				
			TRROField_registerOperationPermissionRequest(nullptr, [](void* context, const char* remote_devid, int permission) {
					printf("remote devid %s permission %d\n", remote_devid, permission);
				});

			TRROField_registerMultiNetworkStatsCallback(nullptr, [](void* context, const TrroMultiNetworkStats stats) {
					printf("multi network local:%s:%d extern:%s:%d rtt %f lost %f recv %ld send %ld\n", 
						stats.local_ip, stats.local_port, stats.extern_ip, stats.extern_port, stats.rtt, stats.lost, stats.recv_bytes, stats.send_bytes);
				});

			TRROField_registerRemoteMixAudioFrameCallback(nullptr, [](void* context, const char* data, int length, int channels, int sample_rate) {
					printf("remote mix audio frame length %d, sample_rate %d, channels %d\n", length, sample_rate, channels);
				});

			TRROField_registerVideoEncodeCallback(nullptr, [](void* context, const char* data, int length, int width, int height, int type, int frame_type, int stream_id) {
				printf("encode video width %d, height %d, size %d, type %d, frame %d, stream %d\n", width, height, length, type, frame_type, stream_id);
				});
			retry_cv.notify_one(); //server connect access, notify to call trro_start 
		} else if(state == kTrroAuthFailed) {
			// 设备ID或密码错误，无法连接信令服务器，需检查配置
			printf("[ERROR] Auth failed: device_id or password is incorrect. Check config.json.\n");
		} else if(state == kTrroKickout) {
			// 被踢出（同一设备ID在其他地方登录），停止 SDK
			printf("[WARN] Kicked out by server (duplicate login). Stopping SDK.\n");
			TRROField_stop();
		} else if(state == kTrroLost) {
			// 信令连接断开，SDK 内部会自动重连，无需手动处理
			printf("[WARN] Signal connection lost, SDK is reconnecting...\n");
		} else if(state == kTrroReup) {
			// 自动重连成功
			printf("[INFO] Signal reconnected successfully.\n");
		}
}

int main() {
	
	TRROField_registerSignalStateCallback(nullptr, signal_callback);
	TRROField_registerOnErrorEvent(nullptr, [](void* context, int error_code, const char* error_msg) {
		printf("async error_code=%d, error_msg=%s\n", error_code, error_msg);
	});

	// 异步模式初始化 SDK，等待 signal_callback 中 kTrroReady 回调后再调用 TRROField_start
	int mode = -1; // 异步起动模式
	int ret = TRROField_initGwPathWithLicense("./config.json", "./license.txt", mode);
	if (TRRO_SUCCEED != ret) {
		const char* init_msg = TRROField_getErrorMsg(-ret);
		printf("[ERROR] TRROField_init failed: %s (ret=%d)\n", init_msg, ret);
		// 初始化失败，无法继续，直接退出
		// 常见原因：
		//   TRRO_CONFIG_UNEXIST          - config.json 或 license.txt 文件不存在
		//   TRRO_CONFIG_PARSE_FAILED     - config.json 格式错误
		//   TRRO_CONFIG_ILLEGAL          - 配置参数非法（如 device_id 格式错误）
		//   TRRO_INIT_LICENSE_FILE_ERROR - license 文件损坏或格式错误
		//   TRRO_INIT_LICENSE_CHECK_TIME_FAILED   - license 已过期
		//   TRRO_INIT_LICENSE_CHECK_DEVICE_FAILED - license 硬件验证失败（设备不匹配）
		//   TRRO_INIT_LICENSE_CHECK_STREAM_FAILED - license 授权流数小于配置流数
		//   TRRO_INIT_LICENSE_CHECK_ID_FAILED     - license 设备ID验证失败
		//   TRRO_INIT_REPEAT             - SDK 已初始化，重复调用
		//	 TRRO_SIGNAL_CONNECT_OUTTIME - 信令连接超时 (mode == -1 异步模式会直接返回)
		if(mode != -1 || ret != -TRRO_SIGNAL_CONNECT_OUTTIME) {
		}
	}

 	{
		// 等待信令连接成功（kTrroReady 回调通知）
        std::unique_lock<std::mutex> lock(retry_mtx);
		retry_cv.wait(lock);

		// 调用 TRROField_start，失败时针对 license 超时进行重试
		int retryCount = 0; 
	
		while (retryCount < MAX_RETRY) {
			int ret = TRROField_start();
			if (ret == TRRO_SUCCEED || ret == -TRRO_INIT_REPEAT) {
				printf("[INFO] TRROField_start success.\n");
				break; 
			} else if (ret == -TRRO_INIT_PUBLIC_LICENSE_CHECK_TIMEOUT) {
				// 公有云 license 验证超时，重试
				retryCount++;
				printf("[WARN] TRROField_start: license check timeout, retry %d/%d\n", retryCount, MAX_RETRY);
				sleep(1);
			} else if (ret == -TRRO_INIT_PUBLIC_LICENSE_CHECK_NOT_BIND) {
				// 设备未绑定 license，无法重试，直接退出
				printf("[ERROR] TRROField_start: device not bound to license. Please bind license in console.\n");
				return -1;
			} else if (ret == -TRRO_INIT_PUBLIC_LICENSE_CHECK_NOT_ENOUGH) {
				// license 数量不足，无法重试
				printf("[ERROR] TRROField_start: license count not enough. Please purchase more licenses.\n");
				return -1;
			} else if (ret == -TRRO_INIT_PUBLIC_LICENSE_CHECK_OVERTIME) {
				// 公有云 license 已过期
				printf("[ERROR] TRROField_start: license expired. Please renew license.\n");
				return -1;
			} else if (ret == -TRRO_INIT_PUBLIC_LICENSE_CHECK_NOT_DURATION) {
				// 公有云 license 剩余时长不足
				printf("[ERROR] TRROField_start: license duration not enough. Please renew license.\n");
				return -1;
			} else {
				// 其他错误，打印后退出
				const char* start_msg = TRROField_getErrorMsg(-ret);
				printf("[ERROR] TRROField_start failed: %s (ret=%d)\n", start_msg, ret);
				return -1;
			}
		}
		if (retryCount == MAX_RETRY) {
			printf("[ERROR] TRROField_start: license check timeout after %d retries, giving up.\n", MAX_RETRY);
			return -1;
		}
        
    }

	const char* ver = TRROField_getSdkVersion();
	printf("sdk version is %s\n", ver);

    //optionally test the network
	// std::thread thread1([] {
	// 	sleep(5);
	// 	int i = 0;
	// 	int ret = TRROField_testNetworkQuality(&i, 1, 0);
	// 	printf("TRROField_testNetworkQuality ret = %d\n", ret);
	// });

	/* input YUV data to SDK for video transmission, the stream should be configured with protocol outside*/
    std::thread t1(externalOutYuvBuffer, "./720p_30.yuv", 0);
    t1.detach();

    // manually use the capture api to get camera data and input to SDK, the stream should be configured with protocol outside
	// note that the SDK can automatically capture and trasmit if the stream is configured with protocol v4l2 
	// unsigned long long capture_id = 0;
	// TRROField_startVideoCapture(nullptr, "/dev/video8", (Trro_VideoCaptureProtocol)1, (TrroColor)4, 1920, 1080, 30, 
	// 	[](void *context, unsigned long long capture_id, const char* data, int length, int width, int height, TrroColor color_format){
	// 		TRROField_externalVideoData(0, data, width, height, color_format);
	// 	}, 
	// &capture_id);

    while(true){
        sleep(30000);
    }
	return 0;

}


