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

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

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

function string_split(target, sep)
	if target == nil then return {} end
	local sep, fields = sep or ":", {}
	local pattern = "([^"..sep.."]+)"
	target:gsub(pattern, function(c) table.insert(fields, c) end)
	return fields
end

function analysis_media(cur_media, v)
    -- 解析媒体类型
    local m_info = string_split(v, " ");
    local a = cjson.encode(m_info);

    cur_media.rtp_port = m_info[1];
    cur_media.transport= m_info[2];

    local c =  #m_info;
    for m = 3, c do
        table.insert(cur_media.rtpmaps, m_info[m]);
        -- if m_info[m] == "8" then
        --     cur_media["8"] = "PCMA/8000";
        -- elseif m_info[m] == "0" then
        --     cur_media["0"] = "PCMU/8000";
        -- end
    end

    if cur_media.rtp_port == "0" then
        cur_media.value = "NONE";
    end
end

function analysis_rtmp(cur_media, v) 
    local f = string.find(v, " ");
    if f == nil then return; end

    local item = {};
    item.value = string.sub(v, f+1, -1);
    cur_media.rtpmaps_value[string.sub(v, 0, f-1)] = item;
end

function analysis_fmtp(cur_media, v) 
    local f = string.find(v, " ");
    if f == nil then return; end

    local pt = string.sub(v, 0, f-1);
    fmtp = string.sub(v, f+1, -1);
    local item = cur_media.rtpmaps_value[pt];
    if item then
        if string.find(item.value, "telephone") then
            cur_media.fmtp = fmtp;
            cur_media.fmtp_pt = pt;
        end
        item.fmtp = fmtp;
    end
end

-- 解析sdp信息
function analysis_sdp(c)
    -- 音频信息
    local a = {};
    a.value = "INACTIVE";
    a.rtp_port = 0;
    a.transport = "";
    a.rtpmaps = {};
    a.rtpmaps_value = {};
    a.fmtp = "";
    a.fmtp_pt = "";

    -- 视频信息
    local v = {};
    v.value = "INACTIVE";
    v.rtp_port = 0;
    v.transport = "";
    v.rtpmaps = {};
    v.rtpmaps_value = {};

    local msg = {};
    msg.audio = a;
    msg.video = v;

    local cur_media = nil;
    if c then
        local obj = string_split(c, "\n");
        for i,v in ipairs(obj) do 
            local f = string.find(v, "m=audio");

            if f then
                cur_media = msg["audio"];
                cur_media.value = "SENDRECV";
                analysis_media(cur_media, string.sub(v, f+7, -1));
            else 
                f = string.find(v, "m=video");
                if f then
                    cur_media = msg["video"];
                    cur_media.value = "SENDRECV";
                    analysis_media(cur_media, string.sub(v, f+7, -1));
                end
            end

            if cur_media then
                -- 检查媒体
                if string.find(v, "a=sendrecv") then
                    cur_media.value = "SENDRECV";
                elseif string.find(v, "a=recvonly") then
                    cur_media.value = "SENDONLY";
                elseif string.find(v, "a=recvdonly") then
                    cur_media.value = "RECVONLY";
                elseif string.find(v, "a=inactive") then
                    cur_media.value = "INACTIVE";
                end
                -- 解析rtmp
                f = string.find(v, "a=rtpmap:");
                if f then
                    analysis_rtmp(cur_media, string.sub(v, f+9, -1));
                end
                -- 解析dtmf
                f = string.find(v, "a=fmtp:");
                if f then
                    analysis_fmtp(cur_media, string.sub(v, f+7, -1));
                end
            end
        end
        
    end
    return msg;
end

