--- 模块功能：音频播放.
-- 支持MP3、amr文件播放；
-- 支持本地TTS播放、通话中TTS播放到对端（需要使用支持TTS功能的core软件）
-- @module audio
-- @author openLuat
-- @license MIT
-- @copyright openLuat
-- @release 2018.3.19

require "common"
require "misc"
require "utils"
module(..., package.seeall)

local req = ril.request

--音频播放的协程ID
local taskID

--sPriority：当前播放的音频优先级
--sType：当前播放的音频类型
--sPath：当前播放的音频数据信息
--sVol：当前播放音量
--sCb：当前播放结束或者出错的回调函数
--sDup：当前播放的音频是否需要重复播放
--sDupInterval：如果sDup为true，此值表示重复播放的间隔(单位毫秒)，默认无间隔
--sStrategy：优先级相同时的播放策略，0(表示继续播放正在播放的音频，忽略请求播放的新音频)，1(表示停止正在播放的音频，播放请求播放的新音频)
local sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval,sStrategy

local function update(priority,type,path,vol,cb,dup,dupInterval)
    print("audio.update",sPriority,priority,type,path,vol,cb,dup,dupInterval)
    if sPriority then
        if priority>sPriority or (priority==sPriority and sStrategy==1) then
            print("audio.update1",priority,type,path,vol,cb,dup,dupInterval)
            --此处第三个参数传入table是因为publish接口无法处理nil后面的参数
            sys.publish("AUDIO_PLAY_END","NEW",{pri=priority,typ=type,pth=path,vl=vol,c=cb,dp=dup,dpIntval=dupInterval})
        else
            return false
        end
    else
        sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval = priority,type,path,vol,cb,dup,dupInterval
        if vol then setVolume(vol) end
    end
    return true
end

local function playEnd(result)
    log.info("audio.playEnd",result,sCb)
    local cb = sCb
    sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval = nil
    if cb then cb(result) end
end

local function isTtsApi()
    return tonumber((rtos.get_version()):match("Luat_V(%d+)_"))>=29
end

local function taskAudio()
    local playFnc =
    {
        FILE = audiocore.play,
        TTS = function(text)
                if isTtsApi() then
                    audiocore.openTTS()
                    local _,result = sys.waitUntil("TTS_OPEN_IND")
                    if not result then return false end
                    
                    audiocore.playTTS(common.utf8ToUcs2(text))
                else
                    req("AT+QTTS=1") req(string.format("AT+QTTS=%d,\"%s\"",2,string.toHex(common.utf8ToUcs2(text))))
                end
             end,
        TTSCC = function(text) req("AT+QTTS=1") req(string.format("AT+QTTS=%d,\"%s\"",4,string.toHex(common.utf8ToUcs2(text)))) end,
        RECORD = function(id) f,d=record.getSize() req("AT+AUDREC=1,0,2," .. id .. "," .. d*1000)end,   
    }
    
    local stopFnc =
    {
        FILE = audiocore.stop,
        TTS = function(text)
                if isTtsApi() then
                    audiocore.stopTTS()
                    sys.waitUntil("TTS_STOP_IND") 
                    audiocore.closeTTS()
                    sys.waitUntil("TTS_CLOSE_IND")
                else
                    req("AT+QTTS=3") sys.waitUntil("AUDIO_STOP_END")
                end
             end,
        TTSCC = function() req("AT+QTTS=3") sys.waitUntil("AUDIO_STOP_END") end,
        RECORD = function(id) f,d=record.getSize() req("AT+AUDREC=1,0,3," .. id .. "," .. d*1000) sys.waitUntil("AUDIO_STOP_END") end,        
    }

    while true do
        log.info("audio.taskAudio begin",sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval)
        --检查参数
        if not playFnc[sType] then
            playEnd(3)
            if sType==nil then break end
        end
        --开始播放
        if playFnc[sType](sPath)==false then
            playEnd(1)
            if sType==nil then break end
        end
        --挂起播放，等待播放成功、播放失败或者有新的播放请求激活协程
        local _,msg,param = sys.waitUntil("AUDIO_PLAY_END")

        log.info("audio.taskAudio resume msg",msg)        
        if msg=="SUCCESS" then
            if sDup then
                if sType=="TTS" and isTtsApi() then
                    stopFnc[sType](sPath)
                end
                if sDupInterval and sDupInterval>0 then
                    sys.waitUntil("AUDIO_PLAY_END",sDupInterval)
                    if sType==nil then break end
                end
            else
                stopFnc[sType](sPath)
                playEnd(0)
                if sType==nil then break end
            end
        elseif msg=="NEW" then
            stopFnc[sType](sPath)
            playEnd(4)
            --if sType==nil then break end
            update(param.pri,param.typ,param.pth,param.vl,param.c,param.dp,param.dpIntval)
        elseif msg=="STOP" then
            if param=="TTS" and isTtsApi() then
                stopFnc[param]()
            end
            playEnd(5)
            break
        else
            stopFnc[sType](sPath)
            playEnd(1)
            if sType==nil then break end
        end
    end
end

--[[
函数名：urc
功能  ：本功能模块内“注册的底层core通过虚拟串口主动上报的通知”的处理
参数  ：
		data：通知的完整字符串信息
		prefix：通知的前缀
返回值：无
]]
local function urc(data,prefix)	
    if prefix == "+QTTS" then
        local flag = string.match(data,": *(%d)",string.len(prefix)+1)
        --停止播放tts
        if flag=="0" --[[or flag == "1"]] then
            sys.publish("AUDIO_PLAY_END","SUCCESS")
        end	
    end
