-----------------------------------------------------------------------------
-- Name:        catch.wx.lua
-- Purpose:     'Catch' wxLua sample
--              Simple game using either the keyboard or one or two joysticks.

-- Author:      A. Arpin
-- Modified by:
-- Created:     August 6, 2009    
-- Licence:     wxWidgets licence
-----------------------------------------------------------------------------
-- Thanks to John Labenski for his suggestions
------------------------------------------------------------------------------
version = 1.0
package.cpath = package.cpath..";./?.dll;./?.so;../lib/?.so;../lib/vc_dll/?.dll;../lib/bcc_dll/?.dll;../lib/mingw_dll/?.dll;"

require("wx")

assert(wx.wxJoystick, "Sorry, this app cannot run since wxJoystick is not supported on this platform.")

function iff(cond, a, b) if cond then return a else return b end end

-- Generate a unique new wxWindowID
local ids = {wx.wxID_HIGHEST + 1}
function ids:NewID() ids[1] = ids[1] + 1 return ids[1] end

math.randomseed(os.time())
local lastElapsed = -1
local minCellWidth = 16
local cellWidth = 18
local cellHeight = 18
local VerWall, HorWall, offScreenBitmap = nil, nil, nil
local levelMenu

local wallThickness = 5

local joyStick1 = wx.wxJoystick(wx.wxJOYSTICK1)
local joyStick2 = wx.wxJoystick(wx.wxJOYSTICK2)

local redrawRequired = true
local frame          = nil   -- the main wxFrame
local panel          = nil   -- the child wxPanel of the wxScrolledWindow to draw on
local timer          = nil
local initialTimer   = 50
local timeTimer      = initialTimer

ids.ID_EASY    = ids:NewID()
ids.ID_HARD    = ids:NewID()
ids.ID_HARDER  = ids:NewID()
ids.ID_HARDEST = ids:NewID()

ids.ID_JOY_ONE_PLAYER  = ids:NewID()
ids.ID_JOY_TWO_PLAYERS = ids:NewID()
ids.ID_JOY_MV_PERSON   = ids:NewID()
ids.ID_JOY_MV_PRESENT  = ids:NewID()

local numberOfPlayers  = iff(wx.wxJoystick.GetNumberJoysticks() > 1, 2, 1)
local joystick1Control = ids.ID_JOY_MV_PERSON
local Present, Person
local bitmapPost

-- These sounds are from MS Windows, however wxSound doesn't assert in Linux so we
-- create a tempory wxLogNull to ignore the warnings and use wxSound:IsOk() before
-- we try to use them
local logNo = wx.wxLogNull();
local soundTata    = wx.wxSound(wx.wxGetOSDirectory()..[[\media\tada.wav]])
local soundRecycle = wx.wxSound(wx.wxGetOSDirectory()..[[\media\recycle.wav]])
logNo:delete();

--~-- This code was added for window 7 without it the joysticks do not work after the first sound is played
if soundTata:IsOk() then soundTata:Play() soundTata:Stop() 
elseif soundRecycle:IsOk() then soundRecycle:Play() soundRecycle:Stop() end

function distance(m)
    local rd, cd = Present:Offet(m[1], m[2])
    if rd then
        return ((Person.r - rd)^2 + (Person.c - cd)^2)^0.5
    end
    return 0
end

local Game =
{
    width  = 30;
    height = 20;
    hw = {}, -- horizontal walls
    vw = {}, -- vertical walls
    post = {},
    randomPosition = function () return math.random(2, self.height), math.random(1,self.width) end,
    setLevel = function (self, level, menu)
        timeTimer = initialTimer
        self.levelId = level
        if level == ids.ID_EASY then
            self.level          = 'Easy'
            self.numberOfWalls  = 25
            self.numberOfBlocks = 10
            self.maxExtraMove   = 1
            self.maxHoleMoved   = 1
            self.maxWallMoved   = 5
            self.maxTimer       = 500
        elseif level == ids.ID_HARD then
            self.level          = 'Hard'
            self.numberOfWalls  = 35
            self.numberOfBlocks = 15
            self.maxExtraMove   = 2
            self.maxHoleMoved   = 3
            self.maxWallMoved   = 10
            self.maxTimer       = 400
        elseif level == ids.ID_HARDER then
            self.level          = 'Harder'
            self.numberOfWalls  = 100
            self.numberOfBlocks = 20
            self.maxExtraMove   = 2
            self.maxHoleMoved   = 5
            self.maxWallMoved   = 15
            self.maxTimer       = 300
        else
            level = ids.ID_HARDER
            self.level          = 'Hardest !!!'
            self.numberOfWalls  = 200
            self.numberOfBlocks = 40
            self.maxExtraMove   = 2
            self.maxHoleMoved   = 6
            self.maxWallMoved   = 20
            self.maxTimer       = 100
        end
        if levelMenu then levelMenu:Check(level, true) end
    end
}

