# Lua Scripting Guide

Mosis apps use Lua for scripting and interactivity. Each app runs in an isolated sandbox with access to Mosis-specific APIs.

## Getting Started

Embed Lua directly in your RML files:

```xml
<body>
    <button onclick="sayHello()">Click Me</button>

    <script>
        function sayHello()
            print("Hello from Lua!")
        end
    </script>
</body>
```

Or use external files:

```xml
<head>
    <script src="scripts/app.lua"/>
</head>
```

## Lua Basics

If you're new to Lua, here's a quick primer:

### Variables

```lua
-- Local variables (preferred)
local name = "Mosis"
local count = 42
local enabled = true
local items = {"apple", "banana", "cherry"}

-- Global variables (avoid when possible)
globalVar = "accessible everywhere"
```

### Functions

```lua
-- Basic function
function greet(name)
    return "Hello, " .. name .. "!"
end

-- Function with multiple returns
function getPosition()
    return 100, 200
end

local x, y = getPosition()

-- Anonymous functions
local double = function(n) return n * 2 end
```

### Control Flow

```lua
-- If statements
if score > 100 then
    print("High score!")
elseif score > 50 then
    print("Good job!")
else
    print("Keep trying!")
end

-- Loops
for i = 1, 10 do
    print(i)
end

for index, value in ipairs(items) do
    print(index, value)
end

while condition do
    -- loop body
end
```

### Tables

```lua
-- Array-like table
local colors = {"red", "green", "blue"}
print(colors[1])  -- "red" (Lua is 1-indexed)

-- Dictionary-like table
local user = {
    name = "Alice",
    age = 25,
    premium = true
}
print(user.name)
print(user["age"])

-- Mixed table
local app = {
    name = "MyApp",
    version = "1.0",
    features = {"dark mode", "notifications"}
}
```

## DOM Manipulation

Access and modify UI elements using the `document` object:

### Getting Elements

```lua
-- By ID
local button = document:GetElementById("my-button")

-- By tag name
local paragraphs = document:GetElementsByTagName("p")

-- By class name
local cards = document:GetElementsByClassName("card")
```

### Modifying Content

```lua
local element = document:GetElementById("message")

-- Set inner content (HTML-like)
element.inner_rml = "<strong>Hello!</strong>"

-- Get inner content
local content = element.inner_rml

-- Set text only (safer, no HTML parsing)
element:SetInnerRML("Plain text here")
```

### Modifying Attributes

```lua
local input = document:GetElementById("username")

-- Get attribute
local value = input:GetAttribute("value")

-- Set attribute
input:SetAttribute("placeholder", "Enter username")

-- Remove attribute
input:RemoveAttribute("disabled")
```

### Modifying Styles

```lua
local box = document:GetElementById("box")

-- Set individual properties
box.style.width = "200dp"
box.style.backgroundColor = "#00d4ff"
box.style.display = "none"  -- hide element

-- Read properties
local width = box.style.width
```

### Classes

```lua
local element = document:GetElementById("panel")

-- Add class
element:SetClass("active", true)

-- Remove class
element:SetClass("active", false)

-- Check class
if element:IsClassSet("active") then
    print("Panel is active")
end
```

## Event Handling

### Inline Events

```xml
<button onclick="handleClick()">Click</button>
<input onchange="handleChange(event)"/>
<div onmouseover="handleHover()"/>
```

### Event Listeners

```lua
local button = document:GetElementById("my-button")

-- Add listener
button:AddEventListener("click", function(event)
    print("Button clicked!")
end)

-- Remove listener (need reference)
local handler = function(event)
    print("Clicked")
end
button:AddEventListener("click", handler)
button:RemoveEventListener("click", handler)
```

### Event Object

```lua
function handleEvent(event)
    -- Event type
    print(event.type)  -- "click", "change", etc.

    -- Target element
    local target = event:GetCurrentElement()

    -- Mouse position (for mouse events)
    local x = event.parameters.mouse_x
    local y = event.parameters.mouse_y

    -- Stop propagation
    event:StopPropagation()
end
```

### Common Events

| Event | Description |
|-------|-------------|
| `click` | Element clicked |
| `dblclick` | Element double-clicked |
| `mousedown` | Mouse button pressed |
| `mouseup` | Mouse button released |
| `mouseover` | Mouse enters element |
| `mouseout` | Mouse leaves element |
| `focus` | Element gains focus |
| `blur` | Element loses focus |
| `change` | Input value changed |
| `submit` | Form submitted |
| `keydown` | Key pressed |
| `keyup` | Key released |

## Timers

### setTimeout

```lua
-- Execute once after delay
local timerId = setTimeout(function()
    print("Executed after 1 second")
end, 1000)  -- milliseconds

-- Cancel timer
clearTimeout(timerId)
```