function number_format(num)
    local temp = num;
    local sub2 = string.sub(temp,1,2);
    local sub3 = string.sub(temp,1,3);
    local sub4 = string.sub(temp,1,4);
    local sub5 = string.sub(temp,1,5);
    local sub6 = string.sub(temp,1,6);

    if sub3 == "+86" then
        temp,_ = string.gsub(temp, "+86", "0");
    end
    if sub5 == "+0086" then
        temp,_ = string.gsub(temp, "+0086", "0");
    end
    if sub5 == "0086" then
        temp,_ = string.gsub(temp, "0086", "0");
    end
    if sub2 == "86" then
        temp,_ = string.gsub(temp, "86", "0");
    end
    if sub2 == "+0" then
        temp,_ = string.gsub(temp, "+0", "0");
    end
    if sub2 == "01" and sub3 ~= "010" then
        temp,_ = string.gsub(temp, "01", "1");
    end

    return temp;
end

--------------------------------------------------------------------------
function main()
    local server_id = freeswitch.getGlobalVariable("lcc_server_name");
    local caller_number = session:getVariable("caller_id_number");
    local callee_number = session:getVariable("destination_number");
    local uuid = session:getVariable("uuid");
    local company_code = "";
    local service_code = "";
    logN("电话呼入:["..uuid.."], 主叫:"..caller_number..", 被叫:"..callee_number);

    -- 判定路由检测通话
    if caller_number == "routeCall" then
        local route_path = session:getVariable("sip_h_X-LCC-ROUTE-PATH");
        if route_path then
            route_path = route_path..","..server_id;
        else
            route_path = server_id;
        end
        local route_from = session:getVariable("sip_h_X-LCC-ROUTE-FROM");
        if route_from then
            session:setVariable("sip_h_X-LCC-ROUTE-FROM", route_from);
        end

        session:setVariable("sip_h_X-LCC-ROUTE-PATH", route_path);

        session:execute("bridge", "sofia/gateway/"..callee_number.."/"..callee_number);
        return;
    end

    if false then
        if caller_number == "18060482680" then
            -- session:execute("lcc_playback", "+-1 /home/ivr/090014/cloudcc/1692421963590676480.wav");
            -- session:execute("play_and_get_digits", "4 5 3 5000 # /home/server/switch/freeswitch/sounds/als.wav /home/ivr/090008/cloudcc/1688135025688674310.wav lcc_digits_result \\d+ 10");
        end
        session:execute("export", "lcc_cdr_id=18060482680");
        session:execute("bridge", "sofia/gateway/test-ant-sbc01/003096_001612");
        return;
    end
    -- 临时处理号码变换
    callee_number = number_format(callee_number)
    caller_number = number_format(caller_number)
    display_number = session:getVariable("caller_id_name") or caller_number;
    
    local msg = {};
    msg.req_id = api:execute("create_uuid");
    msg.number = callee_number;
    msg.callin_type = 1;

    -- 判断呼入是否是注册分机
    local extension = session:getVariable("sip_h_X-LCC-EXTENSION");
    if extension then
        msg.number = caller_number;
        msg.callin_type = 0;
        logN("call in from reg extension, change query msg number.");
    end

    -- 查询呼入号码的企业信息
    local url = freeswitch.getGlobalVariable("lcc_base_server");
    if url == nil then
        logE("get lcc_base_server error!");
        return;
    end
    local cmd = url.." content-type application/json;charset=utf-8 connect-timeout 2 timeout 2 post '"..cjson.encode(msg).."'";
    local ret = api:execute("curl", cmd);

    if ret and string.find(ret, "{") then
        logN("query call in number info respons:"..ret);
        local res = cjson.decode(ret);
        if res then
            if res.result == true then
                company_code = res.company_code;
                service_code = res.service_code;
                if res.service_url and res.service_url ~= "" and type(res.service_url) ~= "userdata" then
                    session:setVariable("lcc_curl_server", res.service_url);
                end

                if res.media_ip == "local" then
                    local ip = freeswitch.getGlobalVariable("lcc_ip_v4");
                    if ip then
                        session:setVariable("rtp_adv_audio_ip", ip);
                        session:setVariable("rtp_adv_video_ip", ip);
                    end
                elseif res.media_ip == "nat" then
                    local ip = freeswitch.getGlobalVariable("lcc_ip_v4_nat");
                    if ip then
                        session:setVariable("rtp_adv_audio_ip", ip);
                        session:setVariable("rtp_adv_video_ip", ip);
                    end
                end
            else
                logE((res.msg or "invalid call in number").."["..callee_number.."], can not call in.");
                return;
            end
        else
            logE("["..callee_number.."] request number info decode error("..ret.."), can not call in.");
            return;
        end
    else
        logE("["..callee_number.."] request number info respone:"..(ret or "nil")..", can not call in.");
        return;
    end

    -- 呼叫放行
    local uuid = session:getVariable("uuid");
    -- session:setVariable("park_after_bridge", "true"); -- 这样会导致咨询c坐席，c坐席接听后挂机，触发a路也挂机。
    session:setVariable("hangup_after_bridge", "false");
    session:setVariable("limit_ignore_transfer", "true");

    session:setVariable("lcc_company_code", company_code);
    session:setVariable("lcc_service_code", service_code);
    session:setVariable("lcc_caller_number", caller_number);
    session:setVariable("lcc_callee_number", callee_number);
    session:setVariable("lcc_display_number", display_number);
    session:setVariable("lcc_cdr_id", api:execute("create_uuid"));

    -- 呼入sdp信息
    local switch_r_sdp = session:getVariable("switch_r_sdp");
    local media_info = analysis_sdp(switch_r_sdp);
    local audio = media_info.audio.value;
    local video = media_info.video.value;

    local log_info = cjson.encode(media_info);
    log_info = string.gsub(log_info, "\\r", "");
    log_info = string.gsub(log_info, "\\", "");
    logN("呼入媒体信息: "..log_info);

    session:setVariable("lcc_audio_media_flow", string.lower(audio));
    session:setVariable("lcc_video_media_flow", string.lower(video));
    session:setVariable("lcc_first_video_media_flow", string.lower(video));
    session:setVariable("execute_on_lcc_negotiate_sdp", 'lua lcc_negotiate_sdp.lua');

    -- 发送呼入事件
    if false then
        local event = freeswitch.Event("CUSTOM", "lcc_service:callin");
        event:addHeader("unique-id", uuid);
        event:addHeader("Call-Direction", "inbound");
        event:addHeader("variable_lcc_company_code", company_code);
        event:addHeader("variable_lcc_service_code", service_code);
        event:addHeader("variable_lcc_caller_number", caller_number);
        event:addHeader("variable_lcc_callee_number", callee_number);
        event:addHeader("variable_lcc_display_number", display_number);
        event:addHeader("audio", audio);
        event:addHeader("video", video);
        if extension then
            event:addHeader("callin_type", "0");
        else
            event:addHeader("callin_type", "1");
        end
        event:fire();
    -- 直接发送呼入事件
    else
        local data = {};
        data.audio = audio;
        data.video = video;
        data.caller_number = caller_number;
        data.callee_number = callee_number;
        data.display_number = display_number;
        if extension then
            data.callin_type = "0";
        else
            data.callin_type = "1";
        end
    
        local mq_msg = {};
        mq_msg.event = "call_in";
        mq_msg.server_id = freeswitch.getGlobalVariable("lcc_server_name");
        mq_msg.uuid = uuid;
        mq_msg.company_code = company_code;
        mq_msg.service_code = 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_in 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);
            -- 缓存通话
            api:execute("lcc_hiredis_raw", "default SET base:call_loc:"..mq_msg.uuid.." "..mq_msg.server_id.." ex "..(7*3600));

            -- 并发统计
            local media = "audio";
            if video and video == "SENDRECV" then
                media = "video";
            end
            api:execute("uuid_limit", mq_msg.uuid.." lcc_hiredis default "..media.."_"..mq_msg.company_code.."/call_"..mq_msg.server_id);
        end

    end
    session:execute("pre_answer");
    -- 等待呼入响应，最长等待8小时
    -- session:execute("lcc_wait_callin_action", 8*60*60*1000);
    -- session:execute("park_state"); -- 这样做会导致ivr无法播放文件
    session:setVariable("lcc_last_dialplan_hangup", "false");
end

main()