Game:setLevel(ids.ID_EASY)
local Location = {r=0, c=0, minR = 1, minC = 1, maxR = Game.height, maxC = Game.width}

function Location:new (o)
    local o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end

function Location:Position()
    return Position(self.r, self.c)
end

function Location:Offet(dr, dc)
    local nr, nc = self.r + dr, self.c + dc
    if  (nr >= self.minR and nr<= self.maxR and nc >= self.minC and nc <= self.maxC)  then
        return nr, nc
    end
end

function Location:Place(r,c)
    assert(r >= self.minR and r<= self.maxR and c >= self.minC and c <= self.maxC)
    self.r, self.c = r, c
end

local Occupant = Location:new() -- location for a bitmap

-- vertical or horizontal wall
function GenWall(w, h)
    local bm = wx.wxBitmap(w, h)
    local memDC = wx.wxMemoryDC()
    local pen = wx.wxPen(wx.wxColour("red"), 0, wx.wxSOLID)
    local brush = wx.wxBrush(wx.wxColour('green'), wx.wxSOLID)
    
    memDC:SelectObject(bm)
    memDC:SetBrush(brush)
    memDC:SetPen(pen)
    memDC:DrawRectangle(0, 0, w, h)
    
    pen:delete()
    brush:delete()
    memDC:SelectObject(wx.wxNullBitmap)
    memDC:delete()
    return bm
end

function PositionWall(r, c)
    return (c-1)*(cellWidth+wallThickness) ,  (r-1)*(cellHeight+wallThickness)
end

function Game:initBitMap()
    local w, h =  Game.width*(cellWidth+wallThickness)+1, Game.height*(cellHeight+wallThickness)+1       
    return wx.wxBitmap(w, h)
end

function Game:Place(b, r, c)
    if self[r][c] then return end
    b:Place(r,c)
    self[r][c] = b
    return true
end

function Game:Remove(b)
    if b.r ~= 0 and self[b.r][b.c] == b then
        self[b.r][b.c]=nil
    end
end

function Location:Draw(dc)
    dc:DrawBitmap(bitmap, wallThickness+x, wallThickness+y, true)
end

function Game:RandomRC()
    return math.random(1, self.height), math.random(1,self.width)
end

function Game:RandomWall(h)
    if h then
        return math.random(1, self.height - 1), math.random(1, self.width)
    else
        return math.random(1, self.height), math.random(1, self.width - 1)
    end
end

function Game:RandomIncr()
    -- note: 0, 0 is possible
    return math.random(-1, 1), math.random(-1, 1)
end

function Game:Init()
    VerWall = GenWall(wallThickness, cellHeight + wallThickness)
    HorWall = GenWall(cellWidth + wallThickness, wallThickness)
    offScreenBitmap = Game:initBitMap()
    frame:SetClientSize(wx.wxSize(offScreenBitmap.Width, offScreenBitmap.Height))
    local r, c
    for r = 1, self.height   do self[r]    = {} end
    for r = 1, self.height-1 do self.hw[r] = {} end
    for c = 1, self.width-1  do self.vw[c] = {} end
    repeat
        for _, v in ipairs(({Present, Person}))  do
            self:Remove(v)
            repeat until self:Place(v, self:RandomRC())
        end
    until distance({0,0}) > 20
    self.post = {}
    for i= 1, 10 + self.maxHoleMoved  do
        local post = Occupant:new({bitmap = bitmapPost})
        repeat until self:Place(post, self:RandomRC())
        table.insert(self.post, post)
    end
    for i = 1, self.numberOfWalls  do
        repeat r, c = self:RandomWall(false) until self.vw[c][r] == nil
        self.vw[c][r]=true
        repeat r, c = self:RandomWall(true) until self.hw[r][c]== nil
        self.hw[r][c]=true
    end
    self.start = os.time()
    RestartTimer()
    frame:Refresh()
    redrawRequired = true
    timeTimer = initialTimer