### setInterval

```lua
-- Execute repeatedly
local intervalId = setInterval(function()
    print("Tick")
end, 1000)

-- Cancel interval
clearInterval(intervalId)
```

## Storage

Persist data between app sessions:

```lua
-- Save data
storage.set("username", "Alice")
storage.set("settings", {
    darkMode = true,
    notifications = false
})

-- Load data
local username = storage.get("username")
local settings = storage.get("settings")

-- Delete data
storage.remove("username")

-- Clear all data
storage.clear()
```

## Navigation

Navigate between screens in your app:

```lua
-- Navigate to screen
navigateTo("settings")  -- loads assets/settings.rml

-- Go back
goBack()

-- Go to home screen
goHome()

-- Replace current screen (no back)
replaceTo("login")
```

### Navigation Events

```lua
-- Listen for navigation
onNavigate(function(screenName)
    print("Navigated to: " .. screenName)
end)

-- Listen for back
onBack(function()
    print("Going back")
end)
```

## HTTP Requests

Make network requests (requires `network` permission):

```lua
-- GET request
http.get("https://api.example.com/data", function(response)
    if response.ok then
        local data = json.decode(response.body)
        print(data.message)
    else
        print("Error: " .. response.status)
    end
end)

-- POST request
http.post("https://api.example.com/submit", {
    headers = {
        ["Content-Type"] = "application/json"
    },
    body = json.encode({
        name = "Alice",
        action = "subscribe"
    })
}, function(response)
    print("Status: " .. response.status)
end)
```

## JSON

```lua
-- Parse JSON string
local data = json.decode('{"name": "Alice", "age": 25}')
print(data.name)

-- Convert to JSON string
local str = json.encode({
    items = {"a", "b", "c"},
    count = 3
})
```

## Date and Time

```lua
-- Current timestamp
local now = os.time()

-- Format date
local formatted = os.date("%Y-%m-%d %H:%M:%S", now)

-- Parse date components
local t = os.date("*t", now)
print(t.year, t.month, t.day, t.hour, t.min, t.sec)
```

## Utilities

### String Functions

```lua
-- Concatenation
local greeting = "Hello, " .. name .. "!"

-- String functions
string.upper("hello")  -- "HELLO"
string.lower("HELLO")  -- "hello"
string.sub("hello", 1, 3)  -- "hel"
string.find("hello", "ll")  -- 3
string.gsub("hello", "l", "L")  -- "heLLo"
string.format("Score: %d", 100)  -- "Score: 100"
```

### Math Functions

```lua
math.floor(3.7)  -- 3
math.ceil(3.2)   -- 4
math.round(3.5)  -- 4
math.abs(-5)     -- 5
math.min(1, 2, 3)  -- 1
math.max(1, 2, 3)  -- 3
math.random()      -- 0-1
math.random(1, 6)  -- 1-6
```

### Table Functions

```lua
-- Insert
table.insert(items, "new item")
table.insert(items, 1, "at beginning")

-- Remove
table.remove(items)  -- remove last
table.remove(items, 1)  -- remove first

-- Sort
table.sort(items)
table.sort(items, function(a, b) return a > b end)  -- descending

-- Length
local count = #items
```

## Sandbox Restrictions

For security, these are **NOT** available:

- `os.execute`, `io.popen` - No shell commands
- `loadfile`, `dofile` - No arbitrary file loading
- `require` - No external modules (use `import` for app modules)
- `debug` library - No debugging hooks
- `rawget`, `rawset` - No metatable bypass

## Best Practices

1. **Use local variables** - Faster and prevents pollution
2. **Handle errors** - Use `pcall` for operations that might fail
3. **Clean up timers** - Clear intervals when navigating away
4. **Minimize DOM queries** - Cache element references
5. **Batch updates** - Group style changes together

### Error Handling

```lua
local success, result = pcall(function()
    -- Code that might fail
    local data = json.decode(invalidJson)
    return data
end)

if success then
    print("Parsed:", result)
else
    print("Error:", result)
end
```

### Module Pattern

```lua
-- utils.lua
local Utils = {}

function Utils.formatCurrency(amount)
    return string.format("$%.2f", amount)
end

function Utils.capitalize(str)
    return str:sub(1,1):upper() .. str:sub(2)
end

return Utils
```

```lua
-- main.lua
local Utils = import("utils")

print(Utils.formatCurrency(19.99))
```

## Next Steps

- [Permissions Guide](permissions.md) - Request device capabilities
- [API Reference](../api/lua-api.md) - Complete API documentation
- [Debugging Guide](debugging.md) - Debug your Lua code
