text:
---@type ZxSimpleUI
local ZxSimpleUI = LibStub("AceAddon-3.0"):GetAddon("ZxSimpleUI")
---@type ZxSimpleUI
local FramePool47 = ZxSimpleUI.FramePool47

---@class CoreFactory47
local CoreFactory47 = {}
CoreFactory47.__index = CoreFactory47
ZxSimpleUI.CoreFactory47 = CoreFactory47

CoreFactory47._frameEnableToggleList = {}
CoreFactory47._prevDisabledOptionsList = {}

---@param curModule table
---Explicitly call OnEnable() and OnDisable() depending on the module's IsEnabled()
---This function is exactly like refreshConfig(), except it is called only during initialization.
function CoreFactory47:initModuleEnableState(curModule)
  if type(curModule.handleEnableToggle) == "function" then curModule:handleEnableToggle() end
  if curModule:IsEnabled() then
    curModule:OnEnable()
  else
    curModule:OnDisable()
  end
  curModule:refreshConfig()
end

---@param optionInstance table Instance of an option object, such as BarTemplateEnableOptions or
-- Aura47Options
---@param curModule table The current module, such as PlayerHealth47 or TargetDebuffs47
---@return table
function CoreFactory47:registerAndReturnOptionTable(optionInstance, curModule)
  optionInstance:registerModuleOptionsTable()
  local optionTable = optionInstance.options
  return optionTable
end

-- ####################################
-- # PRIVATE FUNCTIONS
-- ####################################

domain: lua


text:
--[[

This example is a highly parallel
implementation of the quicksort algorithm.

   Of course, the parallelism is not exploited
by the Lua VM but it is still an interesting
 exercise in "channel-oriented-programming".

--]]

require "pithreads"

LENGTH = tonumber(arg and arg[1]) or 50
MIN = tonumber(arg and arg[2]) or 1
MAX = tonumber(arg and arg[3]) or LENGTH

function Head(proc, nb_links, unlink)
   --print("<Head> started: " .. tostring(nb_links) .. " candidates (unlink=" .. tostring(unlink) .. ")")
   local nb_unlinked = 0
   local first_pivot = nil
   for i = 1, nb_links do
      local unlinked = proc:receive(unlink)
      nb_unlinked = nb_unlinked + 1
      --print("<Head> Unlink: nb unlinked = " ..tostring(nb_unlinked) .. " (unlinked=" .. tostring(unlinked) .. ")")
      if unlinked ~= nil then
         first_pivot = unlinked
      end
   end
   -- all the candidates have been unlinked
   -- time to print the result
   proc:send(first_pivot, 'print')
end

function Pivot(proc, head, prev_, self_, next_, link, val)
   --print("<Pivot> started: val=" .. tostring(val) .. " (link=" .. tostring(link) .. ")")
   while true do
      proc:choice(
         { link:receive(),
           function(link_val)
              --print("<Pivot" ..tostring(val) .. "> link from " ..tostring(link_val))
              if link_val <= val then
                 if prev_ == nil then -- new smaller pivot
                    prev_ = proc:new("self")
                    local prev_link = proc:new("link")
                    proc:send(head, prev_) -- tell the head there is a new smallest pivot
                    proc:yield()
                    proc:spawn("Pivot", Pivot, head, nil, prev_, self_, prev_link, link_val)
                    proc:yield()
                 else
                    proc:send(prev_, 'link_from_next', link_val) -- the previous pivot manage this link
                    proc:yield()
                 end
              elseif next_ == nil then -- new greater pivot
                 proc:signal(head)
                 next_ = proc:new("self")
                 local next_link = proc:new("link")
                 proc:spawn("Pivot", Pivot, head, self_, next_, nil, next_link, link_val)
                 proc:yield()
              else
                 proc:send(next_, 'link_from_prev', link_val) -- the next pivot manage this link
                 proc:yield()
              end
           end
         },
         { self_:receive(),
           function(cmd, value)
              --print("<Pivot" ..tostring(val) .. "> self cmd='" ..tostring(cmd) .. "' value=" ..tostring(value))
              if cmd == 'link_from_next' then
                 local link_val = value
                 if link_val >= val then
                    -- need to create a new pivot in between
                    proc:signal(head)
                    local old_next_ = next_
                    next_ = proc:new("self")
                    local next_link = proc:new("link")
                    proc:spawn("Pivot", Pivot, head, self_, next_, old_next_, next_link, link_val)
                    proc:yield()  -- WARNING: if yield() is removed then there is a 'chicken-and-egg' issue...
                    proc:send(old_next_, 'rebind_prev', next_)
                    proc:yield()
                 else
                    proc:spawn("Candidate", Candidate, link, link_val)
                 end
              elseif cmd == 'rebind_prev' then
                 _prev = value
              elseif cmd == 'link_from_prev' then
                 local link_val = value
                 if link_val <= val then
                    -- need to create a new pivot in between
                    proc:signal(head)
                    proc:yield()
                    local old_prev_ = prev_
                    prev_ = proc:new("self")
                    local prev_link = proc:new("link")
                    proc:spawn("Pivot", Pivot, head, old_prev_, prev_, self_, prev_link, link_val)
                    proc:yield()
                    proc:send(old_prev_, 'rebind_next', prev_)
                    proc:yield()
                 else
                    proc:spawn("Candidate", Candidate, link, link_val)
                    proc:yield()
                 end
              elseif cmd == 'rebind_next' then
                 _next = value
              elseif cmd == 'print' then
                 io.write(tostring(val))
                 if next_ ~= nil then
                    io.write(", ")
                    proc:send(next_, 'print')
                    proc:yield()
                 else
                    print()  -- add a newline at the end
                 end
              else
                 error("Unrecognized command: '" .. tostring(cmd) .. "'")
              end
           end
         }
      )() -- end of choice

   end -- while
end

function Candidate(proc, link, val)
   --print("<Candidate> started: val = " ..tostring(val) .. " (link=" .. tostring(link) .. ")")
   proc:yield()
   proc:send(link, val)   -- and that's it !
   proc:yield()
end

function PrintTab(tab)
  local str = ""
  for i,elem in ipairs(tab) do
    str = str .. tostring(elem)
    if i<#tab then
      str = str .. ", "
    end
  end
  str = str .. ""
  return str
end