end

function main()
    bitmapPost = wx.wxBitmap(xpm('wall'))
    Person = Occupant:new({bitmap = wx.wxBitmap(xpm('Person'))})
    Present = Occupant:new({bitmap = wx.wxBitmap(xpm('Present'))})       
    frame = wx.wxFrame( wx.NULL, wx.wxID_ANY, "wxLua Catch the present", 
                       wx.wxDefaultPosition, wx.wxDefaultSize,
                       wx.wxCAPTION+wx.wxSYSTEM_MENU+wx.wxCLOSE_BOX+wx.wxMINIMIZE_BOX)
    -- -----------------------------------------------------------------------
    -- Create the menubar
    local fileMenu = wx.wxMenu()
    fileMenu:Append(wx.wxID_EXIT,   "E&xit\tCtrl+Q",      "Quit the program")

    local helpMenu = wx.wxMenu()
    helpMenu:Append(wx.wxID_ABOUT,  "&About...", "About the wxLua Catch Application")

    levelMenu = wx.wxMenu()
    levelMenu:AppendRadioItem(ids.ID_EASY, "&1 Easy", "Level the game is being played")
    levelMenu:AppendRadioItem(ids.ID_HARD, "&2 Hard", "Level the game is being played")
    levelMenu:AppendRadioItem(ids.ID_HARDER, "&3 Harder", "Level the game is being played")
    levelMenu:AppendRadioItem(ids.ID_HARDEST, "&4 Hardest", "Level the game is being played")
    local joystickControlMenu = wx.wxMenu()
    joystickControlMenu:AppendRadioItem(ids.ID_JOY_ONE_PLAYER, "&1 One player", "One player present moves by itself")
    joystickControlMenu:AppendRadioItem(ids.ID_JOY_TWO_PLAYERS, "&2 Two players", "Two players")
    joystickControlMenu:AppendSeparator()
    joystickControlMenu:AppendRadioItem(ids.ID_JOY_MV_PERSON, "&3 Joystick:1 move Person", "Joystick with id set to 1")
    joystickControlMenu:AppendRadioItem(ids.ID_JOY_MV_PRESENT, "&4 JoyStick:1 move Present", "Joystick with id set to 1")

    local menuBar = wx.wxMenuBar()
    menuBar:Append(fileMenu, "&File")
    menuBar:Append(levelMenu, "&Level")
    menuBar:Append(joystickControlMenu, '&Joystick ('.. wx.wxJoystick.GetNumberJoysticks() ..')')
    menuBar:Append(helpMenu, "&Help")
    frame:SetMenuBar(menuBar)
    if (wx.wxJoystick.GetNumberJoysticks() == 0) then menuBar:EnableTop(2, false) end
    -- Create the statusbar
    local statusBar = frame:CreateStatusBar(1)
    frame:SetStatusText("Welcome to Catch.")

    panel = wx.wxPanel(frame, wx.wxID_ANY, wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxWANTS_CHARS)
    timer = wx.wxTimer(panel)       
    panel:Connect(wx.wxEVT_PAINT, OnPaint)
    panel:Connect(wx.wxEVT_TIMER, OnTimer)
    panel:Connect(wx.wxEVT_ERASE_BACKGROUND, function(event)
            --// intentionally empty
        end)

    panel:Connect(wx.wxEVT_KEY_DOWN, OnKeyDown)
    panel:Connect(wx.wxEVT_JOY_MOVE,  OnJoyStick)

    if joyStick1:IsOk() then joyStick1:SetCapture(panel, 100) end
    if joyStick2:IsOk() then joyStick2:SetCapture(panel, 100) end

    Game:Init()

    frame:Connect(wx.wxEVT_CLOSE_WINDOW,
        function (event)
            -- prevent paint events using the memDC during closing
            if joyStick1:IsOk() then joyStick1:ReleaseCapture() end
            if joyStick2:IsOk() then joyStick2:ReleaseCapture() end
            if offScreenBitmap then
                offScreenBitmap:delete()
                offScreenBitmap = nil
            end
            soundTata:delete()
            soundRecycle:delete()
            if timer then
                timer:Stop() -- always stop before exiting or deleting it
                timer:delete()
                timer = nil
            end
            -- ensure the event is skipped to allow the frame to close
            event:Skip()
        end )

    -- -----------------------------------------------------------------------
    -- File menu events

    frame:Connect(wx.wxID_EXIT, wx.wxEVT_COMMAND_MENU_SELECTED,
        function (event)
            frame:Close(true)
        end )

    -- -----------------------------------------------------------------------
    -- Game type menu
    frame:Connect(ids.ID_JOY_ONE_PLAYER, wx.wxEVT_COMMAND_MENU_SELECTED,
        function (event)
            numberOfPlayers = 1
            joystickControlMenu:Check(ids.ID_JOY_MV_PERSON, true)
            joystick1Control = ids.ID_JOY_MV_PERSON            
        end)
    frame:Connect(ids.ID_JOY_TWO_PLAYERS, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) numberOfPlayers = 2 end)
    frame:Connect(ids.ID_JOY_MV_PERSON, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) joystick1Control = event.Id end)
    frame:Connect(ids.ID_JOY_MV_PRESENT, wx.wxEVT_COMMAND_MENU_SELECTED,
        function (event)
        joystick1Control = event.Id
        joystickControlMenu:Check(ids.ID_JOY_TWO_PLAYERS, true)
        numberOfPlayers = 2 -- must have 2 players
        end)

    -- -----------------------------------------------------------------------
    -- Level menu events
    frame:Connect(ids.ID_EASY, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) Game:setLevel(event.Id) Game:Init() end)
    frame:Connect(ids.ID_HARD, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) Game:setLevel(event.Id) Game:Init() end)
    frame:Connect(ids.ID_HARDER, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) Game:setLevel(event.Id) Game:Init() end)
    frame:Connect(ids.ID_HARDEST, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) Game:setLevel(event.Id) Game:Init() end)

    -- -----------------------------------------------------------------------
    -- Help menu events

    frame:Connect(wx.wxID_ABOUT, wx.wxEVT_COMMAND_MENU_SELECTED,
        function (event)
            wx.wxMessageBox('"About" dialog for the Catch wxLua Sample.\n\n'..
                "How long will it take you to catch the present\n"..
                'Use either the arrow keys and/or a JoyStick(s)\n'..
                'Do not drop into the holes!\n'..
                'Walls move around and sometime disappear\n\n'..
                "JOYSTICK: (not necessary)\nOne or 2 joysticks are supported\n\n"..
                wxlua.wxLUA_VERSION_STRING.." built with "..wx.wxVERSION_STRING,
                "About wxLua Catch v "..version,
                wx.wxOK + wx.wxICON_INFORMATION,
                frame )
        end )
        
    frame:Center()
    frame:Show(true)    
    
    if (not joyStick1:IsOk() and joyStick2:IsOk()) then
        wx.wxMessageBox('Only one joystick and joystick id is set to 2\n change id to one or program will not work','Catch')
    end
