---模块功能：通用库函数、编码格式转换、时区时间转换
-- @module common
-- @author openLuat
-- @license MIT
-- @copyright openLuat
-- @release 2017.02.20
--定义模块,导入依赖库
module(..., package.seeall)

--加载常用的全局函数至本地
local tinsert, ssub, sbyte, schar, sformat, slen = table.insert, string.sub, string.byte, string.char, string.format, string.len

--- ascii字符串的unicode编码的16进制字符串 转化为 ascii字符串
-- @string inNum，待转换字符串
-- @return string data，转换后的字符串
-- @usage 
-- local data = common.ucs2ToAscii("0031003200330034")
-- data is "1234"
function ucs2ToAscii(inNum)
    local tonum = {}
    for i = 1, slen(inNum), 4 do
        tinsert(tonum, tonumber(ssub(inNum, i, i + 3), 16) % 256)
    end
    return schar(unpack(tonum))
end
--- ascii字符串 转化为 ascii字符串的unicode编码的16进制字符串(仅支持数字和+)
-- @string inNum：待转换字符串
-- @return string data,转换后的字符串
-- @usage 
-- local data = common.nstrToUcs2Hex("+1234")
-- data is "002B0031003200330034"
function nstrToUcs2Hex(inNum)
    local hexs = ""
    local elem = ""
    for i = 1, slen(inNum) do
        elem = ssub(inNum, i, i)
        if elem == "+" then
            hexs = hexs .. "002B"
        else
            hexs = hexs .. "003" .. elem
        end
    end
    return hexs
end

--- ASCII字符串 转化为 BCD编码格式字符串(仅支持数字)
-- @string inStr，待转换字符串
-- @number destLen，转换后的字符串期望长度，如果实际不足，则填充F
-- @return string data,转换后的字符串
-- @usage 
-- local data = common.numToBcdNum("8618126324567")
-- data is "688121364265f7" （表示第1个字节是0x68，第2个字节为0x81，......）
function numToBcdNum(inStr,destLen)
    local l,t,num = string.len(inStr or ""),{}
    
    destLen = destLen or (inStr:len()+1)/2

    for i=1,l,2 do
        num = tonumber(inStr:sub(i,i+1),16)

        if i==l then
            num = 0xf0+num
        else
            num = (num%0x10)*0x10 + (num-(num%0x10))/0x10
        end

        table.insert(t,num)
    end

    local s = string.char(unpack(t))

    l = slen(s)
    if l < destLen then
        s = s .. string.rep("\255",destLen-l)
    elseif l > destLen then
        s = ssub(s,1,destLen)
    end

    return s
end

--- BCD编码格式字符串 转化为 号码ASCII字符串(仅支持数字)
-- @string,num：待转换字符串
-- @return string data,转换后的字符串
-- @usage 
-- local data = common.bcdNumToNum(common.fromHex("688121364265f7")) --表示第1个字节是0x68，第2个字节为0x81，......
-- data is "8618126324567"
function bcdNumToNum(num)
	local byte,v1,v2
	local t = {}

	for i=1,num:len() do
		byte = num:byte(i)
		v1,v2 = bit.band(byte,0x0f),bit.band(bit.rshift(byte,4),0x0f)

		if v1 == 0x0f then break end
		table.insert(t,v1)

		if v2 == 0x0f then break end
		table.insert(t,v2)
	end

	return table.concat(t)
end

--- unicode小端编码 转化为 gb2312编码
-- @string ucs2s,unicode小端编码数据
-- @return string data,gb2312编码数据
-- @usage local data = common.ucs2ToGb2312(ucs2s)
function ucs2ToGb2312(ucs2s)
    local cd = iconv.open("gb2312", "ucs2")
    return cd:iconv(ucs2s)
end

--- gb2312编码 转化为 unicode小端编码
-- @string gb2312s,gb2312编码数据
-- @return string data,unicode小端编码数据
-- @usage local data = common.gb2312ToUcs2(gb2312s)
function gb2312ToUcs2(gb2312s)
    local cd = iconv.open("ucs2", "gb2312")
    return cd:iconv(gb2312s)
end

--- unicode大端编码 转化为 gb2312编码
-- @string ucs2s,unicode大端编码数据
-- @return string data,gb2312编码数据
-- @usage data = common.ucs2beToGb2312(ucs2s)
function ucs2beToGb2312(ucs2s)
    local cd = iconv.open("gb2312", "ucs2be")
    return cd:iconv(ucs2s)
end

--- gb2312编码 转化为 unicode大端编码
-- @string gb2312s,gb2312编码数据
-- @return string data,unicode大端编码数据
-- @usage local data = common.gb2312ToUcs2be(gb2312s)
function gb2312ToUcs2be(gb2312s)
    local cd = iconv.open("ucs2be", "gb2312")
    return cd:iconv(gb2312s)
end

--- unicode小端编码 转化为 utf8编码
-- @string ucs2s,unicode小端编码数据
-- @return string data,utf8编码数据
-- @usage data = common.ucs2ToUtf8(ucs2s)
function ucs2ToUtf8(ucs2s)
    local cd = iconv.open("utf8", "ucs2")
    return cd:iconv(ucs2s)
end

