
local cjson = require "cjson";
local api = freeswitch.API();

function logN(c)
    if type(c) == "string" then
        freeswitch.consoleLog("NOTICE", c.."\n");
    end
end

function logI(c)
    if type(c) == "string" then
        freeswitch.consoleLog("INFO", c.."\n");
    end
end

function logD(c)
    if type(c) == "string" then
        freeswitch.consoleLog("DEBUG", c);
    end
end

function logE(c)
    if type(c) == "string" then
        freeswitch.consoleLog("ERR", c);
    end
end

if session:getVariable("lcc_hold_flag") then
    session:setVariable("lcc_hold_flag", nil);
    return;
end

local channel_name =  session:getVariable("channel_name") or "unkonw";
logN(channel_name.." negotiate handle start.");

local audio_media_flow = session:getVariable("audio_media_flow") or "nil";
local video_media_flow = session:getVariable("video_media_flow") or "nil";
local remote_audio_media_flow= session:getVariable("remote_audio_media_flow") or "nil";
local remote_video_media_flow = session:getVariable("remote_video_media_flow") or "nil";
local rtp_last_audio_codec_string = session:getVariable("rtp_last_audio_codec_string") or "nil";
local rtp_last_video_codec_string = session:getVariable("rtp_last_video_codec_string") or "nil";
local lcc_audio_media_flow = session:getVariable("lcc_audio_media_flow");
local lcc_video_media_flow = session:getVariable("lcc_video_media_flow");
local lcc_first_video_media_flow = session:getVariable("lcc_first_video_media_flow");
local endpoint_disposition = session:getVariable("endpoint_disposition") or "nil";

logI("audio_media_flow: "..audio_media_flow);
logI("video_media_flow: "..video_media_flow);
logI("remote_audio_media_flow: "..remote_audio_media_flow);
logI("remote_video_media_flow: "..remote_video_media_flow);
logI("rtp_last_audio_codec_string: "..rtp_last_audio_codec_string);
logI("rtp_last_video_codec_string: "..rtp_last_video_codec_string);
logI("lcc_audio_media_flow: "..(lcc_audio_media_flow or "nil"));
logI("lcc_video_media_flow: "..(lcc_video_media_flow or "nil"));
logI("lcc_first_video_media_flow: "..(lcc_first_video_media_flow or "nil"));
logI("endpoint_disposition: "..endpoint_disposition);

-- 获取上一次的状态，没有的话初始化为当前状态
local old_lcc_audio_media_flow = lcc_audio_media_flow or remote_audio_media_flow;
local old_lcc_video_media_flow = lcc_video_media_flow or remote_video_media_flow;

-- 赋值当前状态
lcc_audio_media_flow = remote_audio_media_flow;
lcc_video_media_flow = remote_video_media_flow;

-- 媒体是否发生变化
local audio_flow_change = (old_lcc_audio_media_flow ~= lcc_audio_media_flow);
local video_flow_change = (old_lcc_video_media_flow ~= lcc_video_media_flow);

-- 记录初始视频媒体信息
if lcc_first_video_media_flow == nil then
    lcc_first_video_media_flow = old_lcc_video_media_flow;
    session:setVariable("lcc_first_video_media_flow", lcc_first_video_media_flow);
end