end

function Game:Draw(dc)
    local function PositionPieces(r, c)
        return (c-1)*(cellWidth+wallThickness) + (cellWidth - minCellWidth)/2,
        (r-1)*(cellHeight+wallThickness)  + (cellHeight - minCellWidth)/2
    end
    for r,v in ipairs(self)  do
        for c, bitmap in pairs(v)  do
            local x,y = PositionPieces(r,c)
            dc:DrawBitmap(bitmap.bitmap, x+wallThickness, y+wallThickness, true)
        end
    end

    for r,v in ipairs(self.hw)  do
        for c,v in pairs(v)  do
            local x, y = PositionWall(r + 1, c)
            dc:DrawBitmap(HorWall, x+1, y, false)
        end
    end

    for c,v in ipairs(self.vw)  do
        for r,v in pairs(v)  do
            local x, y = PositionWall(r, c + 1)
            dc:DrawBitmap(VerWall, x, y+1, false)
        end
    end
end

function Game:DrawOffScreenBitMap(bmp)
    local dc = wx.wxMemoryDC()       -- create off screen dc to draw on
    dc:SelectObject(bmp)             -- select our bitmap to draw into
    local brush = wx.wxBrush(wx.wxColour('turquoise'), wx.wxSOLID)
    dc:SetBackground(brush)
    dc:Clear()
    self:Draw(dc)
    dc:SelectObject(wx.wxNullBitmap) -- always release bitmap
    dc:delete()    -- ALWAYS delete() any wxDCs created when done
    brush:delete() -- ALWAYS delete() all GDI objects
end

