tableHeightOffset = -9

function onSave()
  return JSON.encode({ tid = tableImageData, cd = checkData })
end

function onload(saved_data)
  if saved_data ~= "" then
    local loaded_data = JSON.decode(saved_data)
    tableImageData = loaded_data.tid
    checkData = loaded_data.cd
  else
    tableImageData = {}
    checkData = { move = false, scale = false }
  end

  --Disables interactable status of objects with GUID in list
  for _, guid in ipairs(ref_noninteractable) do
    local obj = getObjectFromGUID(guid)
    if obj then obj.interactable = false end
  end

  --Establish references to table parts
  obj_leg1 = getObjectFromGUID("afc863")
  obj_leg2 = getObjectFromGUID("c8edca")
  obj_leg3 = getObjectFromGUID("393bf7")
  obj_leg4 = getObjectFromGUID("12c65e")
  obj_surface = getObjectFromGUID("4ee1f2")
  obj_side_top = getObjectFromGUID("35b95f")
  obj_side_bot = getObjectFromGUID("f938a2")
  obj_side_lef = getObjectFromGUID("9f95fd")
  obj_side_rig = getObjectFromGUID("5af8f2")

  controlActive = true
  createOpenCloseButton()
end

--Activation/deactivation of control panel

--Activated by clicking on
function click_toggleControl(_, color)
  if permissionCheck(color) then
    if not controlActive then
      --Activate control panel
      controlActive = true
      self.clearButtons()
      createOpenCloseButton()
      createSurfaceInput()
      createSurfaceButtons()
      createScaleInput()
      createScaleButtons()
    else
      --Deactivate control panel
      controlActive = false
      self.clearButtons()
      self.clearInputs()
      createOpenCloseButton()
    end
  end
end

--Table surface control

--Changes table surface
function click_applySurface(_, color)
  if permissionCheck(color) then
    updateSurface()
    broadcastToAll("New Table Image Applied", { 0.2, 0.9, 0.2 })
  end
end

--Saves table surface
function click_saveSurface(_, color)
  if permissionCheck(color) then
    local nickname = self.getInputs()[1].value
    local url = self.getInputs()[2].value
    if nickname == "" then
      --No nickname
      broadcastToAll("Please supply a nickname for this save.", { 0.9, 0.2, 0.2 })
    else
      --Nickname exists

      if findInImageDataIndex(url, nickname) == nil then
        --Save doesn't exist already
        table.insert(tableImageData, { url = url, name = nickname })
        broadcastToAll("Image URL saved to memory.", { 0.2, 0.9, 0.2 })
        --Refresh buttons
        self.clearButtons()
        createOpenCloseButton()
        createSurfaceButtons()
        createScaleButtons()
      else
        --Save exists already
        broadcastToAll("Memory already contains a save with this Name or URL. Delete it first.", { 0.9, 0.2, 0.2 })
      end
    end
  end
end

--Loads table surface
function click_loadMemory(_, color, index)
  if permissionCheck(color) then
    self.editInput({ index = 0, value = tableImageData[index].name })
    self.editInput({ index = 1, value = tableImageData[index].url })
    updateSurface()
    broadcastToAll("Table Image Loaded", { 0.2, 0.9, 0.2 })
  end
end

--Deletes table surface
function click_deleteMemory(_, color, index)
  if permissionCheck(color) then
    table.remove(tableImageData, index)
    self.clearButtons()
    createOpenCloseButton()
    createSurfaceButtons()
    createScaleButtons()
    broadcastToAll("Element Removed from Memory", { 0.2, 0.9, 0.2 })
  end
end

--Updates surface from the values in the input field
function updateSurface()
  local customInfo = obj_surface.getCustomObject()
  customInfo.diffuse = self.getInputs()[2].value
  obj_surface.setCustomObject(customInfo)
  obj_surface = obj_surface.reload()
end

--Table Scale control