-- B路状态逻辑处理，用于判定接通前是否需要先发送update，解决freeswitch视频呼出音频接起没有转成音频的BUG
local lcc_media_reneging = session:getVariable("lcc_media_reneging");
local lcc_media_reneg_req_id = session:getVariable("lcc_media_reneg_req_id");
local partner_uuid = session:getVariable("signal_bond") or session:getVariable("originate_signal_bond");
if partner_uuid then 
    local partner_endpoint_disposition = api:execute("uuid_getvar", partner_uuid.." endpoint_disposition");
    logI("partner_endpoint_disposition: "..partner_endpoint_disposition);
    local codec_string = "=";
    if rtp_last_audio_codec_string ~= "nil" then
        codec_string = codec_string..rtp_last_audio_codec_string;
    else
        codec_string = codec_string.."=PCMA@8000h@20i@1c";
    end

    if lcc_video_media_flow ~= "inactive" then
        if rtp_last_video_codec_string ~= "nil" then
            codec_string = codec_string..","..rtp_last_video_codec_string;
        else
            codec_string = codec_string..",H264@90000h";
        end
    end

    -- 1. 修改媒体协商参数、设置通道变量，在200OK前发送update
    if partner_endpoint_disposition ~= "ANSWER" then
        if video_flow_change then
            local arg = partner_uuid.." "..codec_string;
            logI("lcc_media_reset_before_answer: "..arg);
            local rr = api:execute("lcc_media_reset_before_answer", arg);
            if rr == "true" and lcc_first_video_media_flow ~= lcc_video_media_flow then
                api:execute("uuid_setvar", partner_uuid.." lcc_update_before_answer true")
                logI("lcc_update_before_answer: "..partner_uuid);
            end
            logI("lcc_media_reset_before_answer result: "..rr);
        end
    -- 2. 自动触发媒体切换
    elseif endpoint_disposition == "ANSWER" then
	-- 非主动触发的媒体切换
        local b_session = freeswitch.Session(partner_uuid);
        local lcc_first_reneging = session:getVariable("lcc_first_reneging");
        local b_lcc_first_reneging = b_session:getVariable("lcc_first_reneging");
        -- 如果B路有重协商标记，说明此次的媒体协商是由于B发起的，需要判定下A路是否有接收，没有接收需要给B路也发一次重协商(还原)
        if b_lcc_first_reneging or lcc_first_reneging == nil then
            if lcc_media_reneging == nil then
                -- 获取下B路的媒体状态
                local b_session = freeswitch.Session(partner_uuid);
                local lcc_audio_media_flow_b = b_session:getVariable("lcc_audio_media_flow");
                local lcc_video_media_flow_b = b_session:getVariable("lcc_video_media_flow");
                local a_sendonly_b_recvonly = true;
                --if lcc_video_media_flow == "sendrecv" then
                --	lcc_video_media_flow = "sendonly";
                --end
				-- 判断如果是一方sendonly另一方recvonly是正常情况无需再发起reinvite
                if ( lcc_audio_media_flow_b == "sendonly" and lcc_audio_media_flow == "recvonly" ) or ( lcc_audio_media_flow_b == "recvonly" and lcc_audio_media_flow == "sendonly" ) 
                or ( lcc_video_media_flow_b == "sendonly" and lcc_video_media_flow == "recvonly" ) or ( lcc_video_media_flow_b == "recvonly" and lcc_video_media_flow == "sendonly" ) then
                    a_sendonly_b_recvonly = false;
                end
                if ( lcc_audio_media_flow_b ~= lcc_audio_media_flow or lcc_video_media_flow_b ~= lcc_video_media_flow ) and a_sendonly_b_recvonly then
                    -- 媒体流发生变更，重新协商
                    b_session:setVariable("lcc_video_media_reneg_flow", lcc_video_media_flow);
                    b_session:setVariable("lcc_audio_media_reneg_flow", lcc_audio_media_flow);
                    -- 给A路打上标记，说明此次的媒体是由A路触发的，等B的回复结果，如果拒绝了，那么还要把A路的媒体还原
                    if b_lcc_first_reneging == nil then 
                        session:setVariable("lcc_first_reneging", "true");
                        logI("媒体发生变化，自动发起reinvite: "..partner_uuid.." "..codec_string.." "..lcc_video_media_flow);
                    else
                        logI("协商拒绝，给发起方发起reinvite，还原媒体: "..partner_uuid.." "..codec_string.." "..lcc_video_media_flow);
                    end
                    api:execute("uuid_media_reneg", partner_uuid.." "..codec_string);
                else
                    b_session:setVariable("lcc_first_reneging", nil);
                end
            end
        end
        -- 说明是自己发起的重协商，清掉标记
        if lcc_first_reneging then
            session:setVariable("lcc_first_reneging", nil);
        end
    else
        -- a路接通，bridge呼叫b路。b路183-video inactive->b路update-video sendrecv。在b路接通的时候再协商一次媒体。
        if video_flow_change or audio_flow_change then
            session:setVariable("execute_on_answer_negotiation", "lua lcc_negotiate_200OK.lua");
        end
    end
