-------------------------------------------------------------------------=---
-- Name:        coroutine.wx.lua
-- Purpose:     Tests coroutines and wxIDLE_EVENTS
-- Author:      Leandro Motta Barros
-- Created:     2006
-- Copyright:
-- Licence:     wxWidgets licence
-------------------------------------------------------------------------=---

-- Load the wxLua module, does nothing if running from wxLua, wxLuaFreeze, or wxLuaEdit
package.cpath = package.cpath..";./?.dll;./?.so;../lib/?.so;../lib/vc_dll/?.dll;../lib/bcc_dll/?.dll;../lib/mingw_dll/?.dll;"
require("wx")

-- Test that we can get the value from a wxLuaObject in a coroutine
wxlobj = wxlua.wxLuaObject("Created in main lua_State")

-------------------------------------------------------------------------------
-- ProgressWindow
-------------------------------------------------------------------------------

ProgressWindow = {
    -- The 'wxDialog' being encapsulated by this "class".
    dialog = nil,

    -- The label telling what's going on ("Processing 7/10...")
    label = nil,
}

-- The constructor for the progress dialog.
-- 'parent' is the parent window.
-- 'workCoroutine' is the coroutine used to process whatever is desired.
-- 'caption' is the caption to be used for this window.
-- 'initialLabel' is the string that will be initially shown in the window.
function ProgressWindow:new(parent, workCoroutine, caption, initialLabel)

    -- Sanity test to ensure that we can call binding functions
    -- fails on r:SetX if wx bindings aren't installed correctly for the coroutine
    local r = wx.wxRect(1,2,3,4)
    r:SetX(10)
    assert((r:GetX() == 10) and (r:GetY() == 2))

    -- Test that wxLuaObjects also work in coroutines
    local co_wxlobj = wxlua.wxLuaObject("hello")
    assert(co_wxlobj:GetObject() == "hello")
    co_wxlobj:SetObject(r)
    assert(co_wxlobj:GetObject():GetX() == 10)

    -- Test that wxLuaObjects created in main lua_State still work in coroutines
    assert(wxlobj:GetObject() == "Created in main lua_State")

    -- Test that wxLuaObjects created in coroutine can be accessed in main lua_State
    self.wxlobj = wxlua.wxLuaObject("Created in coroutine")

    -- Create a Lua 'class' object to store the coroutine's data
    local o = { }
    setmetatable(o, self)
    self.__index = self

    -- Check input parameters
    assert(type(workCoroutine) == "thread")
    assert(type(caption) == "string")
    assert(type(initialLabel) == "string")

    -- Create dialog
    o.dialog = wx.wxDialog(parent, wx.wxID_ANY, caption,
                           wx.wxDefaultPosition, wx.wxDefaultSize,
                           wx.wxDEFAULT_DIALOG_STYLE)

    o.label = wx.wxTextCtrl(o.dialog, wx.wxID_ANY, "",
                            wx.wxDefaultPosition, wx.wxDefaultSize,
                            wx.wxTE_MULTILINE + wx.wxTE_READONLY)
    local mainSizer = wx.wxBoxSizer(wx.wxVERTICAL)
    mainSizer:Add(o.label, 1, wx.wxGROW)
    o.dialog:SetSizer(mainSizer)

    -- Handle idle events: run the coroutine's next "step"
    o.dialog:Connect(wx.wxEVT_IDLE,
            function (event)
                if coroutine.status(workCoroutine) ~= "dead" then
                    local s, msg = coroutine.resume(workCoroutine)
                    if not msg then
                        o.dialog:Close()
                    else
                        o:setStatus(msg)
                    end
                    event:RequestMore()
                    event:Skip()
                end
            end)

    -- Voilà
    return o
end


-- Sets the ProgressWindow's "status". For now, this means "change the text"
-- being displayed in the window (usually something like "Doing this thing").
function ProgressWindow:setStatus(label)
    self.label:AppendText("\n"..label)
end


-------------------------------------------------------------------------------
-- The main frame
-------------------------------------------------------------------------------

local ID_THE_BUTTON = wx.wxID_HIGHEST + 100

frame = wx.wxFrame(wx.NULL, wx.wxID_ANY,
                    "wxLua Idle Events and Coroutines")

-- ----------------------------------------------------------------------------
-- create a simple file menu
local fileMenu = wx.wxMenu()
fileMenu:Append(wx.wxID_EXIT, "E&xit", "Quit the program")

-- create a simple help menu
local helpMenu = wx.wxMenu()
helpMenu:Append(wx.wxID_ABOUT, "&About", "About the wxLua Coroutine Sample")

-- create a menu bar and append the file and help menus
local menuBar = wx.wxMenuBar()
menuBar:Append(fileMenu, "&File")
menuBar:Append(helpMenu, "&Help")

-- attach the menu bar into the frame
frame:SetMenuBar(menuBar)

-- connect the selection event of the exit menu item to an
-- event handler that closes the window
frame:Connect(wx.wxID_EXIT, wx.wxEVT_COMMAND_MENU_SELECTED,
              function (event) frame:Close(true) end )

-- connect the selection event of the about menu item
frame:Connect(wx.wxID_ABOUT, wx.wxEVT_COMMAND_MENU_SELECTED,
        function (event)
            wx.wxMessageBox('This is the "About" dialog of the Coroutine wxLua sample.\n'..
                            wxlua.wxLUA_VERSION_STRING.." built with "..wx.wxVERSION_STRING,
                            "About wxLua",
                            wx.wxOK + wx.wxICON_INFORMATION,
                            frame)
        end )

-- ----------------------------------------------------------------------------

panel = wx.wxPanel(frame, wx.wxID_ANY)
button = wx.wxButton(panel, ID_THE_BUTTON, "Perform some long operation")

frame:Connect(ID_THE_BUTTON, wx.wxEVT_COMMAND_BUTTON_CLICKED,
        function (event)
            local function workFunc()
                for i = 1, 10 do
                    coroutine.yield("Performing step "..tostring(i).."/10")
                    wx.wxSleep(1)
                end
            end

            local workCoroutine = coroutine.create(workFunc)

            local wndProgress =
                ProgressWindow:new(frame, workCoroutine,
                                   "Performing some long operation",
                                   "Performing step 1/many")
            wndProgress.dialog:ShowModal(true)

            wndProgress.dialog:Destroy()

            assert(wndProgress.wxlobj:GetObject() == "Created in coroutine")
        end)


-- Show the main frame
frame:Show(true)

-- 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()