function Main(proc,tab)
  print("Initial:\n" .. tostring(PrintTab(tab)))
  print("Result:")

  -- spawn the head process
  local unlink = proc:new("unlink")
  proc:spawn("Head", Head, #tab, unlink)
  proc:yield()

  -- spawn the first pivot and linked candidates
  local first_pivot = nil
  for idx, val in ipairs(tab) do
     if first_pivot == nil then
        local self_ = proc:new("self")
        first_pivot = proc:new("link")
        proc:spawn("Pivot", Pivot, unlink, nil, self_, nil, first_pivot, val)
        proc:send(unlink, self_)  -- the first pivot must be unlinked
     else
        proc:spawn("Candidate", Candidate, first_pivot, val)
     end
  end
end

local agent = pithreads.init()

--[[
-- random array
local tab = {}
for i=1,LENGTH do
  table.insert(tab,math.random(MIN,MAX))
end
--]]

-- debuggable example
local tab = { 3, 2, 5, 4, 1, 7 }

agent:spawn("Main",Main,tab)

agent:run()


text:
SKILL.name = "Flaming Sword of Rhuin"

SKILL.LevelReq = 5
SKILL.SkillPointCost = 1
SKILL.Incompatible = {

}
SKILL.RequiredSkills = {

}
SKILL.icon = "vgui/skills/inv_sword_01.png"
SKILL.category = "Lore of Fire"-- Common Passives, Warrior, Lore of Light, Lore of Life

SKILL.slot = "MELEE" -- ULT, RANGED, MELEE, AOE, PASSIVE
SKILL.class = {
    "bright_wizard"
}

SKILL.desc = [[
A fiery sword materializes in the caster's grasp. Being magical in nature, it also imparts greater prowess in battle.
The wizard can also ensorcel his allies' weapons, making them burn with a savagely hungry flame.
Ability Slot: 2
Class Restriction: Bright Wizard

Level Requirement: ]] .. SKILL.LevelReq .. [[

Skill Point Cost:]] .. SKILL.SkillPointCost .. [[

]]

SKILL.coolDown = 20

local function ability(SKILL, ply )
    local nospam = ply:GetNWBool( "nospamAOE" )
	if (nospam) then
		if timer.Exists(ply:SteamID().."nospamMelee") then return  end // nospamRanged, nospamUlt, nospamAOE, nospamMelee
		timer.Create(ply:SteamID().."nospamMelee", SKILL.coolDown, 1, function()
			ply:SetNWBool( "nospamMelee", false )
		end)
		return
	end

	local mana = ply:getLocalVar("mana", 0)
		if mana < 50 then
			return
		end
	ply:setLocalVar("mana", mana - 50)

	ply:Give("fire_sword")
	ply:SelectWeapon( "fire_sword" )

	if SERVER then

			ply:SetNWBool( "nospamMelee", true )

			net.Start( "MeleeActivated" ) // RangedActivated, UltActivated, AOEActivated, MeleeActivated
			net.Send( ply )

		if timer.Exists(ply:SteamID().."nospamMelee") then return  end
		timer.Create(ply:SteamID().."nospamMelee", SKILL.coolDown, 1, function()
			ply:SetNWBool( "nospamMelee", false )
		end)

	end
end

SKILL.ability = ability


text:
#!/usr/bin/env tarantool
test = require("sqltester")
test:plan(22)

--!./tcltestrunner.lua
-- 2005 September 19
--
-- The author disclaims copyright to this source code.  In place of
-- a legal notice, here is a blessing:
--
--    May you do good and not evil.
--    May you find forgiveness for yourself and forgive others.
--    May you share freely, never taking more than you give.
--
-------------------------------------------------------------------------
-- This file implements regression tests for SQLite library.
--
-- This file implements tests for left outer joins containing ON
-- clauses that restrict the scope of the left term of the join.
--
-- $Id: join5.test,v 1.2 2007/06/08 00:20:48 drh Exp $
-- ["set","testdir",[["file","dirname",["argv0"]]]]
-- ["source",[["testdir"],"\/tester.tcl"]]
test:do_execsql_test(
    "join5-1.1",
    [[
        CREATE TABLE t1(a integer primary key, b integer, c integer);
        CREATE TABLE t2(x integer primary key, y TEXT);
        CREATE TABLE t3(p integer primary key, q TEXT);
        START TRANSACTION;
        INSERT INTO t3 VALUES(11,'t3-11');
        INSERT INTO t3 VALUES(12,'t3-12');
        INSERT INTO t2 VALUES(11,'t2-11');
        INSERT INTO t2 VALUES(12,'t2-12');
        INSERT INTO t1 VALUES(1, 5, 0);
        INSERT INTO t1 VALUES(2, 11, 2);
        INSERT INTO t1 VALUES(3, 12, 1);
        COMMIT;
    ]], {
        -- <join5-1.1>

        -- </join5-1.1>
    })

test:do_execsql_test(
    "join5-1.2",
    [[
        select * from t1 left join t2 on t1.b=t2.x and t1.c=1
    ]], {
        -- <join5-1.2>
        1, 5, 0, "", "", 2, 11, 2, "", "", 3, 12, 1, 12, "t2-12"
        -- </join5-1.2>
    })

test:do_execsql_test(
    "join5-1.3",
    [[
        select * from t1 left join t2 on t1.b=t2.x where t1.c=1
    ]], {
        -- <join5-1.3>
        3, 12, 1, 12, "t2-12"
        -- </join5-1.3>
    })

test:do_execsql_test(
    "join5-1.4",
    [[
        select * from t1 left join t2 on t1.b=t2.x and t1.c=1
                         left join t3 on t1.b=t3.p and t1.c=2
    ]], {
        -- <join5-1.4>
        1, 5, 0, "", "", "", "", 2, 11, 2, "", "", 11, "t3-11", 3, 12, 1, 12, "t2-12", "", ""
        -- </join5-1.4>
    })

test:do_execsql_test(
    "join5-1.5",
    [[
        select * from t1 left join t2 on t1.b=t2.x and t1.c=1
                         left join t3 on t1.b=t3.p where t1.c=2
    ]], {
        -- <join5-1.5>
        2, 11, 2, "", "", 11, "t3-11"
        -- </join5-1.5>
    })

-- Ticket #2403
--
test:do_test(
    "join5-2.1",
    function()
        test:execsql [[
            CREATE TABLE ab(a  INT primary key,b INT );
            INSERT INTO ab VALUES(1,2);
            INSERT INTO ab VALUES(3,NULL);

            CREATE TABLE xy(x INT ,y  INT primary key);
            INSERT INTO xy VALUES(2,3);
            INSERT INTO xy VALUES(NULL,1);
        ]]
        return test:execsql "SELECT * FROM xy LEFT JOIN ab ON 0"
    end, {
        -- <join5-2.1>
        "", 1, "", "", 2, 3, "", ""
        -- </join5-2.1>
    })

test:do_execsql_test(
    "join5-2.2",
    [[
        SELECT * FROM xy LEFT JOIN ab ON 1
    ]], {
        -- <join5-2.2>
        "", 1, 1, 2, "", 1, 3, "", 2, 3, 1, 2, 2, 3, 3, ""
        -- </join5-2.2>
    })

test:do_execsql_test(
    "join5-2.3",
    [[
        SELECT * FROM xy LEFT JOIN ab ON NULL
    ]], {
        -- <join5-2.3>
        "", 1, "", "", 2, 3, "", ""
        -- </join5-2.3>
    })

test:do_execsql_test(
    "join5-2.4",
    [[
        SELECT * FROM xy LEFT JOIN ab ON 0 WHERE 0
    ]], {
        -- <join5-2.4>

        -- </join5-2.4>
    })

test:do_execsql_test(
    "join5-2.5",
    [[
        SELECT * FROM xy LEFT JOIN ab ON 1 WHERE 0
    ]], {
        -- <join5-2.5>

        -- </join5-2.5>
    })

test:do_execsql_test(
    "join5-2.6",
    [[
        SELECT * FROM xy LEFT JOIN ab ON NULL WHERE 0
    ]], {
        -- <join5-2.6>

        -- </join5-2.6>
    })

test:do_execsql_test(
    "join5-2.7",
    [[
        SELECT * FROM xy LEFT JOIN ab ON 0 WHERE 1
    ]], {
        -- <join5-2.7>
        "", 1, "", "", 2, 3, "", ""
        -- </join5-2.7>
    })

test:do_execsql_test(
    "join5-2.8",
    [[
        SELECT * FROM xy LEFT JOIN ab ON 1 WHERE 1
    ]], {
        -- <join5-2.8>
        "",1 ,1, 2, "", 1, 3, "", 2, 3, 1, 2, 2, 3, 3, ""
        -- </join5-2.8>
    })

test:do_execsql_test(
    "join5-2.9",
    [[
        SELECT * FROM xy LEFT JOIN ab ON NULL WHERE 1
    ]], {
        -- <join5-2.9>
        "", 1, "", "", 2, 3, "", ""
        -- </join5-2.9>
    })

test:do_execsql_test(
    "join5-2.10",
    [[
        SELECT * FROM xy LEFT JOIN ab ON 0 WHERE NULL
    ]], {
        -- <join5-2.10>

        -- </join5-2.10>
    })

test:do_execsql_test(
    "join5-2.11",
    [[
        SELECT * FROM xy LEFT JOIN ab ON 1 WHERE NULL
    ]], {
        -- <join5-2.11>

        -- </join5-2.11>
    })

test:do_execsql_test(
    "join5-2.12",
    [[
        SELECT * FROM xy LEFT JOIN ab ON NULL WHERE NULL
    ]], {
        -- <join5-2.12>

        -- </join5-2.12>
    })

-- Ticket https://www.sqlite.org/src/tktview/6f2222d550f5b0ee7ed37601
-- Incorrect output on a LEFT JOIN.
--
test:do_execsql_test(
    "join5-3.1",
    [[
        DROP TABLE IF EXISTS t1;
        DROP TABLE IF EXISTS t2;
        DROP TABLE IF EXISTS t3;
        CREATE TABLE x1(a  INT primary key);
        INSERT INTO x1 VALUES(1);
        CREATE TABLE x2(b TEXT NOT NULL primary key);
        CREATE TABLE x3(c TEXT primary key, d TEXT);
        INSERT INTO x3 VALUES('a', NULL);
        INSERT INTO x3 VALUES('b', NULL);
        INSERT INTO x3 VALUES('c', NULL);
        SELECT * FROM x1 LEFT JOIN x2 LEFT JOIN x3 ON x3.d = x2.b;
    ]], {
        -- <join5-3.1>
        1, "", "", ""
        -- </join5-3.1>
    })

test:do_execsql_test(
    "join5-3.2",
    [[
        DROP TABLE IF EXISTS t1;
        DROP TABLE IF EXISTS t2;
        DROP TABLE IF EXISTS t3;
        DROP TABLE IF EXISTS t4;
        DROP TABLE IF EXISTS t5;
        CREATE TABLE t1(x text NOT NULL primary key, y text);
        CREATE TABLE t2(u text NOT NULL primary key, x text NOT NULL);
        CREATE TABLE t3(w text NOT NULL primary key, v text);
        CREATE TABLE t4(w text NOT NULL primary key, z text NOT NULL);
        CREATE TABLE t5(z text NOT NULL primary key, m text);
        INSERT INTO t1 VALUES('f6d7661f-4efe-4c90-87b5-858e61cd178b',NULL);
        INSERT INTO t1 VALUES('f6ea82c3-2cad-45ce-ae8f-3ddca4fb2f48',NULL);
        INSERT INTO t1 VALUES('f6f47499-ecb4-474b-9a02-35be73c235e5',NULL);
        INSERT INTO t1 VALUES('56f47499-ecb4-474b-9a02-35be73c235e5',NULL);
        INSERT INTO t3 VALUES('007f2033-cb20-494c-b135-a1e4eb66130c',
                              'f6d7661f-4efe-4c90-87b5-858e61cd178b');
        SELECT *
          FROM t3
               INNER JOIN t1 ON t1.x= t3.v AND t1.y IS NULL
               LEFT JOIN t4  ON t4.w = t3.w
               LEFT JOIN t5  ON t5.z = t4.z
               LEFT JOIN t2  ON t2.u = t5.m
               LEFT JOIN t1 xyz ON xyz.y = t2.x;
    ]], {
        -- <join5-3.2>
        "007f2033-cb20-494c-b135-a1e4eb66130c", "f6d7661f-4efe-4c90-87b5-858e61cd178b", "f6d7661f-4efe-4c90-87b5-858e61cd178b", "", "", "", "", "", "", "", "", ""
        -- </join5-3.2>
    })

test:do_execsql_test(
    "join5-3.3",
    [[
        DROP TABLE IF EXISTS x1;
        DROP TABLE IF EXISTS x2;
        DROP TABLE IF EXISTS x3;
        CREATE TABLE x1(a  INT primary key);
        INSERT INTO x1 VALUES(1);
        CREATE TABLE x2(b TEXT NOT NULL primary key);
        CREATE TABLE x3(c TEXT primary key, d INT );
        INSERT INTO x3 VALUES('a', NULL);
        INSERT INTO x3 VALUES('b', NULL);
        INSERT INTO x3 VALUES('c', NULL);
        SELECT * FROM x1 LEFT JOIN x2 JOIN x3 WHERE x3.d = x2.b;
    ]], {
        -- <join5-3.3>

        -- </join5-3.3>
    })

-- Ticket https://www.sqlite.org/src/tktview/c2a19d81652f40568c770c43 on
-- 2015-08-20.  LEFT JOIN and the push-down optimization.
--
test:do_execsql_test(
    "join6-4.1",
    [[
        SELECT *
        FROM (
            SELECT 'apple' fruit
            UNION ALL SELECT 'banana'
        ) a
        JOIN (
            SELECT 'apple' fruit
            UNION ALL SELECT 'banana'
        ) b ON a.fruit=b.fruit
        LEFT JOIN (
            SELECT 1 isyellow
        ) c ON b.fruit='banana';
    ]], {
        -- <join6-4.1>
        "apple", "apple", "", "banana", "banana", 1
        -- </join6-4.1>
    })

test:do_execsql_test(
    "join6-4.2",
    [[
        SELECT *
          FROM (SELECT 'apple' fruit UNION ALL SELECT 'banana')
               LEFT JOIN (SELECT 1) ON fruit='banana';
    ]], {
        -- <join6-4.2>
        "apple", "", "banana", 1
        -- </join6-4.2>
    })

test:finish_test()


text:
POT_GROW_TIME = 60 * 15;
MAX_POT = 5;

local MIXTURE = {}

MIXTURE.ID = 2;

MIXTURE.Results = "drug_pot";
MIXTURE.Ingredients = {"drug_pot_seeds", "item_pot"};
MIXTURE.Requires = 	{
						{GENE_INTELLIGENCE, 1},
						{GENE_DEXTERITY, 1},
					};

MIXTURE.Free = true;

MIXTURE.RequiresHeatSource = false;
MIXTURE.RequiresWaterSource = false;
MIXTURE.RequiresSawHorse = false;

function MIXTURE.CanMix ( player, pos )
	if (player:Team() != TEAM_CITIZEN) then
		player:Notify("You cannot do this as a government official.");
		return false;
	end

	local NumDrugs = 0;

		//needs fix
		for k, v in pairs(ents.FindByClass('ent_pot')) do
			if v:GetTable().ItemSpawner == player then
				NumDrugs = NumDrugs + 1;
			end
		end

	local VipWeed = MAX_POT;
	if(player:GetLevel() == 99) then
		VipWeed = 6;
	elseif(player:GetLevel() == 98) then
		VipWeed = 8
	elseif(player:GetLevel() <= 97) then
		VipWeed = 10
	end

	if (NumDrugs >= VipWeed) then
		if (player:GetLevel() == 100) then
			player:Notify("Bronze VIP pot limit reached");
		elseif(player:GetLevel() == 99) then
			player:Notify("Silver VIP pot limit reached");
		elseif(player:GetLevel() == 98) then
			player:Notify("Gold VIP pot limit reached");
		elseif(player:GetLevel() == 97) then
			player:Notify("Diamond VIP pot limit reached");
		elseif(player:GetLevel() < 97) then
			player:Notify("Admin pot limit reached");
		else
			player:Notify("Guest pot limit reached");
		end
		return false;
	end

	return true;
end

GM:RegisterMixture(MIXTURE);


text:
---
-- Source a complex-valued signal from a USRP. This source requires the libuhd
-- library.
--
-- @category Sources
-- @block UHDSource
-- @tparam string device_address Device address string
-- @tparam number frequency Tuning frequency in Hz
-- @tparam number rate Sample rate in Hz
-- @tparam[opt={}] table options Additional options, specifying:
--      * `channel` (int, default 0)
--      * `gain` (number in dB, manual overall gain, default nil)
--      * `bandwidth` (number in Hz)
--      * `antenna` (string)
--      * `autogain` (bool, default true if manual gain is nil)
--      * `gains` (table, gain element name to value in dB)
--
-- @signature > out:ComplexFloat32
--
-- @usage
-- -- Source samples from a B200 at 91.1 MHz sampled at 2 MHz
-- local src = radio.UHDSource("type=b200", 91.1e6, 2e6)
--
-- -- Source samples from a B200 at 915 MHz sampled at 10 MHz, with 20 dB
-- -- overall gain
-- local src = radio.UHDSource("type=b200", 915e6, 10e6, {gain = 20})
--
-- -- Source samples from a B200 at 144.390 MHz sampled at 8 MHz, with 2.5 MHz
-- -- baseband bandwidth
-- local src = radio.UHDSource("type=b200", 144.390e6, 8e6, {bandwidth = 2.5e6})

local ffi = require('ffi')

local block = require('radio.core.block')
local debug = require('radio.core.debug')
local types = require('radio.types')

local UHDSource = block.factory("UHDSource")

function UHDSource:instantiate(device_address, frequency, rate, options)
    self.device_address = assert(device_address, "Missing argument #1 (device_address)")
    self.frequency = assert(frequency, "Missing argument #1 (frequency)")
    self.rate = assert(rate, "Missing argument #2 (rate)")

    assert(type(device_address) == "string", "Invalid argument #1 (device_address), should be string.")

    self.options = options or {}
    self.channel = self.options.channel or 0
    self.gain = self.options.gain or nil
    self.bandwidth = self.options.bandwidth
    self.antenna = self.options.antenna
    self.gains = self.options.gains
    self.autogain = (self.gain == nil) and true or self.options.autogain

    self:add_type_signature({}, {block.Output("out", types.ComplexFloat32)})
end

function UHDSource:get_rate()
    return self.rate
end

if not package.loaded['radio.blocks.sinks.uhd'] then
    ffi.cdef[[
        /* Opaque handles */
        typedef struct uhd_usrp* uhd_usrp_handle;
        typedef struct uhd_rx_streamer* uhd_rx_streamer_handle;
        typedef struct uhd_tx_streamer* uhd_tx_streamer_handle;
        typedef struct uhd_rx_metadata_t* uhd_rx_metadata_handle;
        typedef struct uhd_tx_metadata_t* uhd_tx_metadata_handle;
        typedef struct uhd_meta_range_t* uhd_meta_range_handle;
        typedef struct uhd_string_vector_t* uhd_string_vector_handle;

        /* Structures and enums */
        typedef enum {
            UHD_TUNE_REQUEST_POLICY_NONE   = 78,
            UHD_TUNE_REQUEST_POLICY_AUTO   = 65,
            UHD_TUNE_REQUEST_POLICY_MANUAL = 77
        } uhd_tune_request_policy_t;

        typedef struct {
            double target_freq;
            uhd_tune_request_policy_t rf_freq_policy;
            double rf_freq;
            uhd_tune_request_policy_t dsp_freq_policy;
            double dsp_freq;
            char* args;
        } uhd_tune_request_t;

        typedef struct {
            double clipped_rf_freq;
            double target_rf_freq;
            double actual_rf_freq;
            double target_dsp_freq;
            double actual_dsp_freq;
        } uhd_tune_result_t;

        typedef struct {
            char* cpu_format;
            char* otw_format;
            char* args;
            size_t* channel_list;
            int n_channels;
        } uhd_stream_args_t;

        typedef enum {
            UHD_STREAM_MODE_START_CONTINUOUS   = 97,
            UHD_STREAM_MODE_STOP_CONTINUOUS    = 111,
            UHD_STREAM_MODE_NUM_SAMPS_AND_DONE = 100,
            UHD_STREAM_MODE_NUM_SAMPS_AND_MORE = 109
        } uhd_stream_mode_t;

        typedef struct {
            uhd_stream_mode_t stream_mode;
            size_t num_samps;
            bool stream_now;
            int64_t time_spec_full_secs;
            double time_spec_frac_secs;
        } uhd_stream_cmd_t;

        typedef enum {
            UHD_RX_METADATA_ERROR_CODE_NONE         = 0x0,
            UHD_RX_METADATA_ERROR_CODE_TIMEOUT      = 0x1,
            UHD_RX_METADATA_ERROR_CODE_LATE_COMMAND = 0x2,
            UHD_RX_METADATA_ERROR_CODE_BROKEN_CHAIN = 0x4,
            UHD_RX_METADATA_ERROR_CODE_OVERFLOW     = 0x8,
            UHD_RX_METADATA_ERROR_CODE_ALIGNMENT    = 0xC,
            UHD_RX_METADATA_ERROR_CODE_BAD_PACKET   = 0xF
        } uhd_rx_metadata_error_code_t;

        typedef enum {
            UHD_ERROR_NONE = 0,
            UHD_ERROR_INVALID_DEVICE = 1,
            UHD_ERROR_INDEX = 10,
            UHD_ERROR_KEY = 11,
            UHD_ERROR_NOT_IMPLEMENTED = 20,
            UHD_ERROR_USB = 21,
            UHD_ERROR_IO = 30,
            UHD_ERROR_OS = 31,
            UHD_ERROR_ASSERTION = 40,
            UHD_ERROR_LOOKUP = 41,
            UHD_ERROR_TYPE = 42,
            UHD_ERROR_VALUE = 43,
            UHD_ERROR_RUNTIME = 44,
            UHD_ERROR_ENVIRONMENT = 45,
            UHD_ERROR_SYSTEM = 46,
            UHD_ERROR_EXCEPT = 47,
            UHD_ERROR_BOOSTEXCEPT = 60,
            UHD_ERROR_STDEXCEPT = 70,
            UHD_ERROR_UNKNOWN = 100
        } uhd_error;

        /* Functions */
        uhd_error uhd_get_abi_string(char* abi_string_out, size_t buffer_len);
        uhd_error uhd_get_version_string(char* version_out, size_t buffer_len);

        uhd_error uhd_usrp_make(uhd_usrp_handle *h, const char *args);
        uhd_error uhd_usrp_free(uhd_usrp_handle *h);

        uhd_error uhd_rx_streamer_make(uhd_rx_streamer_handle *h);
        uhd_error uhd_rx_streamer_free(uhd_rx_streamer_handle *h);
        uhd_error uhd_tx_streamer_make(uhd_tx_streamer_handle *h);
        uhd_error uhd_tx_streamer_free(uhd_tx_streamer_handle *h);

        uhd_error uhd_rx_metadata_make(uhd_rx_metadata_handle* handle);
        uhd_error uhd_rx_metadata_free(uhd_rx_metadata_handle* handle);
        uhd_error uhd_tx_metadata_make(uhd_tx_metadata_handle* handle, bool has_time_spec, int64_t full_secs, double frac_secs, bool start_of_burst, bool end_of_burst);
        uhd_error uhd_tx_metadata_free(uhd_tx_metadata_handle* handle);

        uhd_error uhd_meta_range_make(uhd_meta_range_handle* h);
        uhd_error uhd_meta_range_free(uhd_meta_range_handle* h);

        uhd_error uhd_string_vector_make(uhd_string_vector_handle *h);
        uhd_error uhd_string_vector_free(uhd_string_vector_handle *h);

        uhd_error uhd_usrp_get_rx_num_channels(uhd_usrp_handle h, size_t *num_channels_out);
        uhd_error uhd_usrp_get_tx_num_channels(uhd_usrp_handle h, size_t *num_channels_out);
        uhd_error uhd_usrp_set_rx_antenna(uhd_usrp_handle h, const char* ant, size_t chan);
        uhd_error uhd_usrp_set_tx_antenna(uhd_usrp_handle h, const char* ant, size_t chan);
        uhd_error uhd_usrp_get_rx_antennas(uhd_usrp_handle h, size_t chan, uhd_string_vector_handle *antennas_out);
        uhd_error uhd_usrp_get_tx_antennas(uhd_usrp_handle h, size_t chan, uhd_string_vector_handle *antennas_out);
        uhd_error uhd_usrp_get_rx_rates(uhd_usrp_handle h, size_t chan, uhd_meta_range_handle rates_out);
        uhd_error uhd_usrp_get_tx_rates(uhd_usrp_handle h, size_t chan, uhd_meta_range_handle rates_out);
        uhd_error uhd_usrp_set_rx_rate(uhd_usrp_handle h, double rate, size_t chan);
        uhd_error uhd_usrp_set_tx_rate(uhd_usrp_handle h, double rate, size_t chan);
        uhd_error uhd_usrp_get_rx_rate(uhd_usrp_handle h, size_t chan, double *rate_out);
        uhd_error uhd_usrp_get_tx_rate(uhd_usrp_handle h, size_t chan, double *rate_out);
        uhd_error uhd_usrp_set_rx_bandwidth(uhd_usrp_handle h, double bandwidth, size_t chan);
        uhd_error uhd_usrp_set_tx_bandwidth(uhd_usrp_handle h, double bandwidth, size_t chan);
        uhd_error uhd_usrp_get_rx_bandwidth(uhd_usrp_handle h, size_t chan, double *bandwidth_out);
        uhd_error uhd_usrp_get_tx_bandwidth(uhd_usrp_handle h, size_t chan, double *bandwidth_out);
        uhd_error uhd_usrp_get_rx_bandwidth_range(uhd_usrp_handle h, size_t chan, uhd_meta_range_handle bandwidth_range_out);
        uhd_error uhd_usrp_get_tx_bandwidth_range(uhd_usrp_handle h, size_t chan, uhd_meta_range_handle bandwidth_range_out);
        uhd_error uhd_usrp_set_rx_agc(uhd_usrp_handle h, bool enable, size_t chan);
        uhd_error uhd_usrp_set_rx_gain(uhd_usrp_handle h, double gain, size_t chan, const char *gain_name);
        uhd_error uhd_usrp_set_tx_gain(uhd_usrp_handle h, double gain, size_t chan, const char *gain_name);
        uhd_error uhd_usrp_get_rx_gain(uhd_usrp_handle h, size_t chan, const char *gain_name, double *gain_out);
        uhd_error uhd_usrp_get_tx_gain(uhd_usrp_handle h, size_t chan, const char *gain_name, double *gain_out);
        uhd_error uhd_usrp_get_rx_gain_range(uhd_usrp_handle h, const char* name, size_t chan, uhd_meta_range_handle gain_range_out);
        uhd_error uhd_usrp_get_tx_gain_range(uhd_usrp_handle h, const char* name, size_t chan, uhd_meta_range_handle gain_range_out);
        uhd_error uhd_usrp_get_rx_gain_names(uhd_usrp_handle h, size_t chan, uhd_string_vector_handle *gain_names_out);
        uhd_error uhd_usrp_get_tx_gain_names(uhd_usrp_handle h, size_t chan, uhd_string_vector_handle *gain_names_out);
        uhd_error uhd_usrp_get_rx_freq_range(uhd_usrp_handle h, size_t chan, uhd_meta_range_handle freq_range_out);
        uhd_error uhd_usrp_get_tx_freq_range(uhd_usrp_handle h, size_t chan, uhd_meta_range_handle freq_range_out);
        uhd_error uhd_usrp_set_rx_freq(uhd_usrp_handle h, uhd_tune_request_t *tune_request, size_t chan, uhd_tune_result_t *tune_result);
        uhd_error uhd_usrp_set_tx_freq(uhd_usrp_handle h, uhd_tune_request_t *tune_request, size_t chan, uhd_tune_result_t *tune_result);
        uhd_error uhd_usrp_get_rx_freq(uhd_usrp_handle h, size_t chan, double *freq_out);
        uhd_error uhd_usrp_get_tx_freq(uhd_usrp_handle h, size_t chan, double *freq_out);

        uhd_error uhd_usrp_get_rx_stream(uhd_usrp_handle h, uhd_stream_args_t *stream_args, uhd_rx_streamer_handle h_out);
        uhd_error uhd_usrp_get_tx_stream(uhd_usrp_handle h, uhd_stream_args_t *stream_args, uhd_tx_streamer_handle h_out);
        uhd_error uhd_rx_streamer_max_num_samps(uhd_rx_streamer_handle h, size_t *max_num_samps_out);
        uhd_error uhd_tx_streamer_max_num_samps(uhd_tx_streamer_handle h, size_t *max_num_samps_out);
        uhd_error uhd_rx_streamer_issue_stream_cmd(uhd_rx_streamer_handle h, const uhd_stream_cmd_t *stream_cmd);

        uhd_error uhd_rx_metadata_error_code(uhd_rx_metadata_handle h, uhd_rx_metadata_error_code_t *error_code_out);
        uhd_error uhd_rx_metadata_strerror(uhd_rx_metadata_handle h, char* strerror_out, size_t strbuffer_len);

        uhd_error uhd_usrp_last_error(uhd_usrp_handle h, char* error_out, size_t strbuffer_len);
        uhd_error uhd_rx_streamer_last_error(uhd_rx_streamer_handle h, char* error_out, size_t strbuffer_len);
        uhd_error uhd_tx_streamer_last_error(uhd_tx_streamer_handle h, char* error_out, size_t strbuffer_len);

        uhd_error uhd_meta_range_to_pp_string(uhd_meta_range_handle h, char* pp_string_out, size_t strbuffer_len);
        uhd_error uhd_meta_range_start(uhd_meta_range_handle h, double *start_out);
        uhd_error uhd_meta_range_stop(uhd_meta_range_handle h, double *stop_out);
        uhd_error uhd_meta_range_step(uhd_meta_range_handle h, double *step_out);

        uhd_error uhd_string_vector_at(uhd_string_vector_handle h, size_t index, char* value_out, size_t strbuffer_len);
        uhd_error uhd_string_vector_size(uhd_string_vector_handle h, size_t *size_out);

        uhd_error uhd_rx_streamer_recv(uhd_rx_streamer_handle h, void** buffs, size_t samps_per_buff, uhd_rx_metadata_handle *md, double timeout, bool one_packet, size_t *items_recvd);
        uhd_error uhd_tx_streamer_send(uhd_tx_streamer_handle h, const void **buffs, size_t samps_per_buff, uhd_tx_metadata_handle *md, double timeout, size_t *items_sent);
    ]]
end
local libuhd_available, libuhd

function UHDSource:initialize()
    -- Load UHD library here, because it writes some version information to
    -- stdout
    libuhd_available, libuhd = pcall(ffi.load, "libuhd")

    -- Check library is available
    if not libuhd_available then
        error("UHDSource: libuhd not found. Is libuhd installed?")
    end
end

local function uhd_code_strerror(code)
    local uhd_error_codes = {
        [ffi.C.UHD_ERROR_NONE] = "UHD_ERROR_NONE",
        [ffi.C.UHD_ERROR_INVALID_DEVICE] = "UHD_ERROR_INVALID_DEVICE",
        [ffi.C.UHD_ERROR_INDEX] = "UHD_ERROR_INDEX",
        [ffi.C.UHD_ERROR_KEY] = "UHD_ERROR_KEY",
        [ffi.C.UHD_ERROR_NOT_IMPLEMENTED] = "UHD_ERROR_NOT_IMPLEMENTED",
        [ffi.C.UHD_ERROR_USB] = "UHD_ERROR_USB",
        [ffi.C.UHD_ERROR_IO] = "UHD_ERROR_IO",
        [ffi.C.UHD_ERROR_OS] = "UHD_ERROR_OS",
        [ffi.C.UHD_ERROR_ASSERTION] = "UHD_ERROR_ASSERTION",
        [ffi.C.UHD_ERROR_LOOKUP] = "UHD_ERROR_LOOKUP",
        [ffi.C.UHD_ERROR_TYPE] = "UHD_ERROR_TYPE",
        [ffi.C.UHD_ERROR_VALUE] = "UHD_ERROR_VALUE",
        [ffi.C.UHD_ERROR_RUNTIME] = "UHD_ERROR_RUNTIME",
        [ffi.C.UHD_ERROR_ENVIRONMENT] = "UHD_ERROR_ENVIRONMENT",
        [ffi.C.UHD_ERROR_SYSTEM] = "UHD_ERROR_SYSTEM",
        [ffi.C.UHD_ERROR_EXCEPT] = "UHD_ERROR_EXCEPT",
        [ffi.C.UHD_ERROR_BOOSTEXCEPT] = "UHD_ERROR_BOOSTEXCEPT",
        [ffi.C.UHD_ERROR_STDEXCEPT] = "UHD_ERROR_STDEXCEPT",
        [ffi.C.UHD_ERROR_UNKNOWN] = "UHD_ERROR_UNKNOWN",
    }
    return uhd_error_codes[tonumber(code)] or "Unknown"
end

local function rx_metadata_code_strerror(code)
    local rx_metadata_error_codes = {
        [ffi.C.UHD_RX_METADATA_ERROR_CODE_NONE] = "UHD_RX_METADATA_ERROR_CODE_NONE",
        [ffi.C.UHD_RX_METADATA_ERROR_CODE_TIMEOUT] = "UHD_RX_METADATA_ERROR_CODE_TIMEOUT",
        [ffi.C.UHD_RX_METADATA_ERROR_CODE_LATE_COMMAND] = "UHD_RX_METADATA_ERROR_CODE_LATE_COMMAND",
        [ffi.C.UHD_RX_METADATA_ERROR_CODE_BROKEN_CHAIN] = "UHD_RX_METADATA_ERROR_CODE_BROKEN_CHAIN",
        [ffi.C.UHD_RX_METADATA_ERROR_CODE_OVERFLOW] = "UHD_RX_METADATA_ERROR_CODE_OVERFLOW",
        [ffi.C.UHD_RX_METADATA_ERROR_CODE_ALIGNMENT] = "UHD_RX_METADATA_ERROR_CODE_ALIGNMENT",
        [ffi.C.UHD_RX_METADATA_ERROR_CODE_BAD_PACKET] = "UHD_RX_METADATA_ERROR_CODE_BAD_PACKET",
    }
    return rx_metadata_error_codes[tonumber(code)] or "Unknown"
end

local function uhd_last_strerror(usrp_handle)
    local errmsg = ffi.new("char[512]")
    libuhd.uhd_usrp_last_error(usrp_handle, errmsg, ffi.sizeof(errmsg))
    return ffi.string(errmsg)
end

local function rx_streamer_last_strerror(rx_streamer_handle)
    local errmsg = ffi.new("char[512]")
    libuhd.uhd_rx_streamer_last_error(rx_streamer_handle, errmsg, ffi.sizeof(errmsg))
    return ffi.string(errmsg)
end

function UHDSource:debug_dump_usrp(usrp_handle)
    local ret

    -- Number of channels
    local num_channels = ffi.new("size_t[1]")
    ret = libuhd.uhd_usrp_get_rx_num_channels(usrp_handle, num_channels)
    if ret ~= 0 then
        error("uhd_usrp_get_rx_num_channels(): " .. uhd_last_strerror(usrp_handle))
    end
    debug.printf("[UHDSource] Number of RX channels: %u\n", tonumber(num_channels[0]))

    -- Helper functions to construct and access metarange objects
    local function uhd_meta_range_new()
        local meta_range_handle = ffi.new("uhd_meta_range_handle[1]")
        local ret = libuhd.uhd_meta_range_make(meta_range_handle)
        if ret ~= 0 then
            error("uhd_meta_range_make(): " .. uhd_code_strerror(ret))
        end
        meta_range_handle = ffi.gc(meta_range_handle, libuhd.uhd_meta_range_free)
        return meta_range_handle
    end

    local function uhd_meta_range_start(meta_range_handle)
        local value = ffi.new("double[1]")
        local ret = libuhd.uhd_meta_range_start(meta_range_handle, value)
        if ret ~= 0 then
            error("uhd_meta_range_start(): " .. uhd_code_strerror(ret))
        end
        return value[0]
    end

    local function uhd_meta_range_stop(meta_range_handle)
        local value = ffi.new("double[1]")
        local ret = libuhd.uhd_meta_range_stop(meta_range_handle, value)
        if ret ~= 0 then
            error("uhd_meta_range_stop(): " .. uhd_code_strerror(ret))
        end
        return value[0]
    end

    local function uhd_string_vector_new()
        local string_vector_handle = ffi.new("uhd_string_vector_handle[1]")
        local ret = libuhd.uhd_string_vector_make(string_vector_handle)
        if ret ~= 0 then
            error("uhd_string_vector_make(): " .. uhd_code_strerror(ret))
        end
        string_vector_handle = ffi.gc(string_vector_handle, libuhd.uhd_string_vector_free)
        return string_vector_handle
    end

    local function uhd_string_vector_to_array(string_vector_handle)
        local length = ffi.new("size_t[1]")
        local ret = libuhd.uhd_string_vector_size(string_vector_handle, length)
        if ret ~= 0 then
            error("uhd_string_vector_make(): " .. uhd_code_strerror(ret))
        end

        local strings = {}
        for i = 0, tonumber(length[0])-1 do
            local s = ffi.new("char[128]")
            ret = libuhd.uhd_string_vector_at(string_vector_handle, i, s, ffi.sizeof(s))
            if ret ~= 0 then
                error("uhd_string_vector_at(): " .. uhd_code_strerror(ret))
            end
            strings[i+1] = ffi.string(s)
        end

        return strings
    end

    for channel=0, tonumber(num_channels[0])-1 do
        debug.printf("[UHDSource] RX Channel %d\n", channel)

        -- Supported sample rates
        local rates_range = uhd_meta_range_new()
        ret = libuhd.uhd_usrp_get_rx_rates(usrp_handle, channel, rates_range[0])
        if ret ~= 0 then
            error("uhd_usrp_get_rx_rates(): " .. uhd_last_strerror(usrp_handle))
        end
        debug.printf("[UHDSource]     Sample rate:      %f - %f Hz\n", uhd_meta_range_start(rates_range[0]), uhd_meta_range_stop(rates_range[0]))

        -- Center frequency range
        local freq_range = uhd_meta_range_new()
        ret = libuhd.uhd_usrp_get_rx_freq_range(usrp_handle, channel, freq_range[0])
        if ret ~= 0 then
            error("uhd_usrp_get_rx_freq_range(): " .. uhd_last_strerror(usrp_handle))
        end
        debug.printf("[UHDSource]     Center frequency: %f - %f Hz\n", uhd_meta_range_start(freq_range[0]), uhd_meta_range_stop(freq_range[0]))

        -- Bandwidth ranges
        local bandwidth_range = uhd_meta_range_new()
        ret = libuhd.uhd_usrp_get_rx_bandwidth_range(usrp_handle, channel, bandwidth_range[0])
        if ret ~= 0 then
            error("uhd_usrp_get_rx_bandwidth_range(): " .. uhd_last_strerror(usrp_handle))
        end
        debug.printf("[UHDSource]     Bandwidth:        %f - %f Hz\n", uhd_meta_range_start(bandwidth_range[0]), uhd_meta_range_stop(bandwidth_range[0]))

        -- Overall gain range
        local gain_range = uhd_meta_range_new()
        ret = libuhd.uhd_usrp_get_rx_gain_range(usrp_handle, "", channel, gain_range[0])
        if ret ~= 0 then
            error("uhd_usrp_get_rx_gain_range(): " .. uhd_last_strerror(usrp_handle))
        end
        debug.printf("[UHDSource]     Overall gain:     %f - %f dB\n", uhd_meta_range_start(gain_range[0]), uhd_meta_range_stop(gain_range[0]))

        -- Gain element ranges
        debug.printf("[UHDSource]     Gain elements:\n")
        local gain_names_vector = uhd_string_vector_new()
        ret = libuhd.uhd_usrp_get_rx_gain_names(usrp_handle, channel, gain_names_vector)
        if ret ~= 0 then
            error("uhd_usrp_get_rx_gain_names(): " .. uhd_last_strerror(usrp_handle))
        end

        local gain_names = uhd_string_vector_to_array(gain_names_vector[0])


text:
---@class CS.FairyGUI.Utils.XML
---@field public name string
---@field public text string
---@field public attributes CS.System.Collections.Generic.Dictionary_CS.System.String_CS.System.String
---@field public elements CS.FairyGUI.Utils.XMLList

---@type CS.FairyGUI.Utils.XML
CS.FairyGUI.Utils.XML = { }
---@return CS.FairyGUI.Utils.XML
---@param XmlString string
function CS.FairyGUI.Utils.XML.New(XmlString) end
---@return CS.FairyGUI.Utils.XML
---@param tag string
function CS.FairyGUI.Utils.XML.Create(tag) end
---@return boolean
---@param attrName string
function CS.FairyGUI.Utils.XML:HasAttribute(attrName) end
---@overload fun(attrName:string): string
---@return string
---@param attrName string
---@param optional defValue string
function CS.FairyGUI.Utils.XML:GetAttribute(attrName, defValue) end
---@overload fun(attrName:string): number
---@return number
---@param attrName string
---@param optional defValue number
function CS.FairyGUI.Utils.XML:GetAttributeInt(attrName, defValue) end
---@overload fun(attrName:string): number
---@return number
---@param attrName string
---@param optional defValue number
function CS.FairyGUI.Utils.XML:GetAttributeFloat(attrName, defValue) end
---@overload fun(attrName:string): boolean
---@return boolean
---@param attrName string
---@param optional defValue boolean
function CS.FairyGUI.Utils.XML:GetAttributeBool(attrName, defValue) end
---@overload fun(attrName:string): String[]
---@overload fun(attrName:string, seperator:number): String[]
---@overload fun(attrName:string, i1:CS.System.Int32, i2:CS.System.Int32): boolean
---@overload fun(attrName:string, f1:CS.System.Single, f2:CS.System.Single): boolean
---@overload fun(attrName:string, s1:CS.System.String, s2:CS.System.String): boolean
---@overload fun(attrName:string, i1:CS.System.Int32, i2:CS.System.Int32, i3:CS.System.Int32, i4:CS.System.Int32): boolean
---@return String[]
---@param attrName string
---@param optional f1 CS.System.Single
---@param optional f2 CS.System.Single
---@param optional f3 CS.System.Single
---@param optional f4 CS.System.Single
function CS.FairyGUI.Utils.XML:GetAttributeArray(attrName, f1, f2, f3, f4) end
---@return CS.UnityEngine.Color
---@param attrName string
---@param defValue CS.UnityEngine.Color
function CS.FairyGUI.Utils.XML:GetAttributeColor(attrName, defValue) end
---@return CS.UnityEngine.Vector2
---@param attrName string
function CS.FairyGUI.Utils.XML:GetAttributeVector(attrName) end
---@overload fun(attrName:string, attrValue:string): void
---@overload fun(attrName:string, attrValue:boolean): void
---@overload fun(attrName:string, attrValue:number): void
---@param attrName string
---@param attrValue number
function CS.FairyGUI.Utils.XML:SetAttribute(attrName, attrValue) end
---@param attrName string
function CS.FairyGUI.Utils.XML:RemoveAttribute(attrName) end
---@return CS.FairyGUI.Utils.XML
---@param selector string
function CS.FairyGUI.Utils.XML:GetNode(selector) end
---@overload fun(): CS.FairyGUI.Utils.XMLList
---@return CS.FairyGUI.Utils.XMLList
---@param optional selector string
function CS.FairyGUI.Utils.XML:Elements(selector) end
---@overload fun(): CS.FairyGUI.Utils.XMLList.Enumerator
---@return CS.FairyGUI.Utils.XMLList.Enumerator
---@param optional selector string
function CS.FairyGUI.Utils.XML:GetEnumerator(selector) end
---@param child CS.FairyGUI.Utils.XML
function CS.FairyGUI.Utils.XML:AppendChild(child) end
---@param child CS.FairyGUI.Utils.XML
function CS.FairyGUI.Utils.XML:RemoveChild(child) end
---@param selector string
function CS.FairyGUI.Utils.XML:RemoveChildren(selector) end
---@param aSource string
function CS.FairyGUI.Utils.XML:Parse(aSource) end
function CS.FairyGUI.Utils.XML:Reset() end
---@return string
---@param includeHeader boolean
function CS.FairyGUI.Utils.XML:ToXMLString(includeHeader) end
return CS.FairyGUI.Utils.XML


text:
dofile("./isTransparentPixel.lua")

----------------------------------------------------------------------
-- Removes any non-transparent pixels that overlap
-- with the given image.
--
-- PARAMS
-- src (Image)
-- overlap (Image)
--
-- RETURNS
-- None
----------------------------------------------------------------------

function removeImageOverlap(src, overlap)
  for x = 0, overlap.width - 1, 1 do
    for y = 0, overlap.height - 1, 1 do
      local curPixel = overlap:getPixel(x, y)
      if not isTransparentPixel(curPixel) then
        src:drawPixel(x, y, app.pixelColor.rgba(0, 0, 0, 0))
      end
    end
  end
end


text:
local map = require('config.utils').map

local silent = { silent = true }

-- Navigate buffers and repos
map('n', '<leader>pe', [[<cmd>Telescope find_files<cr>]], silent)
map('n', '<leader>pb', [[<cmd>Telescope buffers<cr>]], silent)
map('n', '<leader>pf', [[<cmd>Telescope frecency<cr>]], silent)
map('n', '<leader>pg', [[<cmd>Telescope live_grep<cr>]], silent)
map('n', '<leader>pG', [[<cmd>Telescope grep_string<cr>]], silent)
map('n', '<leader>ph', [[<cmd>Telescope help_tags<cr>]], silent)
map('n', '<leader>p.', [[<cmd>Telescope file_browser<cr>]], silent)
map('n', '<leader>pt', [[<cmd>Telescope treesitter<cr>]], silent)
map('n', '<leader>gf', [[<cmd>Telescope git_files<cr>]], silent)
map('n', '<leader>gc', [[<cmd>Telescope git_commits<cr>]], silent)
map('n', '<leader>bg', [[<cmd>Telescope git_bcommits<cr>]], silent)
map('n', '<leader>gb', [[<cmd>Telescope git_branches<cr>]], silent)
map('n', '<leader>gs', [[<cmd>Telescope git_status<cr>]], silent)
map('n', '<leader>gS', [[<cmd>Telescope git_stash<cr>]], silent)
map('n', '<leader>tt', [[<cmd>Telescope builtin<cr>]], silent)


text:
--.------------------------------------------------
--. Rules_38_To_37.lua  (Version:1.0)            --
--. Applies rules from core 38 to core 37        --
--.------------------------------------------------

--.--------------------
--. Prefix functions --
--.--------------------
module("CoreRules", package.seeall)

function rejectAll(Fields, Where)
    return false
end

-- Kinds 12.1  and 13.1 are new in core 39 (CBLOB and BLOB)
function rejectLobExts(Fields, Where)
    if Fields.FK_KIND == "12.1"  or Fields.FK_KIND == "13.1"  then
        return false
    end
    return true
end

function replaceVersion (Fields)
    if Fields.NVERSION == "'39'" then
        return "'38'"
    else
        return Fields.NVERSION
    end
end

function InitRules()

    local NewTable = {
				insert = {
					lineFilter = rejectAll
				}
				,update = {
					lineFilter = rejectAll
				}
				,delete = {
					lineFilter = rejectAll
				}
            }

    local ReplaceNVersion = {
				insert = {
					replaceValue = { NVERSION = replaceVersion }
				}
				,update = {
					replaceValue = { NVERSION = replaceVersion }
				}
            }

    local currentRules = {
            EXT_DEF = {
				insert = {
                    removeColumns = { LOB_COL = 1 }
					,replaceValue = { NVERSION = replaceVersion }
                    ,lineFilter = rejectLobExts
				}
				,update = {
                    removeColumns = { LOB_COL = 1 }
					,replaceValue = { NVERSION = replaceVersion }
                    ,lineFilter = rejectLobExts
				}
            }
-- Correct the NVersion Collumn
            ,EDIT_DEF = ReplaceNVersion
            ,MENU_ITEM = ReplaceNVersion
-- New Tables
            ,SQL_SCRIPT = NewTable
    }

   return currentRules

end


text:
-- Stand Power Star Platinum
local s, id = GetID()
function s.initial_effect( c )
  -- You can only control 1 "${SP} Star Platinum".
  c:SetUniqueOnField(1, 0, id)
  -- Can only be equipped to a "${SU} Kujo Jotaro" you control.
  aux.AddEquipProcedure(c, 0, aux.FilterBoolFunction(Card.IsCode, 1090076074))
  -- Destroy this instead
  local e1 = Effect.CreateEffect(c)
  e1:SetType(EFFECT_TYPE_CONTINUOUS + EFFECT_TYPE_EQUIP)
  e1:SetProperty(EFFECT_FLAG_IGNORE_IMMUNE)
  e1:SetCode(EFFECT_DESTROY_REPLACE)
  e1:SetTarget(s.desreptg)
  e1:SetOperation(s.desrepop)
  c:RegisterEffect(e1)
  -- Gains 1000 ATK
  local e2 = Effect.CreateEffect(c)
  e2:SetType(EFFECT_TYPE_EQUIP)
  e2:SetCategory(CATEGORY_ATKCHANGE)
  e2:SetCode(EFFECT_UPDATE_ATTACK)
  e2:SetValue(1000)
  c:RegisterEffect(e2)
  -- Attack Twice
  local e3 = Effect.CreateEffect(c)
  e3:SetType(EFFECT_TYPE_EQUIP)
  e3:SetCode(EFFECT_EXTRA_ATTACK)
  e3:SetValue(1)
  c:RegisterEffect(e3)
  -- battle resist
  local e4 = Effect.CreateEffect(c)
  e4:SetType(EFFECT_TYPE_EQUIP)
  e4:SetCode(EFFECT_INDESTRUCTABLE_BATTLE)
  e4:SetValue(1)
  c:RegisterEffect(e4)
end

s.listed_names = { 2233678692, 1090076074 }
function s.desreptg( e, tp, eg, ep, ev, re, r, rp, chk )
  local c = e:GetHandler()
  local tg = c:GetEquipTarget()
  if chk == 0 then
    return c:IsDestructable(e) and not c:IsStatus(STATUS_DESTROY_CONFIRMED) and
             tg and tg:IsReason(REASON_BATTLE + REASON_EFFECT)
  end
  return Duel.SelectEffectYesNo(tp, c, 96)
end
function s.desrepop( e, tp, eg, ep, ev, re, r, rp )
  Duel.Destroy(e:GetHandler(), REASON_EFFECT + REASON_REPLACE)
end


text:
os.loadAPI("button")
m = peripheral.wrap("top")
rednet.open("left")
m.clear()

function fillTable()
   button.setTable("Sheep",sheer,10,25,3,6)
   button.setTable("Place Mobs",place,10,25,8,11)
   button.setTable("Remove Mobs",remove,10,25,13,16)
   button.screen()
end

function getClick()
   event,side,x,y = os.pullEvent("monitor_touch")
   button.checkxy(x,y)
end

function place()
   button.toggleButton("Place Mobs")
   shell.run("sendmsg","place")
   button.toggleButton("Place Mobs")
end

function remove()
   button.toggleButton("Remove Mobs")
   shell.run("sendmsg","remove2")
   button.toggleButton("Remove Mobs")
end

function sheer()
   button.flash("Sheep")
   shell.run("sheepmsg")
end

fillTable()
while true do getClick() end


text:
local Chara = Elona.require("Chara")
local I18N = Elona.require("I18N")
local Input = Elona.require("Input")
local Internal = Elona.require("Internal")
local Registry = Elona.require("Registry")
local table = Elona.require("table")

local function dialog_error(talk, msg, err)
   if err ~= nil then
      error("Dialog error in " .. talk.id .. ": " .. msg .. ":\n    " .. err)
   else
      error("Dialog error in " .. talk.id .. ": " .. msg)
   end
end

--- Convert a response localization key to the full localization key.
--- It can be one of the following.
---  * __MORE__                - "(More)"
---  * __BYE__                 - "Bye bye."
---  * key.fragment            - core.locale.dialog.root.key.fragment
---  * core.locale.dialog.key  - core.locale.dialog.key
---  * {"key.fragment", args = {"arg1", "arg2"}}  - (localized with arguments)
local function resolve_response(obj, talk)
   local args = {}
   local get
   local key

   if obj == nil then
      key = "__MORE__"
   elseif type(obj) == "table" then
      key = obj[1]
      args = obj.args
   else
      key = obj
   end

   if key == "__BYE__" then
      return "core.locale.ui.bye", args
   elseif key == "__MORE__" then
      return "core.locale.ui.more", args
   end

   get = talk.dialog.root .. "." .. key

   if I18N.get_optional(get) == nil then
      get = key
   end
   return get, args
end

--- Opens a single talk window choice.
-- @tparam table talk Dialog control data.
-- @tparam string text String to display (not locale key)
-- @tparam table choices List of choices in format {"response_id", "core.locale.key"}
-- @tparam[opt] num default_choice index of default choice if window is canceled
-- @treturn string Response ID of the choice selected.
local function query(talk, text, choices, default_choice)
   local image = talk.speaker.portrait
   if image == "" then
      image = talk.speaker.image
   end
   local show_impress = true
   if talk.speaker.quality == "Special" and not Chara.is_ally(talk.speaker) then
      show_impress = false
   end

   if #choices == 1 then
      default_choice = 0
   end

   local the_choices = {}
   for i, choice in ipairs(choices) do
      local response, args = resolve_response(choice[2], talk)
      the_choices[i] = I18N.get(response, table.unpack(args))
      if default_choice == nil and choice[1] == "__END__" then
         default_choice = i - 1
      end
   end

   local result
   if show_impress then
      result = Input.prompt_dialog(text, image, Internal.speaker_name(talk.speaker), the_choices, default_choice, talk.speaker.impression, talk.speaker.interest)
   else
      result = Input.prompt_dialog(text, image, Internal.speaker_name(talk.speaker), the_choices, default_choice)
   end

   return choices[result + 1][1]
end

--- Initializes the dialog control data.
-- @tparam LuaCharacter speaker Character who is speaking.
-- @tparam core.dialog dialog Dialog data.
-- @tparam string id Dialog data ID.
-- @treturn table Dialog control data.
local function make_talk(speaker, dialog, id)
   return {
      id = id,
      speaker = speaker,
      dialog = dialog,
      say = function(self, key, response)
         local resolved, args = resolve_response(key, self)
         query(self, I18N.get(resolved, table.unpack(args)), {{"dummy", resolve_response(response, self)}})
      end
   }
end

--- Jumps to a node in a dialog outside the current dialog.
-- @tparam table talk Current dialog control data.
-- @tparam string new_dialog_id New dialog ID to change to.
-- @tparam string node Node inside the dialog to jump to.
-- @treturn table Current node data in {choice, opts} format
local function jump_to_dialog(talk, new_dialog_id, node)
   local dialog = Registry.get_table("core.dialog")[new_dialog_id]
   if dialog == nil then
      error("No such dialog " .. new_dialog_id)
   end
   talk.dialog = dialog
   return {choice = node, opts = {}}
end

local function step_dialog(dialog, node_data, talk, state)
   if node_data.choice == "__END__" then
      return nil
   end
   if node_data.choice == "__IGNORED__" then
      return jump_to_dialog(talk, "core.ignored", "__start")
   end
   if node_data.choice == nil then
      return nil
   end

   local node = dialog.nodes[node_data.choice]
   if node == nil then
      dialog_error(talk, "No node with ID " .. node_data.choice .. " found")
   end

   local next_node = nil

   if type(node) == "function" then
      -- Run function. It is expected to return either a string or
      -- table containing the next node to jump to.
      local ok, result = pcall(node, talk, state, node_data.opts)
      if ok then
         if type(result) == "string" then
            next_node = {choice = result, opts = {}}
         elseif type(result) == "table" then
            next_node = result
         end
      else
         dialog_error(talk, "Error running dialog inline function", result)
      end
   elseif type(node) == "table" then
      -- Parse table structure for dialog data.
      -- text: array of text entries, displayed in order.
      --     text[1][1]: locale fragment or key, or function
      --     text[1].args: function returning table of arguments to localization
      --     text[1].speaker: core.chara ID of speaker to display, if they are in this map
      --     text[1].choice: locale key of default choice. Defaults to "more" or "bye".
      -- choices: array of choices, displayed at last text entry. If nil, end the dialog at the last one.
      --     choices[1][1]: Node ID to jump to
      --     choices[1][2]: locale key of choice. Defaults to "more" or "bye".
      -- on_finish: Function run when this node is exited.
      -- default_choice: If provided, dialog option is cancellable with this response as the default.
      local texts = node.text

      for i, text in ipairs(texts) do
         if texts[i+1] == nil then
            if type(text) ~= "table" then
               dialog_error(talk, "Last text entry must be table (got: " .. type(text) .. ")")
            end
         end

         if type(text) == "table" then
            -- Obtain arguments to I18N.get().
            local args = {}
            if text.args then
               local ok
               ok, args = pcall(text.args, talk, state, node_data.opts)
               if not ok then
                  dialog_error(talk, "Error getting I18N arguments", args)
               end
            end

            -- Get localization key of default response ("more", "bye").
            local choice_key = text.choice
            if choice_key == nil then
               if texts[i+1] == nil then
                  choice_key = "__BYE__"
               else
                  choice_key = "__MORE__"
               end
            end

            -- Change speaking character.
            if text.speaker ~= nil then
               local found = Chara.find(text.speaker, "Others")
               if found == nil then
                  found = Chara.find(text.speaker, "Allies")
               end
               if found ~= nil then
                  talk.speaker = found
               end
            end

            -- Build choices. Default to ending the dialog.
            local choices = node.choices
            if choices == nil then
               choices = {{"__END__", choice_key}}
            elseif type(choices) == "function" then
               local ok
               ok, choices = pcall(choices, talk, state, node_data.opts)
               if not ok then
                  dialog_error(talk, "Error running choices function", choices)
               end
            end

            -- Set the default choice to select if window is
            -- cancelled. If nil, prevent cancellation.
            local default_choice = nil
            if text.default_choice ~= nil then
               for j, choice in ipairs(choices) do
                  if choice[1] == text.default_choice then
                     default_choice = j - 1
                  end
               end
            end

            -- Resolve localized text.
            local key = talk.dialog.root .. "." .. text[1]
            local tex = I18N.get_optional(key, table.unpack(args))
            if tex == nil then
               tex = I18N.get(text[1], table.unpack(args))
            end

            -- Prompt for choice if on the last text entry, otherwise
            -- show single choice.
            if texts[i+1] == nil then
               local choice = query(talk, tex, choices, default_choice)
               next_node = {choice = choice, opts = {}}
            else
               query(talk, tex, {{"dummy", choice_key}})
            end
         elseif type(text) == "function" then
            -- Call an arbitrary function. The result is ignored.
            local ok, err = pcall(text, talk, state, node_data.opts)
            if not ok then
               dialog_error(talk, "Error running text function", err)
            end
         else
            dialog_error(talk, "Cannot parse text entry, must be function or table (got: " .. type(text) .. ")")
         end
      end

      -- Run on_finish callback.
      if node.on_finish then
         local ok, result = pcall(node.on_finish, talk, state, node_data.opts)
         if not ok then
            dialog_error(talk, "Error running on_finish function", result)
         end
      end
   else
      dialog_error(talk, "Invalid node, must be string or table (got: " .. type(node) .. ")")
   end

   return next_node
end

--- Starts a dialog with a character speaking.
-- @tparam LuaCharacter chara The speaking character.
-- @tparam string dialog_id ID of core.dialog to use.
local function show_dialog(chara, id)
   local dialog = Registry.get_table("core.dialog")[id]
   if dialog == nil then
      error("No such dialog " .. id)
   end
   local talk = make_talk(chara, dialog, id)
   local state = {}
   local next_node = {choice = "__start", opts = {}}

   while next_node ~= nil do
      next_node = step_dialog(talk.dialog, next_node, talk, state)
   end
end

return show_dialog


text:
local f = string.format

My.Translator:register("de", {
    side_mission_destroy_graveyard_too_close = "Sie halten sich zu nah am Zielgebiet auf und können die Mission darum nicht annehmen.",
    side_mission_destroy_graveyard = function(sectorName)
        return f("Weltraumschrott in Sektor %s zerstören", sectorName)
    end,
    side_mission_destroy_graveyard_description = function(sectorName, number, payment)
        return Util.random({
            "Sie haben vielleicht schon bemerkt, dass wir hier ein Problem mit Weltraumschrott haben.",
            "Zugegeben, diese Mission ist nicht die ruhmreichste. Aber irgendjemand muss sie erledigen.",
        }) .. " " .. Util.random({
            f("Auf dem Schiffsfriedhof im Sektor %s haben sich einige Wracks selbstständig gemacht und drohen in die nahegelegenen Handelsrouten abzudriften. Sie müssen die Wracks von %d Schiffen pulverisieren.", sectorName, number),
            f("%d Schiffe auf dem nahegelegenen Schiffsfriedhof drohen ihren stabilen Orbit zu verlassen. Damit sie die nahen Reiserouten nicht gefährden, suchen wir jemanden, der sie pulverisiert.", number),
        }) .. "\n\n" .. Util.random({
            f("Wir zahlen %0.2fRP fürs Erledigen dieses Auftrags.", payment),
            f("Wenn Sie sich erbarmen, diesen Auftrag auszuführen, zahlen wir Ihnen %0.2fRP.", payment)
        })

    end,
    side_mission_destroy_graveyard_hint = function(numberOfTargets)
        if numberOfTargets > 1 then
            return f("Zerstören Sie %d weitere markierte Schiffe auf dem Schiffsfriedhof.", numberOfTargets)
        else
            return "Zerstören Sie das letzte markierte Schiff auf dem Schiffsfriedhof."
        end
    end,

    side_mission_destroy_graveyard_success_comms = function(payment)
        return Util.random({
            "Hervorragend. Sie haben bewiesen, dass sie auf unbewegliche Ziele feuern können.",
        }) .. " " .. Util.random({
            "Sollten wir wieder jemanden brauchen, um mit überdimensional Waffen unbewegliche Ziele zu zerstören, kommen wir vielleicht wieder auf Sie zu.",
            "Machen Sie sich nichts draus: Jeder gute Kampfpilot hat mal klein angefangen.",
            "Wenn Sie sich weiterhin so ins Zeug legen, lassen wir Sie demnächst vielleicht auch auf bewegliche Ziele schießen - so wie die großen Jungs.",
            "Hören sie nicht auf die Leute, die sagen, dass die Mission auch eine Gruppe Pfadfinderinnen hätte ausführen können. Die sind nur neidisch.",
        }) .. "\n\n" .. Util.random({
            f("Hier haben sie %0.2fRP.", payment),
            f("Wie vereinbart bekommen Sie von uns %0.2fRP.", payment),
        }) .. " " .. Util.random({
            "Sie dürfen sich davon im Spieleland einen Orden für Sauberkeit kaufen.",
            "Kaufen Sie sich ein Eis davon.",
            "Wenn Sie das Geld gut anlegen, können sie sich in einigen Jahren bestimmt ein Puppenhaus kaufen.",
        })
    end,

    side_mission_destroy_graveyard_raid_comms = function(captainPerson)
        return Util.random({
            f("Halten Sie sich von diesem Schiffscontainer fern. Er ist jetzt offiziell Eigentum von Captain %s.", captainPerson:getFormalName()),
            "Finger weg von dem Schiffscontainer, oder sie werden es mit mir zu tun bekommen.",
        })
    end
})


text:
registerNpc(1253, {
  walk_speed        = 0,
  run_speed         = 0,
  scale             = 70,
  r_weapon          = 0,
  l_weapon          = 0,
  level             = 10,
  hp                = 100,
  attack            = 100,
  hit               = 100,
  def               = 100,
  res               = 100,
  avoid             = 100,
  attack_spd        = 100,
  is_magic_damage   = 0,
  ai_type           = 236,
  give_exp          = 0,
  drop_type         = 72,
  drop_money        = 0,
  drop_item         = 0,
  union_number      = 0,
  need_summon_count = 0,
  sell_tab0         = 0,
  sell_tab1         = 0,
  sell_tab2         = 0,
  sell_tab3         = 0,
  can_target        = 0,
  attack_range      = 200,
  npc_type          = 999,
  hit_material_type = 0,
  face_icon         = 0,
  summon_mob_type   = 0,
  quest_type        = 0,
  height            = 0
});

function OnInit(entity)
  return true
end

function OnCreate(entity)
  return true
end

function OnDelete(entity)
  return true
end

function OnDead(entity)
end

function OnDamaged(entity)
end


text:
local http2 = require "http2"
local core = require "sys.core"
local crypto = require "sys.crypto"
local json = require "sys.json"
local waitgroup = require "sys.waitgroup"
local testaux = require "testaux"

local f<const> = io.open("./a.txt", "w")

return function()
	if not crypto.digestsign then
		print("not enable openssl")
		return
	end
	http2.listen {
		tls_port = ":8081",
		tls_cert= "test/cert.pem",
		tls_key = "test/key.pem",
		handler = function(stream)
			core.sleep(math.random(1, 300))
			local header = stream:read()
			testaux.asserteq(stream.method, "POST", "http2.server method")
			testaux.asserteq(stream.path, "/test", "http2.server path")
			testaux.asserteq(header['hello'], "world", "http2.server header")
			local body = stream:readall()
			testaux.asserteq(body, "http2", "http2 body")
			stream:ack(200, {["foo"] = header['foo']})
			stream:write("http2")
			stream:close()
		end
	}
	local n = 0
	print("test http2 client")
	--[[ disable test http2 client for temporary
	local wg = waitgroup:create()
	for i = 1, 2000 do
		wg:fork(function()
			local key = crypto.randomkey(1028)
			local status, header, body = http2.POST("https://http2.golang.org/reqinfo", {
				['hello'] = 'world',
				['foo'] = key,
			}, "http2")
			n = n + 1
			print("test", n)
			testaux.asserteq(status, 200, "http2.client status")
			testaux.assertneq(body:find("Foo: " .. key), nil, "http2.header key")
			testaux.assertneq(body:find("Hello: world"), nil, "http2.header key")
		end)
	end
	wg:wait()
	]]
	print("test http2 server")
	local wg = waitgroup:create()
	for i = 1, 2000 do
		wg:fork(function()
			local key = crypto.randomkey(1028)
			local status, header, body = http2.POST("https://127.0.0.1:8081/test", {
				['hello'] = 'world',
				['foo'] = key,
			}, "http2")
			testaux.asserteq(status, 200, "http2.client status")
			testaux.asserteq(header['foo'], key, "http2.client header")
			testaux.asserteq(body, 'http2', "http2.client body")
		end)
	end
	wg:wait()
end


text:
local path = select(1,...):sub(1,-(string.len("container")+1))

--Container object class
local container = class("DiskOS.GUI.container")

--Default internal values:
container.static.lightcol = 9 --The light color
container.static.darkcol = 4 --The dark color
container.static.bgcol = 0 --Background color
container.static.tcol = 7 --Text color

--Create a new objects container:
--<gui>: The GUI instance of this container.
--[x],[y]: The position of the top-left corner of the object.
--[w],[h]: The size of the object.
function container:initialize(gui,parentContainer,x,y,w,h,bgcol,tcol,lightcol,darkcol,sheet)
  self.gui = gui or error("GUI State has to be passed",2)
  self.container = parentContainer or error("Container has to be passed",2)

  self.objects = {} --Registered objects

  self.x = x or 0
  self.y = y or 0
  self.w = w or screenWidth()
  self.h = h or screenHeight()

  self.visible = true

  if self.container == self then

    self.sheet = sheet

    self.lightcol = lightcol or container.static.lightcol
    self.darkcol = darkcol or container.static.darkcol
    self.bgcol = bgcol or container.static.bgcol
    self.tcol = tcol or container.static.tcol

  else

    self:setLightColor(lightcol or self.container:getLightColor(),true)
    self:setDarkColor(darkcol or self.container:getDarkColor(),true)
    self:setBGColor(bgcol or self.container:getBGColor(),true)
    self:setTColor(tcol or self.container:getTColor(),true)

    self:setSheet(sheet or self.container:getSheet(),true)

  end
end

--Get object GUI instance.
function container:getGUI()
  return self.gui
end

--Get object parent container.
function container:getContainer()
  return self.container
end

--Set object visiblilty
function container:setVisible(visible,nodraw)
  self.visible = visible or false

  if not self.visible then
    self.down, self.touchid, self.mousepressed = false, false, false
    if not nodraw then self:clear() end
  end
end

function container:isVisible()
  return self.visible
end

--Set object position.
function container:setX(x,nodraw)
  if not nodraw then self:clear() end

  self.x = x or self.x
  if not nodraw then self:draw() end

  return self
end

function container:setY(y,nodraw)
  if not nodraw then self:clear() end

  self.y = y or self.y
  if not nodraw then self:draw() end

  return self
end

function container:setPosition(x,y,nodraw)
  if not nodraw then self:clear() end

  self:setX(x,true)
  self:setY(y,true)

  if not nodraw then self:draw() end

  return self
end

--Get object position
function container:getX() return self.x end
function container:getY() return self.y end
function container:getPosition() return self:getX(), self:getY() end

--Set object size
function container:setWidth(w,nodraw)
  if not nodraw then self:clear() end
  self.w = w or self.w
  if not nodraw then self:draw() end

  return self
end

function container:setHeight(h,nodraw)
  if not nodraw then self:clear() end
  self.h = h or self.h
  if not nodraw then self:draw() end

  return self
end

function container:setSize(w,h,nodraw)
  if not nodraw then self:clear() end

  self:setWidth(w,true)
  self:setHeight(h,true)

  if not nodraw then self:draw() end

  return self
end

--Get object size
function container:getWidth() return self.w end
function container:getHeight() return self.h end
function container:getSize() return self:getWidth(), self:getHeight() end

function container:setRect(x,y,w,h,nodraw)
  if not nodraw then self:clear() end

  self:setPosition(x,y,true)
  self:setSize(x,y,true)

  if not nodraw then self:draw() end

  return self
end

function container:getRect()
  local x,y = self:getPosition()
  local w,h = self:getSize()
  return x,y,w,h
end

--Set object colors
function container:setLightColor(lightcol,nodraw)
  if not nodraw then self:clear() end
  self.lightcol = lightcol or self.lightcol
  if not nodraw then self:draw() end
  return self
end

function container:setDarkColor(darkcol,nodraw)
  if not nodraw then self:clear() end
  self.darkcol = darkcol or self.darkcol
  if not nodraw then self:draw() end
  return self
end

function container:setBGColor(bgcol,nodraw)
  if not nodraw then self:clear() end
  self.bgcol = bgcol or self.bgcol
  if not nodraw then self:draw() end
  return self
end

function container:setTColor(tcol,nodraw)
  if not nodraw then self:clear() end
  self.tcol = tcol or self.tcol
  if not nodraw then self:draw() end
  return self
end

function container:setColors(lightcol,darkcol,bgcol,tcol,nodraw)
  if not nodraw then self:clear() end

  self:setLightColor(lightcol,true)
  self:setDarkColor(darkcol,true)
  self:setBGColor(bgcol,true)
  self:setTColor(tcol,true)

  if not nodraw then self:draw() end

  return self
end

--Get object colors
function container:getLightColor() return self.lightcol end
function container:getDarkColor() return self.darkcol end
function container:getBGColor() return self.bgcol end
function container:getTColor() return self.tcol end
function container:getColors() return self:getLightColor(), self:getDarkColor(), self:getBGColor(), self:getTColor() end

--Set the object sheet
function container:setSheet(sheet,nodraw)
  if not nodraw then self:clear() end
  self.sheet = sheet or self.sheet
  if not nodraw then self:draw() end
  return self
end

--Get the object sheet
function container:getSheet() return self.sheet end

--Register a new object
function container:register(obj)
  table.insert(self.objects,obj)
end

--Return registered objects list
function container:getObjects() return self.objects end

--Create a new object
function container:newObject(name,...)
  local objclass = self.gui:getObjectClass(name) --Get the object class from the loaded objects list

  if not objclass then return error("Object '"..name.."' doesn't exist !") end

  local obj = objclass(self.gui,self,...) --Create a new object
  self:register(obj) --Register the object in this GUI instance so it's events get called.

  return obj --Return the created object
end

--Clear previous draw area.
function container:clear()
  local x,y = self:getPosition()
  local w,h = self:getSize()
  local bgColor = self.container:getBGColor()
  rect(x,y,w,h,false,bgColor)
end

--Draw the container
function container:_draw(dt)
  if self:isVisible() then
    local x,y = self:getPosition()
    local w,h = self:getSize()
    local bgColor = self:getBGColor()
    rect(x,y,w,h,false,bgColor)

    self:event("draw")
  end
end

--Hooks
function container:event(event,a,b,c,d,e,f)
  event = "_"..event

  if self[event] and event ~= "_draw" then
    if self[event](self,a,b,c,d,e,f) then
      return true --Event consumed
    end
  end

  local x,y = self:getPosition()

  pushMatrix()
  cam("translate",x,y)

  if event:sub(2,6) == "mouse" then
    a,b = a-x, b-y
  elseif event:sub(2,6) == "touch" then
    b,c = b-x, c-y
  end

  local consumed = false --Did the even get consumed ?

  for k, obj in ipairs(self:getObjects()) do
    if obj.event and event ~= "_draw" then
      if obj:event(event:sub(2,-1),a,b,c,d,e,f) then
        consumed = true
        break --Event consumed
      end
    elseif obj[event] then
      if obj[event](obj,a,b,c,d,e,f) then
        consumed = true
        break --Event consumed
      end
    end
  end

  popMatrix()

  if event == "_mousepressed" or event == "_mousereleased" then
    self:_mousemoved(a,b,0,0,d)
  end

  return consumed
end

function container:draw()
  self:_draw()
  self:event("draw")
end

function container:_mousemoved(x,y,dx,dy,istouch)
  if istouch then return end

  local cx,cy = self:getPosition()

  for k, obj in ipairs(self:getObjects()) do
    if obj.cursor then
      local c = obj:cursor(x-cx,y-cy)
      if c then
        return c
      end
    end
  end
end

function container:cursor(x,y)
  return self:_mousemoved(x,y,0,0,false)
end

return container


text:
--[[
@file geolocation.lua
@license The MIT License (MIT)
@copyright 2017 William Fleurant
--]]

--- @module geolocation

local geolocation = {}

local config = require("config")
local shell = require("lib.shell")
local threadman = require("threadman")
local support = require("support")

function geolocation.run()

    local listener = threadman.registerListener("geolocation",{"exit", "heartbeat"})
	local lastTime = 0
    while true do
        local msg = listener:listen()
        if msg ~= nil then
            if msg["type"] == "exit" then
                break
            end
        end
        if msg["type"] == "heartbeat" then
			local time = os.time()
			if time > lastTime + 30 then
				geolocation.updateLocation()
				lastTime = time
			end
		end
    end

    threadman.unregisterListener(listener)

end

function geolocation.updateLocation()
    local modules = support.getGeo()
    local bestlocation = {}
    for geomod, geo in pairs(modules) do
        local module = require("geo."..geo.module)
        -- TODO: use algo that takes in multiple readings
        -- and returns single higher accuracy reading
        bestlocation = module.queryLocation()
    end

    threadman.setShared('geolocation', bestlocation)

end

function geolocation.getLocation()
    return threadman.getShared('geolocation')
end


text:
building = {
  tileSize = 16,

  screen_height,

  x = 0,
  y = 0,
  width = 0,
  height = 0,
  body,
  shape
} -- the table representing the class, which will double as the metatable for the instances

building.__index = building -- failed table lookups on the instances should fallback to the class table, to get methods

function building:makeBuilding(x, y, tileSize)

  local self = setmetatable({}, building)

  self:setupBuilding(x, y, tileSize)

  return self
end

function building:setupBuilding(x, tileSize)

  self.tileSize = tileSize
  self.x = x
  self.y = 300

  self.width  = math.ceil((love.math.random( ) * 10) + 20)
  self.height = math.ceil(5 + love.math.random( ) * 7)
  --self.height = 7
  self.body = love.physics.newBody(world, 0, 0, "static")
  self.shape = love.physics.newRectangleShape(self.x, self.y,
                                              self.tileSize * self.width,
                                              self.tileSize * self.height)
  fixture = love.physics.newFixture(self.body, self.shape)
  fixture:setUserData("Building")
end

function building:update(body, dt, other_building)

  if self.x + self.width/2 * self.tileSize < body:getX() then
      self:setupBuilding(
          other_building.x + other_building.width  * self.tileSize + 150,
          16)
  end
end

function building:draw(tilesetBatch, tileQuads)
  x1, y1 = self.shape:getPoints()

  tilesetBatch:add(tileQuads[0], self.x, self.y, 0)
  for x=self.width - 1, 0, -1 do
    for y=0,self.height - 1, 1 do
      if x == 0 and y == 0 then
        tilesetBatch:add(tileQuads[1], x1 + x * tileSize, y1 + y * tileSize, 0)
      else
        if y == 0 and x == self.width - 1 then
          tilesetBatch:add(tileQuads[3], x1 + x * tileSize, y1 + y * tileSize, 0)
        else
          if y == 0 then
            tilesetBatch:add(tileQuads[2], x1 + x * tileSize, y1 + y * tileSize, 0)
          else
            num = math.floor(x + y + x1 + y1)
            if (num)%5 == 0 then
              --tilesetBatch:add(tileQuads[5], x1 + x * tileSize, y1 + y * tileSize, 0)
            else
              tilesetBatch:add(tileQuads[4], x1 + x * tileSize, y1 + y * tileSize, 0)
            end
          end
        end
      end
    end
  end
end


text:
-- Honestly, I have no idea why I put so much work into these cross-platform constants. I just wanted to see how this would run on lpp-vita.
PLAT_UNDEFINED = 0
PLAT_VITA = 1
PLAT_3DS = 2
currentPlatform = PLAT_3DS
if (currentPlatform == PLAT_VITA) then
	dofile("app0:vita.lua")
end

dataFilesystemPrefix = "";
if (currentPlatform == PLAT_VITA) then
	dataFilesystemPrefix = "ux0:data/MNGA00001"
end

-- Good line.
System.setCpuSpeed(NEW_3DS_CLOCK)
-- =======================
-- READING VARS
-- =======================
-- Your current menu/place
--	0 = mainRead
--	1 = titleScreen
--	2 = fileSelector
--	3 = download
--	4 = options
local currentPlace=1;
-- Current page to be displaying
local currentPageImage;
-- Current path pages are in
local currentPath = "/NewReader/";
-- Current page being displayed;
local currentPage=1;
-- X position in imaage we're at.
local currentX=0;
-- Y position in image we're at.
local currentY=0;

--    ======================
--    PAGE SPECIFIC SETTINGS
--    (These are changed every time a page is loaded)
--    ======================
-- A variable to tell how the page is drawn.
--	0 = Normal
--  1 = Just drawn at the top left. No vertical scroll. No horizontal scroll.
--  2 = No vetical scrolling, only horizontal.
--  3 = No horiontal scrolling. Vertical only.
local currentDrawPageMode=0;
-- The current page's width
local currentPageWidth;
-- The current page's height
local currentPageHeight;

--      =======================
--      READING OPTION VARS
--      =======================
-- How many pixels the page moves per second
local moveSpeed=10;
-- Where you start on the next page.
	-- True is for the top right, false is for the top left.
local returnPlace=true;
-- The current image format for the current manga
local currentImageFormat="happu!";
-- This is the format string to be used with string.format(<this variable>, <page number>)
local currentNumberFormatString = "";
-- True if the bottom part of the page should be drawn.
local drawBottom=true;
-- Number of pixels the image moves per frame.
local moveSpeed=15;
-- Bottom bar is on?
local bottomBarEnabled=false;
-- ======================
-- GENERIC VARS
-- ======================
-- White
local colorWhite = Color.new(255,255,255);
-- Green
local colorGreen = Color.new(0,255,255);
-- Commonly reused variable for the current buttons pressed.
local pad;
-- Commonly reused variable for the buttons pressed the last frame
local oldPad;
-- Circle pad y
local circlePadY;
-- Circle pad x
local circlePadX;
-- Touch x
local tchX=0;
-- Touch y
local tchY=0;
-- Old Touch x
local oldTchX=0;
-- Old Touch y
local oldTchY=0;
-- is new 3ds?
local isNew3ds;
-- ======================
-- Options
--	Things the user can change in the options.
-- ======================

-- =======================
-- DEBUG
-- =======================
debugMode=false;
if (debugMode) then
	fpsTimer = Timer.new();
	frames=0;
	lastFps=0;
end

-- //////////////////////////////////////////////////
-- CROSS PLATFORM INCONSISTANT ARGUMENTS
-- //////////////////////////////////////////////////
if (currentPlatform == PLAT_VITA) then
	drawBottom = false;
end

-- /////////////////////////////////////////////////////////////////////////////////////
-- Real stuff incoming...
-- /////////////////////////////////////////////////////////////////////////////////////

function ShowAndWait(textToShow)
	startDraw();
	clearScreens();
	Screen.debugPrint(0, 0, textToShow, colorWhite, TOP_SCREEN)
	endDraw();
	waitForAPress();
end

-- Saves the options file with the globla options variables
function saveOptions()
	local _fileStream;
	local _optionsToWrite;

	_optionsToWrite = (moveSpeed .. "," .. tostring(returnPlace))

	_fileStream = io.open(dataFilesystemPrefix .. "/MangaReader-cfg",FCREATE);
	io.write(_fileStream,0,forceTripleDigitNumber(#_optionsToWrite), 3);
	io.write(_fileStream,3,_optionsToWrite,#_optionsToWrite);
	io.close(_fileStream);

	_fileStream=nil;
	_optionFileLength=nil;
end

-- Loads the options files and sets the vaules
function loadOptions()
	if (System.doesFileExist(dataFilesystemPrefix .. "/MangaReader-cfg")==false) then
		return;
	end

	local fileStream;
	local _optionFileLength;
	local _loadedString;
	fileStream = io.open(dataFilesystemPrefix .. "/MangaReader-cfg",FREAD);
	_optionFileLength = tonumber(io.read(fileStream,0,3));
	_loadedString = io.read(fileStream,3,_optionFileLength);
	io.close(fileStream);
	fileStream=nil;
	_optionFileLength=nil;

	local _loadedValues = {};

	--string = "cat,dog"
	_loadedValues[1], _loadedValues[2] = _loadedString:match("([^,]+),([^,]+)")

	if (_loadedValues[1]==nil) then
		System.deleteFile("/MangaReader-cfg");
	end

	moveSpeed=tonumber(_loadedValues[1]);
	if (_loadedValues[2]=="true") then
		returnPlace=true;
	else
		returnPlace=false;
	end

	_loadedValues=nil;
	_loadedString=nil;
end

-- Sets touch position variables.
function getTouch()
	oldTchX = tchX;
	oldtchy = tchY;
	tchX,tchY = Controls.readTouch()
end

-- Waits a number of miliseconds
function wait(amount)
	_tempTimer = Timer.new();
	while (Timer.getTime(_tempTimer)<=amount) do
	end
	Timer.destroy(_tempTimer);
end

-- Must run this before drawing
function startDraw()
	Screen.waitVblankStart()
	Screen.refresh()
end

-- Must run after drawing
function endDraw()
	Screen.flip()
end

-- Clears top and bottom screens.
function clearScreens()
	Screen.clear(TOP_SCREEN);
	Screen.clear(BOTTOM_SCREEN);
end

-- Sets pad to the current controls. Sets oldPad to old pad.
function getControls()
	oldPad=pad;
	pad = Controls.read();
	circlePadX, circlePadY = Controls.readCirclePad();
	if (currentPlatform == PLAT_VITA) then
		if (pad~=nil and oldPad~=nil and isJustPressed(KEY_POWER)==true) then
			if (Graphics.debugPrint == GoodDebugPrintA) then
				Graphics.debugPrint = GoodDebugPrintB
			else
				Graphics.debugPrint = GoodDebugPrintA
			end
		end
	end
end

-- Returns true if a button is pressed according to pad variable. _toCheck is button to check. Returns false otherwise.
function isPressed(_toCheck)
	if (Controls.check(pad,_toCheck)) then
		return true;
	end
	return false;
end

-- Returns true if a button was just pressed according to pad and oldPad. _toCheck is the button to check. Returns false otherwise.
function isJustPressed(_toCheck)
	if (Controls.check(oldPad,_toCheck)==false) then
		if (Controls.check(pad,_toCheck)) then
			return true;
		end
	end
	return false;
end

-- Takes the input number and returns it as a double number string
function forceDoubleDigitNumber(_num)
	if (_num<10) then
		return ("0".. _num);
	end
	return tostring(_num);
end

-- Takes the input number and returns it as a triple number string
function forceTripleDigitNumber(_num)
	if (_num<10) then
		return ("00" .. _num);
	elseif (_num<100) then
		return ("0" .. _num)
	end
	return _num;
end

-- ============================================
-- MAIN READ
-- =============================================

-- Main reading section
function mainRead()
	getControls();
	startDraw();
	clearScreens();
	getMoveInputs();
	drawPage();
	if (debugMode) then
		Screen.debugPrint(0, 0, tostring(lastFps), colorWhite, BOTTOM_SCREEN)
		Screen.debugPrint(0, 16, tostring(currentDrawPageMode), colorWhite, BOTTOM_SCREEN)
	end
	if (bottomBarEnabled) then
		drawBottomBar();
	end
	endDraw();
	getPageChangeInputs();
	if (debugMode) then
		frames=frames+1;
		if (Timer.getTime(fpsTimer)>=1000) then
			Timer.reset(fpsTimer);
			lastFps=frames;
			frames=0;
		end
	end
end

-- Handles the controls for mainRead.
function getMoveInputs()
	if (currentDrawPageMode==0 or currentDrawPageMode==2) then
		if (isPressed(KEY_DRIGHT) or circlePadX>20) then
			if (currentX+moveSpeed+400>currentPageWidth) then
				currentX=currentPageWidth-400;
			else
				currentX=currentX+moveSpeed;
			end
		elseif (isPressed(KEY_DLEFT) or circlePadX<-20) then
			if (currentX-moveSpeed<0) then
				currentX=0;
			else
				currentX=currentX-moveSpeed;
			end
		end
	end

	if (currentDrawPageMode==0 or currentDrawPageMode==3) then
		if (isPressed(KEY_DDOWN) or circlePadY<-20) then
			if (currentY+moveSpeed+240>currentPageHeight) then
				currentY=currentPageHeight-240;
			else
				currentY=currentY+moveSpeed;
			end
		elseif (isPressed(KEY_DUP) or circlePadY>20) then
			if (currentY-moveSpeed<0) then
				currentY=0;
			else
				currentY=currentY-moveSpeed;
			end
		end
	end

	if (isJustPressed(KEY_X)) then
		if (bottomBarEnabled==false) then
			bottomBarEnabled=true;
		else
			bottomBarEnabled=false;
		end
	end

end

function drawBottomBar()

	h,m,s = System.getTime()
	Screen.fillRect(0, 320, 214, 239, Color.new(0,155,50), BOTTOM_SCREEN)
	if (h>=13) then
		Screen.debugPrint(16, 224, string.format("%02d:%02d:%02d PM",h-12,m,s), colorWhite, BOTTOM_SCREEN)
	else
		Screen.debugPrint(16, 224, string.format("%02d:%02d:%02d AM",h,m,s), colorWhite, BOTTOM_SCREEN)
	end
	Screen.debugPrint(222, 224, ("Page: " .. tostring(currentPage)), colorWhite, BOTTOM_SCREEN)

end

-- Gets the inputs for L and R and startDraw
function getPageChangeInputs()
	if (isJustPressed(KEY_R) or isJustPressed(KEY_ZL)) then
		currentPage=currentPage+1;
		if (loadNextPage()==false) then
			initializeTitleScreen();
			currentPlace=1;
		end
	elseif (isJustPressed(KEY_L) or isJustPressed(KEY_ZR)) then
		currentPage=currentPage-1;
		if (loadNextPage()==false) then
			initializeTitleScreen();
			currentPlace=1;
		end
	end
	if (isJustPressed(KEY_START)) then
		destroyMainRead();
		initializeTitleScreen();
		currentPlace=1;
	end
end

function destroyMainRead()

	if (currentPageImage~=nil) then
	Screen.freeImage(currentPageImage)
	currentPageImage=nil;
	end

	fileStream = io.open(currentPath .. "_lastPage",FCREATE);
	io.write(fileStream,0,tostring(forceTripleDigitNumber(currentPage)), 3)
	io.close(fileStream);
	fileStream=nil;
end

-- Draws the currently loaded page
function drawPage()

	if (currentDrawPageMode==0) then
		Screen.drawPartialImage(0,0,currentX,currentY,400,240,currentPageImage,TOP_SCREEN)
	elseif (currentDrawPageMode==1) then
		Screen.drawImage(0,0,currentPageImage,TOP_SCREEN)
	elseif (currentDrawPageMode==2) then
		Screen.drawPartialImage(0,0,currentX,0,400,currentPageHeight,currentPageImage,TOP_SCREEN)
	else
		Screen.drawPartialImage(0,0,0,currentY,currentPageWidth,240,currentPageImage,TOP_SCREEN)
	end

	if (drawBottom) then
		-- 38 is bottom screen offset. Hence x+38.
		if (currentDrawPageMode==0 or currentPageMode==3) then
			if (currentPageHeight-(currentY+240))<240 then
				Screen.drawPartialImage(0,0,currentX+38,currentY+240,320,(currentPageHeight-(currentY+240)),currentPageImage,BOTTOM_SCREEN)
			else
				Screen.drawPartialImage(0,0,currentX+38,currentY+240,320,240,currentPageImage,BOTTOM_SCREEN)
			end
		end
	end
end

-- Sets the page specific settigs such as draw mode.
function getCurrentPageSpecificSettings()
	currentPageWidth = Screen.getImageWidth(currentPageImage);
	currentPageHeight = Screen.getImageHeight(currentPageImage);

	if currentPageWidth>399 then
		currentDrawPageMode=2;
	elseif currentPageHeight>239 then
		currentDrawPageMode=3;
	else
		currentDrawPageMode=1;
	end

	if (currentPageHeight>239 and currentPageWidth>399) then
		currentDrawPageMode=0;
	end

	-- Return to top.
	if (returnPlace==false) then
		currentX=0;
	else
		if (currentDrawPageMode==0 or currentDrawPageMode==2) then
			currentX=currentPageWidth-400;
		end
	end
	currentY=0;
end

-- loads the next page with the current settings
--	Returns false if it failed. Returns nil otherwise.
function loadNextPage()
	-- Frees the last image if it needs to so memory can be freed.
	if (currentPageImage~=nil) then
		Screen.freeImage(currentPageImage)
		currentPageImage=nil;
	end
	-- Checks if the next page exists.
	if ((System.doesFileExist(currentPath .. string.format(currentNumberFormatString, currentPage) .. currentImageFormat)==false)) then
		System.deleteFile(currentPath .. "_lastPage");
		return false;
	end
	-- Finally, loads the next page
	currentPageImage = Screen.loadImage(currentPath .. string.format(currentNumberFormatString, currentPage) .. currentImageFormat);
	getCurrentPageSpecificSettings();
end

-- Finds out number of zeros and format
-- Sets currentNumberFormatString and currentImageFormat to what it finds.
-- Returns false if it failed.
function detectMangaSettings()
	local _result;
	_result = detectZeros(currentPath,".jpg");
	if (_result~=false) then
		currentNumberFormatString=_result;
		currentImageFormat=".jpg";
		return true;
	end
	_result = detectZeros(currentPath,".png");
	if (_result~=false) then
		currentNumberFormatString=_result;
		currentImageFormat=".png";
		return true;
	end
	_result = detectZeros(currentPath,".bmp");
	if (_result~=false) then
		currentNumberFormatString=_result;
		currentImageFormat=".bmp";
		return true;
	end
	-- Nothing found, return false.
	return false;
end

-- Detects number of zeros using specified file format and path.
-- Returns the format string for the number
function detectZeros(_path, _format)
	local _workPrefix="";
	for i=1,8 do
		if (System.doesFileExist(_path .. _workPrefix .. "1" .. _format)) then
			return "%0" .. i .. "d";
		end
		_workPrefix = ("0" .. _workPrefix);
	end
	return false;
end

function gotoRead()
	if System.doesFileExist(currentPath .. "_lastPage") then
		fileStream = io.open(currentPath .. "_lastPage",FREAD);
		currentPage = tonumber(io.read(fileStream,0,3));
		io.close(fileStream);
		fileStream=nil;
	else
		currentPage=1;
	end
	currentPlace=0;
	loadNextPage();
end

-- =============================================
-- FILE SELECTOR
-- =============================================
local currentDirectory=(dataFilesystemPrefix .. "/Manga/");
local currentFileList;
local currentFileSelectorSelected;
local currentFileSelectorOffset;

local sort_func = function( a,b ) return a.name < b.name end

-- Sorts currentFileList in alphabetical order
function sortCurrentFileList()
	table.sort( currentFileList, sort_func )
end

function initializeFileSelector()
	currentFileSelectorSelected=1;
	currentFileSelectorOffset=0;
	refreshDirectory();
end

-- In goes selection, returns offset. It took me about an hour to figure this out. :(
function getOffsetForSelection(_currentSelection)
	if (_currentSelection<=14) then
		return 0;
	end
	return (_currentSelection-14);
end

-- Removes a thingie from a file list
-- _toRemove is thingie to remove
-- _list is da list
function removeFromFileList(_toRemove,_list)
	for i=1,#_list do
		if (_list[i].name==_toRemove) then
			table.remove(_list,i);
			break;
		end
	end
end

-- Sets currentFileList to the list in currentDirectiry
function refreshDirectory()
	currentFileList=System.listDirectory(currentDirectory);
	sortCurrentFileList();

	if (System.doesFileExist(currentDirectory .. "_lastChapter")==true) then
		-- Remove _lastChapter from list
		removeFromFileList("_lastChapter",currentFileList)

		local _chapterStringLength;
		local _lastChapterString;
		if (currentPlatform == PLAT_3DS) then
			fileStream = io.open(currentDirectory .. "_lastChapter",FCREATE); -- Why I was using FCREATE for this, I'll never know.
		else
			fileStream = io.open(currentDirectory .. "_lastChapter",FREAD);
		end
		_chapterStringLength = tonumber(io.read(fileStream,0,2));
		_lastChapterString = io.read(fileStream,2,_chapterStringLength);
		io.close(fileStream);
		fileStream=nil;
		for i=1,#currentFileList do
			if (currentFileList[i].name==_lastChapterString) then
				currentFileSelectorSelected=i;
				currentFileSelectorOffset=getOffsetForSelection(currentFileSelectorSelected);
				break;
			end
		end
	end

end

-- For the file selector, draws the list of files in currentFileList vairable.
function drawList()
	if (#currentFileList==0) then
		Screen.debugPrint(0, 16, "Empty directory", colorWhite, TOP_SCREEN)
		return;
	end
	for i=1,14 do
		Screen.debugPrint(0, i*16, currentFileList[i+currentFileSelectorOffset].name, colorWhite, TOP_SCREEN)
		if (#currentFileList==i) then
			break;
		end
	end
	Screen.debugPrint(0, (currentFileSelectorSelected-currentFileSelectorOffset)*16, currentFileList[currentFileSelectorSelected].name, Color.new(0,255,111), TOP_SCREEN)
end

-- Controls for the file selector
function fileSelectorControls()

	local pushedDown;
	local pushedUp;
	if (isPressed(KEY_L)) then
		if (isPressed(KEY_DDOWN)) then
			pushedDown=true;
		elseif (isPressed(KEY_DUP)) then
			pushedUp=true;
		end
	else
		if (isJustPressed(KEY_DDOWN)) then
			pushedDown=true;
		elseif (isJustPressed(KEY_DUP)) then
			pushedUp=true;
		end
	end

	if (pushedDown) then
		if (currentFileSelectorSelected==#currentFileList) then
			currentFileSelectorSelected=1;
			currentFileSelectorOffset=0;
		else
			if (currentFileSelectorSelected-currentFileSelectorOffset==14) then
				currentFileSelectorOffset=currentFileSelectorOffset+1;
			end
			currentFileSelectorSelected=currentFileSelectorSelected+1;
		end
	elseif (pushedUp) then
		if (currentFileSelectorSelected==1) then
			currentFileSelectorSelected=#currentFileList;
			if (#currentFileList>14) then
			currentFileSelectorOffset = #currentFileList-14;
			end
		else
			if (currentFileSelectorSelected-currentFileSelectorOffset==1) then
				currentFileSelectorOffset=currentFileSelectorOffset-1;
			end
			currentFileSelectorSelected=currentFileSelectorSelected-1;
		end
	end

	if (isJustPressed(KEY_A)) then
		currentPath=(currentDirectory .. currentFileList[currentFileSelectorSelected].name .. "/");
		if (detectMangaSettings()~=false) then
			createLastChapterFile(currentDirectory,currentFileList[currentFileSelectorSelected].name);
			destroyFileSelector();
			gotoRead();
		else
			currentDirectory=(currentDirectory .. currentFileList[currentFileSelectorSelected].name .. "/");
			currentFileSelectorSelected=1;
			currentFileSelectorOffset=0;
			refreshDirectory();
		end
	elseif (isJustPressed(KEY_B)) then
		for i=2, string.len(currentDirectory) do
			if string.sub(currentDirectory,i*-1,i*-1)=="/" then
				currentDirectory = string.sub(currentDirectory, 0, (i*-1))
				break
			end
		end
		currentFileSelectorSelected=1;
		currentFileSelectorOffset=0;
		refreshDirectory();
	end

	if (isJustPressed(KEY_START)) then
		destroyFileSelector();
		initializeTitleScreen();
		currentPlace=1;
	end

end

-- The file selector's main lop.
function fileSelector()
	startDraw();
	clearScreens();
	drawList();
	Screen.debugPrint(0, 0, currentDirectory, colorWhite, BOTTOM_SCREEN)
	Screen.debugPrint(0, 32, "Press A to open selected directory", colorWhite, BOTTOM_SCREEN)
	Screen.debugPrint(0, 48, "Press B to go back a directory", colorWhite, BOTTOM_SCREEN)
	Screen.debugPrint(0, 64, "Press START to exit to title", colorWhite, BOTTOM_SCREEN)
	Screen.debugPrint(0, 80, "Hold L for really fast movement", colorWhite, BOTTOM_SCREEN)
	endDraw();
	getControls();
	fileSelectorControls();
end

function destroyFileSelector()
	currentFileSelectorSelected=nil;
	currentFileSelectorOffset=nil;
	currentFileList=nil;
end

-- Creates the last chapter file.
-- _createLocation is usually currentDirectory and _folderName is the name of the folder that the lastchapter file will contain.
function createLastChapterFile(_createLocation, _folderName)
	fileStream = io.open(_createLocation .. "_lastChapter",FCREATE);
	io.write(fileStream,0,forceDoubleDigitNumber(#_folderName), 2);
	io.write(fileStream,2,_folderName, #_folderName);
	io.close(fileStream);
	fileStream=nil;
end

-- =============================================
-- TITLE SCREEN
-- =============================================
local titleScreenSelected;

function initializeTitleScreen()
	titleScreenSelected=0;
end

function titleScreenControls()
	if (isJustPressed(KEY_DDOWN)) then
		if (titleScreenSelected~=3) then
			titleScreenSelected=titleScreenSelected+1;
		else
			titleScreenSelected=0;
		end
	elseif (isJustPressed(KEY_DUP)) then
		if (titleScreenSelected~=0) then
			titleScreenSelected=titleScreenSelected-1;
		else
			titleScreenSelected=3;
		end
	elseif (isJustPressed(KEY_A)) then
		if (titleScreenSelected==0) then
			destroyTitleScreen();