end

-- 如果媒体发生变更，推送事件
if lcc_media_reneging or video_flow_change or audio_flow_change then
    session:setVariable("lcc_audio_media_flow", lcc_audio_media_flow);
    session:setVariable("lcc_video_media_flow", lcc_video_media_flow);

    local lcc_media_reneg_req_id = session:getVariable("lcc_media_reneg_req_id");
    logI("lcc_media_reneging: "..(lcc_media_reneging or "nil"));
    logI("lcc_media_reneg_req_id: "..(lcc_media_reneg_req_id or "nil"));
    if lcc_media_reneging == "true" then
        lcc_media_reneging = true;
    else
        lcc_media_reneging = false;
        lcc_media_reneg_req_id = "";
    end
    session:setVariable("lcc_media_reneging", nil);
    session:setVariable("lcc_media_reneg_req_id", nil);

    local data = {};
    data.status = "MEDIARENEG";
    data.caller_number = session:getVariable("lcc_caller_number");
    data.callee_number = session:getVariable("lcc_callee_number");
    data.last_audio = string.upper(old_lcc_audio_media_flow);
    data.last_video = string.upper(old_lcc_video_media_flow);
    data.audio = string.upper(lcc_audio_media_flow);
    data.video = string.upper(lcc_video_media_flow);
    data.media_reneg_request = lcc_media_reneging;
    data.req_id = lcc_media_reneg_req_id;

    local mq_msg = {};
    mq_msg.event = "call_status";
    mq_msg.server_id = freeswitch.getGlobalVariable("server_name");
    mq_msg.uuid = session:getVariable("uuid");
    mq_msg.company_code = session:getVariable("lcc_company_code");
    mq_msg.service_code = session:getVariable("lcc_service_code");
    mq_msg.timestamp = os.time()*1000;
    mq_msg.data = data;

    local json_text = cjson.encode(mq_msg);
    local event_queue_id = session:getVariable("lcc_event_queue_id") or mq_msg.service_code;

    logN("send call_status(MEDIARENEG) event: "..mq_msg.uuid);
    if mq_msg.company_code ~= nil then
        api:execute("push_rocketmq_event", event_queue_id.." "..mq_msg.company_code.." "..mq_msg.uuid.." "..json_text);
    end
end

-- 发送媒体通道建立事件
-- session:answered()
if endpoint_disposition ~= "ANSWER" then
    -- 获取下变量是否有发送过MEDIA事件
    local lcc_media_event_sent = session:getVariable("lcc_media_event_sent");
    if lcc_media_event_sent == nil then
        local data = {};
        data.status = "MEDIA";
        data.caller_number = session:getVariable("lcc_caller_number");
        data.callee_number = session:getVariable("lcc_callee_number");
        data.req_id = lcc_media_reneg_req_id;

        local mq_msg = {};
        mq_msg.event = "call_status";
        mq_msg.server_id = freeswitch.getGlobalVariable("server_name");
        mq_msg.uuid = session:getVariable("uuid");
        mq_msg.company_code = session:getVariable("lcc_company_code");
        mq_msg.service_code = session:getVariable("lcc_service_code");
        mq_msg.timestamp = os.time()*1000;
        mq_msg.data = data;
        
        local json_text = cjson.encode(mq_msg);
        local event_queue_id = session:getVariable("lcc_event_queue_id") or mq_msg.service_code;

        if mq_msg.company_code ~= nil then
            logN("send call_status(MEDIA) event: "..mq_msg.uuid);
            session:setVariable("lcc_media_event_sent", "true");
            api:execute("push_rocketmq_event", event_queue_id.." "..mq_msg.company_code.." "..mq_msg.uuid.." "..json_text);
        end
    end
end
logN(channel_name.." negotiate handle end.");
