StringUtil = {};

function StringUtil.utf8_tail(n, k)  
  local u, r=''  
  for i=1,k do  
    n,r = math.floor(n/0x40), n%0x40  
    u = string.char(r+0x80) .. u  
  end  
  return u, n  
end  
   
function StringUtil.to_utf8(a)  
  local n, r, u = tonumber(a)  
  if n<0x80 then                        -- 1 byte  
    return string.char(n)  
  elseif n<0x800 then                   -- 2 byte  
    u, n = StringUtil.utf8_tail(n, 1)  
    return string.char(n+0xc0) .. u  
  elseif n<0x10000 then                 -- 3 byte  
    u, n = StringUtil.utf8_tail(n, 2)  
    return string.char(n+0xe0) .. u  
  elseif n<0x200000 then                -- 4 byte  
    u, n = StringUtil.utf8_tail(n, 3)  
    return string.char(n+0xf0) .. u  
  elseif n<0x4000000 then               -- 5 byte  
    u, n = StringUtil.utf8_tail(n, 4)  
    return string.char(n+0xf8) .. u  
  else                                  -- 6 byte  
    u, n = StringUtil.utf8_tail(n, 5)  
    return string.char(n+0xfc) .. u
  end  
end  
   
function StringUtil.sto_utf8(s)
  return string.gsub(s, '&#(%d+);', StringUtil.to_utf8)  
end

