-- This file handle all serializations of tables.
-- Supported types are: nil, boolean, number, string and wstring
-- Created by Tycere on 10.02.2012 to give powerauras an import/export feature

-- ChatInput/ChatInput/Field/Input
local copyEditLine = mainForm:CreateWidgetByDesc(
        stateMainForm:GetChildChecked("ChatInput", false)
        :GetChildChecked("ChatInput", false)
        :GetChildChecked("Field", false)
        :GetChildChecked("Input", false)
        :GetWidgetDesc())
copyEditLine:Show(false)

local sNumberTypeCharacter = "%"
local sStringTypeChracter = "$"
local tableOpenBracket = userMods.ToWString("{")
local tableCloseBracket = userMods.ToWString("}")
local escapeCharacter = userMods.ToWString("\\")
local tableEntrySeperator = userMods.ToWString(",")
local keyValueSeperator = userMods.ToWString("=")
local nilTypeCharacter = userMods.ToWString("&")
local booleanFalseChracter = userMods.ToWString("f")
local booleanTrueCharacter = userMods.ToWString("t")
local numberTypeCharacter = userMods.ToWString(sNumberTypeCharacter)
local stringTypeCharacter = userMods.ToWString(sStringTypeChracter)
local wstringTypeCharacter = userMods.ToWString("?")



function EscapeWString(value)
  if not value or type(value) ~= "userdata" or not common.IsWString(value) then
    return common.GetEmptyWString()
  end  

    copyEditLine:SetMaxSize(1000000)
    copyEditLine:SetText(value)
    local result = { }
    local length = copyEditLine:GetPosObjectCount()
    --LogInfo("Start: ", copyEditLine:GetText(), " (", length, ")")
    while length > 0 do
      copyEditLine:SetMaxSize(1)
      --LogInfo("MaxSize(1): ", copyEditLine:GetText(), " (", length, ")")
      local char = copyEditLine:GetText()
      if EqualsWString(char, tableCloseBracket) or EqualsWString(char, escapeCharacter) or 
         EqualsWString(char, tableEntrySeperator) or EqualsWString(char, keyValueSeperator) then
        table.insert(result, escapeCharacter)
      end
      table.insert(result, char)
      copyEditLine:SetMaxSize(length)
      length = length - 1	  
      copyEditLine:SetText(value)
      --LogInfo("Before Delete: ", copyEditLine:GetText(), " (", length, ")")
      copyEditLine:SetCursorPos(0)
      copyEditLine:DeleteCharAtCursorPos()
      value = copyEditLine:GetText()
      --LogInfo("After Delete: ", copyEditLine:GetText(), " (", length, ")")
    end
    return ConcatFromTable(result)
end

function GetSerializedValue(value)
  local valueType = type(value)
  if valueType == "nil" then
    return nilTypeCharacter
  elseif valueType == "boolean" then
    return value and booleanTrueCharacter or booleanFalseChracter
  elseif valueType == "number" then
    return userMods.ToWString(sNumberTypeCharacter .. tostring(value))
  elseif valueType == "string" then
    return EscapeWString(userMods.ToWString(sStringTypeChracter .. value))
  elseif valueType == "table" then
    return Serialize(value)
  elseif valueType == "userdata" and common.IsWString(value) then
    return EscapeWString(Concat(wstringTypeCharacter, value))
  end
end

-- Serialize a table to a wstring
function Serialize(t)
  if not t or type(t) ~= "table" then
    return common.GetEmptyWString()
  end  

  local result = { }
    -- here come all work:	  
    table.insert(result, tableOpenBracket)
    local first = true
    for k, v in t do
    if not first then
      table.insert(result, tableEntrySeperator)
    end
    first = false
    --LogInfo("[", k, "] = ", v)
    table.insert(result, GetSerializedValue(k))
    table.insert(result, keyValueSeperator)
    table.insert(result, GetSerializedValue(v))
    end
    table.insert(result, tableCloseBracket)
  return ConcatFromTable(result)
end

local SeperatorPredicate = function(c) return EqualsWString(c, tableEntrySeperator) or EqualsWString(c, keyValueSeperator) or EqualsWString(c, tableCloseBracket) end

function InternalDeserialize(stream) 
  local type = stream:ReadChar()
  --LogInfo("Type-Char: ", type)
  if EqualsWString(type, tableOpenBracket) then
    --LogInfo("Type: table")
    local result = { }
	--LogInfo("CanReadChar: ", stream:CanReadChar())
    while stream:CanReadChar() do
      local key = InternalDeserialize(stream)
      local value = InternalDeserialize(stream)
      if key ~= nil then
        result[key] = value
      end
      if not stream.LastRemovedCharIsEscaped and EqualsWString(stream.LastRemovedChar, tableCloseBracket) then
        stream:ReadChar()
        break
      end
    end
    return result
  elseif EqualsWString(type, nilTypeCharacter) then
    --LogInfo("Type: nil")
    stream:ReadChar()
    return nil
  elseif EqualsWString(type, booleanFalseChracter) then
    --LogInfo("Type: boolean")
    --LogInfo("Return: false")
    stream:ReadChar()
    return false
  elseif EqualsWString(type, booleanTrueCharacter) then
    --LogInfo("Type: boolean")
    --LogInfo("Return: true")
    stream:ReadChar()
    return true
  elseif EqualsWString(type, numberTypeCharacter) then
    --LogInfo("Type: number")
    local value = stream:ReadUntil(SeperatorPredicate)
    return tonumber(userMods.FromWString(value))
  elseif EqualsWString(type, stringTypeCharacter) then
    --LogInfo("Type: string")
    return userMods.FromWString(stream:ReadUntil(SeperatorPredicate, escapeCharacter))
  elseif EqualsWString(type, wstringTypeCharacter) then
    --LogInfo("Type: WString")
    return stream:ReadUntil(SeperatorPredicate, escapeCharacter)
  end
end

-- Deserialize a wstring to a table
function Deserialize(w)
  --LogInfo("not w: ", not w)
  --LogInfo("type(w): ", type(w))
  --LogInfo("common.IsWString(w): ", common.IsWString(w))
  --LogInfo("common.IsEmptyWString(w): ", common.IsWString(w) and common.IsEmptyWString(w))
  --LogInfo("w: ", w)
  if not w or type(w) ~= "userdata" or not common.IsWString(w) or common.IsEmptyWString(w) then
    return nil
  end

  --LogInfo("Deserialize(", w, ")")
  copyEditLine:SetMaxSize(1000000)
  copyEditLine:SetText(w)
  local stream = CreateWStringStream(copyEditLine)
  local table = InternalDeserialize(stream)
  if type(table) == "table" then
    return table
  end
  return nil
end


--------------------------------------------------------------------------------
--- Here begins the GUI-Part
--------------------------------------------------------------------------------