--Applies Scale to table pieces
function click_applyScale(_, color)
  if permissionCheck(color) then
    local newWidth = tonumber(self.getInputs()[3].value)
    local newDepth = tonumber(self.getInputs()[4].value)
    if type(newWidth) ~= "number" then
      broadcastToAll("Invalid Width", { 0.9, 0.2, 0.2 })
      return
    elseif type(newDepth) ~= "number" then
      broadcastToAll("Invalid Depth", { 0.9, 0.2, 0.2 })
      return
    elseif newWidth < 0.1 or newDepth < 0.1 then
      broadcastToAll("Scale cannot go below 0.1", { 0.9, 0.2, 0.2 })
      return
    elseif newWidth > 12 or newDepth > 12 then
      broadcastToAll("Scale should not go over 12 (world size limitation)", { 0.9, 0.2, 0.2 })
      return
    else
      changeTableScale(math.abs(newWidth), math.abs(newDepth))
      broadcastToAll("Scale applied.", { 0.2, 0.9, 0.2 })
    end
  end
end

--Checks/unchecks move box for hands
function click_checkMove(_, color)
  if permissionCheck(color) then
    local find_func = function(o) return o.click_function == "click_checkMove" end
    if checkData.move == true then
      checkData.move = false
      local buttonEntry = findButton(self, find_func)
      self.editButton({ index = buttonEntry.index, label = "" })
    else
      checkData.move = true
      local buttonEntry = findButton(self, find_func)
      self.editButton({ index = buttonEntry.index, label = string.char(10008) })
    end
  end
end

--Checks/unchecks scale box for hands
--This button was disabled for technical reasons
--[[
function click_checkScale(_, color)
    if permissionCheck(color) then
        local find_func = function(o) return o.click_function=="click_checkScale" end
        if checkData.scale == true then
            checkData.scale = false
            local buttonEntry = findButton(self, find_func)
            self.editButton({index=buttonEntry.index, label=""})
        else
            checkData.scale = true
            local buttonEntry = findButton(self, find_func)
            self.editButton({index=buttonEntry.index, label=string.char(10008)})
        end
    end
end
]]

--Alters scale of elements and moves them
function changeTableScale(width, depth)
  --Scaling factors used to translate scale to position offset
  local width2pos = (width - 1) * 18
  local depth2pos = (depth - 1) * 18

  --Hand zone movement
  if checkData.move == true then
    for _, pc in ipairs(ref_playerColor) do
      if Player[pc].getHandCount() > 0 then
        moveHandZone(Player[pc], width2pos, depth2pos)
      end
    end
  end
  --Hand zone scaling
  --The button to enable this was disabled for technical reasons
  if checkData.scale == true then
    for _, pc in ipairs(ref_playerColor) do
      if Player[pc].getHandCount() > 0 then
        scaleHandZone(Player[pc], width, depth)
      end
    end
  end

  --Resizing table elements
  obj_side_top.setScale({ width, 1, 1 })
  obj_side_bot.setScale({ width, 1, 1 })
  obj_side_lef.setScale({ depth, 1, 1 })
  obj_side_rig.setScale({ depth, 1, 1 })
  obj_surface.setScale({ width, 1, depth })

  --Moving table elements to accomodate new scale
  obj_side_lef.setPosition({ -width2pos, tableHeightOffset, 0 })
  obj_side_rig.setPosition({ width2pos, tableHeightOffset, 0 })
  obj_side_top.setPosition({ 0, tableHeightOffset, depth2pos })
  obj_side_bot.setPosition({ 0, tableHeightOffset, -depth2pos })
  obj_leg1.setPosition({ -width2pos, tableHeightOffset, -depth2pos })
  obj_leg2.setPosition({ -width2pos, tableHeightOffset, depth2pos })
  obj_leg3.setPosition({ width2pos, tableHeightOffset, depth2pos })
  obj_leg4.setPosition({ width2pos, tableHeightOffset, -depth2pos })
  self.setPosition(obj_leg4.positionToWorld({ -22.12, 8.74, -19.16 }))
  --Only enabled when changing tableHeightOffset
  --obj_surface.setPosition({0,tableHeightOffset,0})
end