--startIndex can't bigger endIndex
--endIndex can be nil 
local tab = {}
function StringUtil.getTextByIndex( str,startIndex,endIndex)
    if(endIndex and endIndex < startIndex)then
        printRed("error!startIndex can't bigger endIndex")
        return ""
    end

    TableUtility.ArrayClear(tab);
    for uchar in string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") do 
        tab[#tab+1] = uchar 
    end
    
    if(endIndex and endIndex > #tab or not endIndex)then
        endIndex = #tab
    end
    return table.concat(tab, "", startIndex, endIndex)
end

function StringUtil.getTextLen( str)
    -- body
    local lenInByte = #str
    local len = 0
    local i = 1
    local curByte
    local byteCount = 1
    while (i <= lenInByte) 
    do
        curByte = string.byte(str, i)
        if curByte > 0 and curByte <= 127 then
            byteCount = 1
        elseif curByte >= 192 and curByte < 223 then
            byteCount = 2
        elseif curByte >= 224 and curByte < 239 then
            byteCount = 3
        elseif curByte >= 240 and curByte <= 247 then
            byteCount = 4
        end
         
        i = i + byteCount
        len = len + 1                                          
    end
    return len
end

function StringUtil.StringToCharArray( str)
    -- body
    local result = 0
    local _, count = string.gsub(str, "[^\128-\193]", "")
    local tab = {}
    for uchar in string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") do 
        tab[#tab+1] = uchar 
    end
    return tab
end

function StringUtil.SubString( str,startIndex,length)
    -- local result = 0
    -- local _, count = string.gsub(str, "[^\128-\193]", "")
    -- local tab = {}
    -- for uchar in string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") do 
    --     tab[#tab+1] = uchar 
    -- end
    -- local max = math.min(#tab,startIndex + length-1)
    -- local str = ""
    -- for i = startIndex,max do
    --   str = str..tab[i]
    -- end
    local maxIndex = math.max(StringUtil.ChLength(str), startIndex + length-1);
    return StringUtil.Sub(str,startIndex,maxIndex)
end

function StringUtil.Sub(str, startIndex, endIndex)
  local dropping = string.byte(str, endIndex+1)    
  if not dropping then return str end    
  if dropping >= 128 and dropping < 192 then    
    return StringUtil.Sub(str, startIndex, endIndex-1)    
  end
  return string.sub(str, startIndex, endIndex);
end

-- 中文字串的長度
function StringUtil.ChLength(str)
  return #(string.gsub(str, '[\128-\255][\128-\255]',' '))
end

function StringUtil.AnalyzeDialogOptionConfig(str)
  local optionformat = "(%{([^%{%}]+)%,(%d+)%})";
  local result = {};
  for _,text,id in string.gmatch(str, optionformat) do
    local optionConfig = {};
    optionConfig.id = tonumber(id);
    optionConfig.text = text;
    table.insert(result, optionConfig);
  end
  if(#result==0)then
    local optionConfig = {};
    optionConfig.text = str;
    optionConfig.id = 0;
    table.insert(result, optionConfig);
  end
  return result;
end

function StringUtil.Split(str, delimiter)
  if str==nil or str=='' or delimiter==nil then
    return nil
  end
  
    local result = {}
    for match in (str..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, match)
    end
    return result
end

function StringUtil.Json2Lua( str )
  -- body
  local luaString = LuaUtils.JsonToLua(str)
  if nil ~= luaString then
    luaString = "return "..luaString
    local luaFunc = loadstring(luaString)
    if nil ~= luaFunc then
      local luaObject = luaFunc()
      return luaObject
    end
  else
    print("luaString is nil")
  end
end

function StringUtil.Chsize(char)
    if not char then
        print("not char")
        return 0
    elseif char > 240 then
        return 4
    elseif char > 225 then
        return 3
    elseif char > 192 then
        return 2
    else
        return 1
    end
end

-- 計算utf8字串字元數, 各種字元都按一個字元計算
-- 例如Utf8len("1你好") => 3
function StringUtil.Utf8len(str)
    local len = 0
    local currentIndex = 1
    while currentIndex <= #str do
        local char = string.byte(str, currentIndex)
        currentIndex = currentIndex + StringUtil.Chsize(char)
        len = len +1
    end
    return len
end

function StringUtil.Replace(str,searchStr,replaceStr)
    local replaceStr = string.gsub(replaceStr,"%%","%%%%")
    return string.gsub(str,searchStr,replaceStr)
end

function StringUtil.NumThousandFormat(num, deperator)
   if deperator == nil then  
        deperator = ","  
    end  
    deperator = deperator or ","

    local result = "";
    num = math.floor(num);
    local str = tostring( math.abs(num) );
    local slength = string.len(str);
    for i=1, slength do
        result = string.char(string.byte(str, slength + 1 - i))..result;
        if( i % 3 == 0 and i < slength)then
           result = deperator..result; 
        end
    end 
    return num < 0 and "-"..result or result  
end

local RomansMap = {
	{1000, "M"},
	{900, "CM"}, 
	{500, "D"},
	{400, "CD"},
	{100, "C"},
	{90, "XC"},
	{50, "L"},
	{40, "XL"},
	{10, "X"},
	{9, "IX"},
	{5, "V"},
	{4, "IV"},
	{1, "I"} 
}
function StringUtil.IntToRoman(num)
	local k = num;
	local roman, val, let = "";
	for _, v in ipairs(RomansMap) do 
		val, let = v[1], v[2];
		while k >= val do
			k = k - val;
			roman = roman .. let;
		end
	end
	return roman;
end

function StringUtil.FormatTime2TimeStamp( formatTime )
  local t = {};
  local ifs = string.split(formatTime , " ");
  local d1 = string.split(ifs[1], "-");
  t.year, t.month, t.day = tonumber(d1[1]), tonumber(d1[2]), tonumber(d1[3]);
  local d2 = string.split(ifs[2], ":");
  t.hour, t.min, t.sec = tonumber(d2[1]), tonumber(d2[2]), tonumber(d2[3]);
  return os.time(t);
end

function StringUtil.IsEmpty( content )
  -- body
  if(not content or content == "")then
    return true
  end
end

function StringUtil.LastIndexOf( content , findStr )
  local found = content:reverse():find(findStr:reverse(), nil, true)
  if found then
      return content:len() - findStr:len() - found + 2 
  else
      return found
  end
end