end

--[[
函数名：rsp
功能  ：本功能模块内“通过虚拟串口发送到底层core软件的AT命令”的应答处理
参数  ：
		cmd：此应答对应的AT命令
		success：AT命令执行结果，true或者false
		response：AT命令的应答中的执行结果字符串
		intermediate：AT命令的应答中的中间信息
返回值：无
]]
local function rsp(cmd,success,response,intermediate)
    local prefix = string.match(cmd,"AT(%+%u+%?*)")

    if prefix == "+QTTS" then	
        local action = string.match(cmd,"QTTS=(%d)")
        if not success then            
            if action=="1" or action=="2" then
                sys.publish("AUDIO_PLAY_END","ERROR")
            end
        end
        if action=="3" then
            sys.publish("AUDIO_STOP_END")
        end
    end
end

ril.regUrc("+QTTS",urc)
ril.regRsp("+QTTS",rsp,0)

local function audioMsg(msg)
    sys.publish("AUDIO_PLAY_END",msg.play_end_ind==true and "SUCCESS" or "ERROR")
end

local function ttsMsg(msg)
    log.info("audio.ttsMsg",msg.type,msg.result)
    local tag = {[0]="CLOSE", [1]="OPEN", [2]="PLAY", [3]="STOP"}
    if msg.type==2 then        
        sys.publish("AUDIO_PLAY_END",msg.result and "SUCCESS" or "ERROR")
    else
        if tag[msg.type] then sys.publish("TTS_"..tag[msg.type].."_IND",msg.result) end
    end
end
--注册core上报的rtos.MSG_AUDIO消息的处理函数
rtos.on(rtos.MSG_AUDIO,audioMsg)
if isTtsApi() then
    rtos.on(rtos.MSG_TTS,ttsMsg)
end

--- 播放音频
-- @number priority，音频优先级，数值越大，优先级越高
-- @string type，音频类型，目前仅支持"FILE"、"TTS"、"TTSCC","RECORD"
-- @string path，音频文件路径，跟typ有关
--               typ为"FILE"时：表示音频文件路径
--               typ为"TTS"时：表示要播放的UTF8编码格式的数据
--               typ为"TTSCC"时：表示要播放给通话对端的UTF8编码格式的数据
--               typ为"RECORD"时：表示要播放的录音id
-- @number[opt=4] vol，播放音量，取值范围0到7，0为静音
-- @function[opt=nil] cbFnc，音频播放结束时的回调函数，回调函数的调用形式如下：
-- cbFnc(result)
-- result表示播放结果：
--                   0-播放成功结束；
--                   1-播放出错
--                   2-播放优先级不够，没有播放
--                   3-传入的参数出错，没有播放
--                   4-被新的播放请求中止
--                   5-调用audio.stop接口主动停止
-- @bool[opt=nil] dup，是否循环播放，true循环，false或者nil不循环
-- @number[opt=0] dupInterval，循环播放间隔(单位毫秒)，dup为true时，此值才有意义
-- @return result，bool或者nil类型，同步调用成功返回true，否则返回false
-- @usage audio.play(0,"FILE","/ldata/call.mp3")
-- @usage audio.play(0,"FILE","/ldata/call.mp3",7)
-- @usage audio.play(0,"FILE","/ldata/call.mp3",7,cbFnc)
-- @usage 更多用法参考demo/audio/testAudio.lua
function play(priority,type,path,vol,cbFnc,dup,dupInterval)
    if not update(priority,type,path,vol or 4,cbFnc,dup,dupInterval or 0) then return false end
    if not sType or not taskID or coroutine.status(taskID)=="dead" then
        taskID = sys.taskInit(taskAudio)
    end
    return true
end

--- 停止音频播放
-- @return nil
-- @usage audio.stop()
function stop()
    if sType then
        if sType=="FILE" then
            audiocore.stop()
        elseif (sType=="TTS" and not isTtsApi()) or sType=="TTSCC" then
            req("AT+QTTS=3")
        elseif sType=="RECORD" then
            f,d=record.getSize() req("AT+AUDREC=1,0,3," .. sPath .. "," .. d*1000)
        end
        local typ = sType
        sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval = nil
        sys.publish("AUDIO_PLAY_END","STOP",typ)
    end
end

--- 设置喇叭音量等级
-- @number vol，音量值为0-7，0为静音
-- @return bool result，设置成功返回true，失败返回false
-- @usage audio.setVolume(7)
function setVolume(vol)
    return audiocore.setvol(vol)
end
--- 设置麦克音量等级
-- @number vol，音量值为0-15，0为静音
-- @return bool result，设置成功返回true,失败返回false
-- @usage audio.setMicVolume(14)
function setMicVolume(vol)
    return audiocore.setmicvol(vol)
end

--- 设置优先级相同时的播放策略
-- @number strategy，优先级相同时的播放策略；0：表示继续播放正在播放的音频，忽略请求播放的新音频；1：表示停止正在播放的音频，播放请求播放的新音频
-- @return nil
-- @usage audio.setStrategy(0)
-- @usage audio.setStrategy(1)
function setStrategy(strategy)
    sStrategy=strategy
end

--默认音频通道设置为LOUDSPEAKER，因为目前的模块只支持LOUDSPEAKER通道
audiocore.setchannel(audiocore.LOUDSPEAKER)
--默认音量等级设置为4级，4级是中间等级，最低为0级，最高为7级
setVolume(4)
--默认MIC音量等级设置为1级，最低为0级，最高为15级
setMicVolume(1)