--Move hand zone, p=player reference, facts are scaling factors
function moveHandZone(p, width2pos, depth2pos)
  local widthX = obj_side_rig.getPosition().x
  local depthZ = obj_side_top.getPosition().z
  for i = 1, p.getHandCount() do
    local handT = p.getHandTransform()
    local pos = handT.position
    local y = handT.rotation.y

    if y < 45 or y > 320 or y > 135 and y < 225 then
      if pos.z > 0 then
        pos.z = pos.z + depth2pos - depthZ
      else
        pos.z = pos.z - depth2pos + depthZ
      end
    else
      if pos.x > 0 then
        pos.x = pos.x + width2pos - widthX
      else
        pos.x = pos.x - width2pos + widthX
      end
    end

    --Only enabled when changing tableHeightOffset
    --pos.y = tableHeightOffset + 14

    handT.position = pos
    p.setHandTransform(handT, i)
  end
end

---Scales hand zones, p=player reference, facts are scaling factors
function scaleHandZone(p, width, depth)
  local widthFact = width / obj_side_top.getScale().x
  local depthFact = depth / obj_side_lef.getScale().x
  for i = 1, p.getHandCount() do
    local handT = p.getHandTransform()
    local scale = handT.scale
    local y = handT.rotation.y
    if y < 45 or y > 320 or y > 135 and y < 225 then
      scale.x = scale.x * widthFact
    else
      scale.x = scale.x * depthFact
    end
    handT.scale = scale
    p.setHandTransform(handT, i)
  end
end

--Information gathering

--Checks if a color is promoted or host
function permissionCheck(color)
  if Player[color].host == true or Player[color].promoted == true then
    return true
  else
    return false
  end
end

--Locates a string saved within memory file
function findInImageDataIndex(...)
  for _, str in ipairs({ ... }) do
    for i, v in ipairs(tableImageData) do
      if v.url == str or v.name == str then
        return i
      end
    end
  end
  return nil
end

--Round number (num) to the Nth decimal (dec)
function round(num, dec)
  local mult = 10 ^ (dec or 0)
  return math.floor(num * mult + 0.5) / mult
end

--Locates a button with a helper function
function findButton(obj, func)
  if func == nil then error("No func supplied to findButton") end
  for _, v in ipairs(obj.getButtons()) do
    if func(v) then
      return v
    end
  end
  return nil
end

--Creation of buttons/inputs

function createOpenCloseButton()
  local tooltip = "Open Table Control Panel"
  if controlActive then
    tooltip = "Close Table Control Panel"
  end
  self.createButton({
    click_function = "click_toggleControl",
    function_owner = self,
    position = { 0, 0, 0 },
    rotation = { -45, 0, 0 },
    height = 400,
    width = 400,
    color = { 1, 1, 1, 0 },
    tooltip = tooltip
  })
end

function createSurfaceInput()
  local currentURL = obj_surface.getCustomObject().diffuse
  local nickname = ""
  if findInImageDataIndex(currentURL) ~= nil then
    nickname = tableImageData[findInImageDataIndex(currentURL)].name
  end
  self.createInput({
    label = "Nickname",
    input_function = "none",
    function_owner = self,
    alignment = 3,
    position = { 0, 0, 2 },
    height = 224,
    width = 4000,
    font_size = 200,
    tooltip = "Enter nickname for table image (only used for save)",
    value = nickname
  })
  self.createInput({
    label = "URL",
    input_function = "none",
    function_owner = self,
    alignment = 3,
    position = { 0, 0, 3 },
    height = 224,
    width = 4000,
    font_size = 200,
    tooltip = "Enter URL for tabletop image",
    value = currentURL
  })
end