--- utf8编码 转化为 unicode小端编码
-- @string utf8s,utf8编码数据
-- @return string data,unicode小端编码数据
-- @usage local data = common.utf8ToUcs2(utf8s)
function utf8ToUcs2(utf8s)
    local cd = iconv.open("ucs2", "utf8")
    return cd:iconv(utf8s)
end

--- unicode大端编码 转化为 utf8编码
-- @string ucs2s,unicode大端编码数据
-- @return string data,utf8编码数据
-- @usage data = common.ucs2beToUtf8(ucs2s)
function ucs2beToUtf8(ucs2s)
    local cd = iconv.open("utf8", "ucs2be")
    return cd:iconv(ucs2s)
end

--- utf8编码 转化为 unicode大端编码
-- @string utf8s,utf8编码数据
-- @return string data,unicode大端编码数据
-- @usage local data = common.utf8ToUcs2be(utf8s)
function utf8ToUcs2be(utf8s)
    local cd = iconv.open("ucs2be", "utf8")
    return cd:iconv(utf8s)
end

--- utf8编码 转化为 gb2312编码
-- @string utf8s,utf8编码数据
-- @return string data,gb2312编码数据
-- @usage local data = common.utf8ToGb2312(utf8s)
function utf8ToGb2312(utf8s)
    local cd = iconv.open("ucs2", "utf8")
    local ucs2s = cd:iconv(utf8s)
    cd = iconv.open("gb2312", "ucs2")
    return cd:iconv(ucs2s)
end

--- gb2312编码 转化为 utf8编码
-- @string gb2312s,gb2312编码数据
-- @return string data,utf8编码数据
-- @usage local data = common.gb2312ToUtf8(gb2312s)
function gb2312ToUtf8(gb2312s)
    local cd = iconv.open("ucs2", "gb2312")
    local ucs2s = cd:iconv(gb2312s)
    cd = iconv.open("utf8", "ucs2")
    return cd:iconv(ucs2s)
end

local function timeAddzone(y, m, d, hh, mm, ss, zone)
    if not y or not m or not d or not hh or not mm or not ss then
        return
    end
    hh = hh + zone
    if hh >= 24 then
        hh = hh - 24
        d = d + 1
        if m == 4 or m == 6 or m == 9 or m == 11 then
            if d > 30 then
                d = 1
                m = m + 1
            end
        elseif m == 1 or m == 3 or m == 5 or m == 7 or m == 8 or m == 10 then
            if d > 31 then
                d = 1
                m = m + 1
            end
        elseif m == 12 then
            if d > 31 then
                d = 1
                m = 1
                y = y + 1
            end
        elseif m == 2 then
            if (((y + 2000) % 400) == 0) or (((y + 2000) % 4 == 0) and ((y + 2000) % 100 ~= 0)) then
                if d > 29 then
                    d = 1
                    m = 3
                end
            else
                if d > 28 then
                    d = 1
                    m = 3
                end
            end
        end
    end
    local t = {}
    t.year, t.month, t.day, t.hour, t.min, t.sec = y, m, d, hh, mm, ss
    return t
end
local function timeSubZone(y, m, d, hh, mm, ss, zone)
    if not y or not m or not d or not hh or not mm or not ss then
        return
    end
    hh = hh + zone
    if hh < 0 then
        hh = hh + 24
        d = d - 1
        if m == 2 or m == 4 or m == 6 or m == 8 or m == 9 or m == 11 then
            if d < 1 then
                d = 31
                m = m - 1
            end
        elseif m == 5 or m == 7 or m == 10 or m == 12 then
            if d < 1 then
                d = 30
                m = m - 1
            end
        elseif m == 1 then
            if d < 1 then
                d = 31
                m = 12
                y = y - 1
            end
        elseif m == 3 then
            if (((y + 2000) % 400) == 0) or (((y + 2000) % 4 == 0) and ((y + 2000) % 100 ~= 0)) then
                if d < 1 then
                    d = 29
                    m = 2
                end
            else
                if d < 1 then
                    d = 28
                    m = 2
                end
            end
        end
    end
    local t = {}
    t.year, t.month, t.day, t.hour, t.min, t.sec = y, m, d, hh, mm, ss
    return t
end

--- 时区时间转换
-- @number y,源时区年份
-- @number m,源时区月份
-- @number d,源时区天
-- @number hh,源时区小时
-- @number mm,源时区分
-- @number ss,源时区秒
-- @number srcTimeZone,源时区
-- @number dstTimeZone,目的时区
-- @return table dstZoneTime,返回目的时区对应的时间，{year,month,day,hour,min,sec}
-- @usage
-- local dstZoneTime = common.timeZoneConvert(2018,1,1,18,00,00,0,8)
-- dstZoneTime为{year=2018,month=1,day=2,hour=2,min=0,sec=0}
function timeZoneConvert(y, m, d, hh, mm, ss, srcTimeZone, dstTimeZone)
    local t = {}
    local zone = dstTimeZone-srcTimeZone
    
    if zone >= 0 and zone < 23 then
        t = timeAddzone(y, m, d, hh, mm, ss, zone)
    elseif zone < 0 and zone >= -24 then
        t = timeSubZone(y, m, d, hh, mm, ss, zone)
    end
    return t
end