function OnPaint(event)
    -- ALWAYS create wxPaintDC in wxEVT_PAINT handler, even if unused
    local dc = wx.wxPaintDC(panel)
    if offScreenBitmap and offScreenBitmap:Ok() then
        if redrawRequired then
            Game:DrawOffScreenBitMap(offScreenBitmap)
            redrawRequired = false
        end
        dc:DrawBitmap(offScreenBitmap, 0, 0, false)
    end
    dc:delete() -- ALWAYS delete() any wxDCs created when done
end

function Occupant:move(d_r, d_c)
    local r, c = self:Offet(d_r, d_c)
    if c then
        -- check if a gate is crossed, allow crossing corners (present does that on automatic)
        if d_r == 0 or d_c==0 then
            if d_r ~= 0 then
                if Game.hw[iff(d_r > 0, self.r, r)][c] then return end
            elseif d_c ~= 0 then
                if Game.vw[iff(d_c> 0,  self.c, c)][r] then return end
            end
        end
        if Game[r][c] then return false, Game[r][c] end
        Game[self.r] [self.c] = nil
        Game:Place(self, r, c)
        return true
    end
end

function Game:Elapse()
    return os.time() - self.start
end

function Game:MoveAll()
    if numberOfPlayers == 1 then
        for i= 1, math.random(1,  self.maxExtraMove) do
            local bestMove = {self:RandomIncr()}
            -- 70 % of the time we are moving away for the 1st 30 second
            if  i==1 and (Game:Elapse() % 10) > 2 then
                local otherMove = {self:RandomIncr()}
                if distance(bestMove) < distance(otherMove) then
                    bestMove = otherMove
                end
            end
            Present:move(bestMove[1], bestMove[2])
        end
    end

    for i= 1, math.random(1, self.maxHoleMoved) do
        local extra = math.random(1, #self.post)
        self.post[extra]:move(self:RandomIncr())
    end

    for i= 1, math.random(1, self.maxWallMoved) do
        local r, c = self:RandomWall(true)
        local tr, tc = self:RandomWall(true)
        if not self.hw[tr][tc] then self.hw[r][c], self.hw[tr][tc]  =  self.hw[tr][tc], self.hw[r][c] end
    end

    for i= 1, math.random(1, self.maxWallMoved) do
        local r, c = self:RandomWall(false)
        local tr, tc = self:RandomWall(false)
        if  not self.vw[tc][tr] then  self.vw[c][r], self.vw[tc][tr]  =  self.vw[tc][tr], self.vw[c][r] end
    end

    RestartTimer()
    if lastElapsed ~= Game:Elapse() then
        local w = 0;
        for k,v in pairs(self.hw)  do
            for k,v in pairs(v)  do
                w = w + 1
            end
        end
        for k,v in pairs(self.vw)  do
            for k,v in pairs(v)  do
                w = w + 1
            end
        end
        frame:SetStatusText(string.format("<<< %s >>>     %s%6d     %s%6d     %s%6d",
                string.upper(self.level),"TIME:",Game:Elapse(), 'Speed: ',600 - timeTimer ,'Walls:',w))
        lastElapsed = Game:Elapse()
        -- removing walls makes the game easier as time goes byeselft.
        local r, c = self:RandomWall(false) self.vw[c][r] = nil
        local r, c = self:RandomWall(true)  self.hw[r][c]= nil
    end

    redrawRequired = true
    frame:Refresh()
end

function Occupant:MovePlayer(keyCode)
    local moved, target
    if keyCode == wx.WXK_RIGHT then
        moved, target = self:move(0, 1)
    elseif keyCode == wx.WXK_LEFT then
        moved, target = self:move(0, -1)
    elseif keyCode == wx.WXK_UP then
        moved, target = self:move(-1, 0)
    elseif keyCode == wx.WXK_DOWN then
        moved, target = self:move(1, 0)
    end
    if target then
        timer:Stop()
        local message
        if target == Present or target == Person then
            message = "Got the present "
            if soundTata:IsOk() then soundTata:Play() end
            Game:setLevel(Game.levelId + 1)
        elseif self == Person then
            message = "Sorry you lost the present "
        end
        if message then
            local dialog = wx.wxMessageDialog( frame, message..Game:Elapse() .. ' second(s).\n\nPlay one more', "Catch", wx.wxCENTRE + wx.wxYES_NO)
            local result = dialog:ShowModal()
            dialog:Destroy()
            if result == wx.wxID_YES then Game:Init() else frame:Close(true) end
            return
        end
        RestartTimer()
    end
    if moved then
        Game:MoveAll()
    else
        if soundRecycle:IsOk() then soundRecycle:Play() end
    end
end

function OnKeyDown(event)
    if event.KeyCode == wx.WXK_ESCAPE then frame:Close() end
    if numberOfPlayers == 1 or joystick1Control == ids.ID_JOY_MV_PRESENT then
        Person:MovePlayer(event.KeyCode)
    else
        Present:MovePlayer(event.KeyCode)
    end
end

function OnJoyStick(event)
    if timer:IsRunning() then
        local player = Person
        if event:GetJoystick() == 1 then -- not the same as wx.wxJOYSTICK1 (sorry)
            joystick = joyStick1
            if joystick1Control == ids.ID_JOY_MV_PRESENT then
                player = Present
            end
        else
            joystick = joyStick2
            if joystick1Control ~= ids.ID_JOY_MV_PRESENT then
                player = Present
            end
        end
        local pt = joystick.Position
        local x, y = pt.X - joystick.XMax/2, pt.Y - joystick.YMax/2
        local xym = math.max(math.abs(x), math.abs(y))
        if xym > joystick.XMax/11 then
            if xym == math.abs(x) then
                player:MovePlayer(iff(x > 0, wx.WXK_RIGHT, wx.WXK_LEFT))
            else
                player:MovePlayer(iff(y < 0, wx.WXK_UP, wx.WXK_DOWN))
            end
        end
    end
end

function RestartTimer()
    -- the game slows down
    timeTimer = math.min(timeTimer+1, Game.maxTimer)
    timer:Start(timeTimer)
    collectgarbage("collect")
end

function OnTimer(event)
    Game:MoveAll()
end

-- XPM data
function xpm(name)
    local names = {}

    names.Present = {
"16 16 7 1",
"  c #FFFFFF",
". c #06985C",
"+ c #091925",
"@ c #E1B901",
"# c #68F89B",
"$ c #CBF95B",
"% c #FF7611",
"      .         ",
".......+++......",
".      + +     .",
". @@@@@+@+@@@@@.",
". @   .+ +    @.",
". @ ###+#+####@.",
". @ #. + +   #@.",
"++++++++++++++++",
"+.@ # $+ +  $#@.",
"++++++++++++++++",
". @ # $+%+ %$#@.",
". @ # $+%+%%$#@.",
". @ # $+$+$$$#@.",
". @ ###+#+####@.",
". @@@@@+@+@@@@@.",
".......+.+......"};

    names.Person = {
        "16 16 2 1",
"  c none",
".    c #000000",
"                ",
"       ...      ",
"     ..   ..    ",
"    .       .   ",
"    .  .  .  .  ",
"    .       .   ",
"     ..   ..    ",
"       ...      ",
"       . .      ",
"        .       ",
"       . .      ",
"      .   .     ",
"      .   .     ",
"     .     .    ",
"    .       .   ",
"                "};

    names.wall = {
        "16 16 11 1",
        "   c None",
        ".    c #00950B",
        "+ c #fFfF00",
        "@ c #EF0000",
        "# c #F01002",
        "$ c #F23007",
        "% c #F5600F",
        "& c #F67F13",
        "* c #F9AF1B",
        "= c #FCDF22",
        "- c #FBCF20",
        "................",
        "................",
        "..+@#$%&&%$#++..",
        "..++%*=++=*++@..",
        "..#%++++++++%#..",
        "..$*++++++++*$..",
        "..%=++++++++=%..",
        "..&++++++++++&..",
        "..&++++++++++&..",
        "..%=++++++++=%..",
        "..$*++++++++*$..",
        "..#%-++++++-%#..",
        "..@+++=++=*+#@..",
        "..++#$%&&%$+++..",
        "................",
        "................"};
    return names[name]
end

main()

-- set frame icon
local icon = wx.wxIcon()
local xpm = xpm('Person')
xpm[2] = "  c #FFFFFF" -- change background
icon:CopyFromBitmap(wx.wxBitmap(xpm))
frame:SetIcon(icon)

--~dump(_G)
-- Call wx.wxGetApp():MainLoop() last to start the wxWidgets event loop,
-- otherwise the wxLua program will exit immediately.
-- Does nothing if running from wxLua, wxLuaFreeze, or wxLuaEdit since the
-- MainLoop is already running or will be started by the C++ program.
wx.wxGetApp():MainLoop()