function createSurfaceButtons()
  --Label
  self.createButton({
    label = "Tabletop Surface Image",
    click_function = "none",
    position = { 0, 0, 1 },
    height = 0,
    width = 0,
    font_size = 300,
    font_color = { 1, 1, 1 }
  })
  --Functional
  self.createButton({
    label = "Apply Image\nTo Table",
    click_function = "click_applySurface",
    function_owner = self,
    tooltip = "Apply URL as table image",
    position = { 2, 0, 4 },
    height = 440,
    width = 1400,
    font_size = 200,
  })
  self.createButton({
    label = "Save Image\nTo Memory",
    click_function = "click_saveSurface",
    function_owner = self,
    tooltip = "Record URL into memory (requires nickname)",
    position = { -2, 0, 4 },
    height = 440,
    width = 1400,
    font_size = 200,
  })
  --Label
  self.createButton({
    label = "Load From Memory",
    click_function = "none",
    position = { 0, 0, 5.5 },
    height = 0,
    width = 0,
    font_size = 300,
    font_color = { 1, 1, 1 }
  })
  --Saves, created dynamically from memory file
  for i, memoryEntry in ipairs(tableImageData) do
    --Load
    local funcName = i .. "loadMemory"
    local func = function(x, y) click_loadMemory(x, y, i) end
    self.setVar(funcName, func)
    self.createButton({
      label = memoryEntry.name,
      click_function = funcName,
      function_owner = self,
      tooltip = memoryEntry.url,
      font_size = 200,
      position = { -0.6, 0, 6.5 + 0.5 * (i - 1) },
      height = 240,
      width = 3300,
    })
    --Delete
    local funcName = i .. "deleteMemory"
    local func = function(x, y) click_deleteMemory(x, y, i) end
    self.setVar(funcName, func)
    self.createButton({
      label = "DELETE",
      click_function = funcName,
      function_owner = self,
      tooltip = "",
      position = { 3.6, 0, 6.5 + 0.5 * (i - 1) },
      height = 240,
      width = 600,
      font_size = 160,
      font_color = { 1, 0, 0 },
      color = { 0.8, 0.8, 0.8 }
    })
  end
end

function createScaleInput()
  self.createInput({
    label = string.char(8644),
    input_function = "none",
    function_owner = self,
    alignment = 3,
    position = { -8.5, 0, 2 },
    height = 224,
    width = 400,
    font_size = 200,
    tooltip = "Table Width",
    value = round(obj_side_top.getScale().x, 1)
  })
  self.createInput({
    label = string.char(8645),
    input_function = "none",
    function_owner = self,
    alignment = 3,
    position = { -7.5, 0, 2 },
    height = 224,
    width = 400,
    font_size = 200,
    tooltip = "Table Depth",
    value = round(obj_side_lef.getScale().x, 1)
  })
end

function createScaleButtons()
  --Labels
  self.createButton({
    label = "Table Scale",
    click_function = "none",
    position = { -8, 0, 1 },
    height = 0,
    width = 0,
    font_size = 300,
    font_color = { 1, 1, 1 }
  })
  self.createButton({
    label = string.char(8644) .. "            " .. string.char(8645),
    click_function = "none",
    position = { -8, 0, 2 },
    height = 0,
    width = 0,
    font_size = 300,
    font_color = { 1, 1, 1 }
  })
  self.createButton({
    label = "Move Hands:",
    click_function = "none",
    position = { -8.3, 0, 3 },
    height = 0,
    width = 0,
    font_size = 200,
    font_color = { 1, 1, 1 }
  })
  --Disabled due to me removing the feature for technical reasons
  --[[
    self.createButton({
        label="Scale Hands:", click_function="none",
        position={-8.3,0,4}, height=0, width=0, font_size=200, font_color={1,1,1}
    })
    ]]
  --Checkboxes
  local label = ""
  if checkData.move == true then label = string.char(10008) end
  self.createButton({
    label = label,
    click_function = "click_checkMove",
    function_owner = self,
    tooltip = "Check to move hands when table is rescaled",
    position = { -6.8, 0, 3 },
    height = 224,
    width = 224,
    font_size = 200,
  })
  --[[
    local label = ""
    if checkData.scale == true then label = string.char(10008) end
    self.createButton({
        label=label, click_function="click_checkScale",
        function_owner=self, tooltip="Check to scale the width of hands when table is rescaled",
        position={-6.8,0,4}, height=224, width=224, font_size=200,
    })
    ]]
  --Apply button
  self.createButton({
    label = "Apply Scale",
    click_function = "click_applyScale",
    function_owner = self,
    tooltip = "Apply width/depth to table",
    position = { -8, 0, 4 },
    height = 440,
    width = 1400,
    font_size = 200,
  })
end

--Data tables

ref_noninteractable = {
  "afc863", "c8edca", "393bf7", "12c65e", "f938a2", "9f95fd", "35b95f",
  "5af8f2", "4ee1f2", "bd69bd"
}

ref_playerColor = {
  "White", "Brown", "Red", "Orange", "Yellow",
  "Green", "Teal", "Blue", "Purple", "Pink", "Black"
}

--Dummy function, absorbs unwanted triggers
function none() end