-- see copyright notice in wxLdb.lua

local print = print
local wx = require( "wx" )
_G.print = print -- override wx print function with original one
local wxstc = require( "wxstc" )
local mainthread = require( "mainthread" )

local setmetatable = setmetatable

module( "ui.editor" )

local font
local fontItalic

-- Pick some reasonable fixed width fonts to use for the editor
if wx.__WXMSW__ then
    font       = wx.wxFont(10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_NORMAL, wx.wxFONTWEIGHT_NORMAL, false, "Andale Mono")
    fontItalic = wx.wxFont(10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_ITALIC, wx.wxFONTWEIGHT_NORMAL, false, "Andale Mono")
else
    font       = wx.wxFont(10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_NORMAL, wx.wxFONTWEIGHT_NORMAL, false, "")
    fontItalic = wx.wxFont(10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_ITALIC, wx.wxFONTWEIGHT_NORMAL, false, "")
end

-- Markers for editor margin
markers =
{
	breakpoint = 1,
	badBreakpoint = 2,
	currentLine = 3,
	otherLine = 4,
}

local function createEditor( parent, owner )
	local editor = wxstc.wxStyledTextCtrl( parent, wx.wxID_ANY, wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxSUNKEN_BORDER )
	
	editor:SetBufferedDraw(true)
    editor:StyleClearAll()

    editor:SetFont(font)
    editor:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, font)
    for i = 0, 32 do
        editor:StyleSetFont(i, font)
    end

    editor:StyleSetForeground(0,  wx.wxColour(128, 128, 128)) -- White space
    editor:StyleSetForeground(1,  wx.wxColour(0,   127, 0))   -- Block Comment
    editor:StyleSetFont(1, fontItalic)
    --editor:StyleSetUnderline(1, false)
    editor:StyleSetForeground(2,  wx.wxColour(0,   127, 0))   -- Line Comment
    editor:StyleSetFont(2, fontItalic)                        -- Doc. Comment
    --editor:StyleSetUnderline(2, false)
    editor:StyleSetForeground(3,  wx.wxColour(127, 127, 127)) -- Number
    editor:StyleSetForeground(4,  wx.wxColour(0,   127, 127)) -- Keyword
    editor:StyleSetForeground(5,  wx.wxColour(0,   0,   127)) -- Double quoted string
    editor:StyleSetBold(5,  true)
    --editor:StyleSetUnderline(5, false)
    editor:StyleSetForeground(6,  wx.wxColour(127, 0,   127)) -- Single quoted string
    editor:StyleSetForeground(7,  wx.wxColour(127, 0,   127)) -- not used
    editor:StyleSetForeground(8,  wx.wxColour(0,   127, 127)) -- Literal strings
    editor:StyleSetForeground(9,  wx.wxColour(127, 127, 0))  -- Preprocessor
    editor:StyleSetForeground(10, wx.wxColour(0,   0,   0))   -- Operators
    --editor:StyleSetBold(10, true)
    editor:StyleSetForeground(11, wx.wxColour(0,   0,   0))   -- Identifiers
    editor:StyleSetForeground(12, wx.wxColour(0,   0,   0))   -- Unterminated strings
    editor:StyleSetBackground(12, wx.wxColour(224, 192, 224))
    editor:StyleSetBold(12, true)
    editor:StyleSetEOLFilled(12, true)

    editor:StyleSetForeground(13, wx.wxColour(0,   0,  95))   -- Keyword 2 highlighting styles
    editor:StyleSetForeground(14, wx.wxColour(0,   95, 0))    -- Keyword 3
    editor:StyleSetForeground(15, wx.wxColour(127, 0,  0))    -- Keyword 4
    editor:StyleSetForeground(16, wx.wxColour(127, 0,  95))   -- Keyword 5
    editor:StyleSetForeground(17, wx.wxColour(35,  95, 175))  -- Keyword 6
    editor:StyleSetForeground(18, wx.wxColour(0,   127, 127)) -- Keyword 7
    editor:StyleSetBackground(18, wx.wxColour(240, 255, 255)) -- Keyword 8

    editor:StyleSetForeground(19, wx.wxColour(0,   127, 127))
    editor:StyleSetBackground(19, wx.wxColour(224, 255, 255))
    editor:StyleSetForeground(20, wx.wxColour(0,   127, 127))
    editor:StyleSetBackground(20, wx.wxColour(192, 255, 255))
    editor:StyleSetForeground(21, wx.wxColour(0,   127, 127))
    editor:StyleSetBackground(21, wx.wxColour(176, 255, 255))
    editor:StyleSetForeground(22, wx.wxColour(0,   127, 127))
    editor:StyleSetBackground(22, wx.wxColour(160, 255, 255))
    editor:StyleSetForeground(23, wx.wxColour(0,   127, 127))
    editor:StyleSetBackground(23, wx.wxColour(144, 255, 255))
    editor:StyleSetForeground(24, wx.wxColour(0,   127, 127))
    editor:StyleSetBackground(24, wx.wxColour(128, 155, 255))

    editor:StyleSetForeground(32, wx.wxColour(224, 192, 224))  -- Line number
    editor:StyleSetBackground(33, wx.wxColour(192, 192, 192))  -- Brace highlight
    editor:StyleSetForeground(34, wx.wxColour(0,   0,   255))
    editor:StyleSetBold(34, true)                              -- Brace incomplete highlight
    editor:StyleSetForeground(35, wx.wxColour(255, 0,   0))
    editor:StyleSetBold(35, true)                              -- Indentation guides
    editor:StyleSetForeground(37, wx.wxColour(192, 192, 192))
    editor:StyleSetBackground(37, wx.wxColour(255, 255, 255))

    editor:SetUseTabs(true)
    editor:SetIndentationGuides(true)

    editor:SetVisiblePolicy(wxstc.wxSTC_VISIBLE_SLOP, 3)
    --editor:SetXCaretPolicy(wxstc.wxSTC_CARET_SLOP, 10)
    --editor:SetYCaretPolicy(wxstc.wxSTC_CARET_SLOP, 3)

    editor:SetMarginWidth(0, editor:TextWidth(32, "9999_")) -- line # margin

    editor:SetMarginWidth(1, 16) -- marker margin
    editor:SetMarginType(1, wxstc.wxSTC_MARGIN_SYMBOL)
    editor:SetMarginSensitive(1, true)

    editor:MarkerDefine(markers.breakpoint, wxstc.wxSTC_MARK_ROUNDRECT, wx.wxWHITE, wx.wxRED)
	editor:MarkerDefine(markers.badBreakpoint, wxstc.wxSTC_MARK_ROUNDRECT, wx.wxRED, wx.wxColour(192,192,192))
    editor:MarkerDefine(markers.currentLine, wxstc.wxSTC_MARK_SHORTARROW, wx.wxBLACK, wx.wxColour(255,255,0))
	editor:MarkerDefine(markers.otherLine, wxstc.wxSTC_MARK_ARROW, wx.wxBLACK, wx.wxGREEN)

    editor:SetMarginWidth(2, 16) -- fold margin
    editor:SetMarginType(2, wxstc.wxSTC_MARGIN_SYMBOL)
    editor:SetMarginMask(2, wxstc.wxSTC_MASK_FOLDERS)
    editor:SetMarginSensitive(2, true)

    editor:SetFoldFlags(wxstc.wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED +
                        wxstc.wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED)

    editor:SetProperty("fold", "1")
    editor:SetProperty("fold.compact", "1")
    editor:SetProperty("fold.comment", "1")

    local grey = wx.wxColour(128, 128, 128)
    editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDEROPEN,    wxstc.wxSTC_MARK_BOXMINUS, wx.wxWHITE, grey)
    editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDER,        wxstc.wxSTC_MARK_BOXPLUS,  wx.wxWHITE, grey)
    editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDERSUB,     wxstc.wxSTC_MARK_VLINE,    wx.wxWHITE, grey)
    editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDERTAIL,    wxstc.wxSTC_MARK_LCORNER,  wx.wxWHITE, grey)
    editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDEREND,     wxstc.wxSTC_MARK_BOXPLUSCONNECTED,  wx.wxWHITE, grey)
    editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDEROPENMID, wxstc.wxSTC_MARK_BOXMINUSCONNECTED, wx.wxWHITE, grey)
    editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDERMIDTAIL, wxstc.wxSTC_MARK_TCORNER,  wx.wxWHITE, grey)
    grey:delete()
	
	editor:SetLexer(wxstc.wxSTC_LEX_LUA)

	-- Note: these keywords are shamelessly ripped from scite 1.68
	editor:SetKeyWords(0,
		[[and break do else elseif end false for function if
		in local nil not or repeat return then true until while]])
	editor:SetKeyWords(1,
		[[_VERSION assert collectgarbage dofile error gcinfo loadfile loadstring
		print rawget rawset require tonumber tostring type unpack]])
	editor:SetKeyWords(2,
		[[_G getfenv getmetatable ipairs loadlib next pairs pcall
		rawequal setfenv setmetatable xpcall
		string table math coroutine io os debug
		load module select]])
	editor:SetKeyWords(3,
		[[string.byte string.char string.dump string.find string.len
		string.lower string.rep string.sub string.upper string.format string.gfind string.gsub
		table.concat table.foreach table.foreachi table.getn table.sort table.insert table.remove table.setn
		math.abs math.acos math.asin math.atan math.atan2 math.ceil math.cos math.deg math.exp
		math.floor math.frexp math.ldexp math.log math.log10 math.max math.min math.mod
		math.pi math.pow math.rad math.random math.randomseed math.sin math.sqrt math.tan
		string.gmatch string.match string.reverse table.maxn
		math.cosh math.fmod math.modf math.sinh math.tanh math.huge]])
	editor:SetKeyWords(4,
		[[coroutine.create coroutine.resume coroutine.status
		coroutine.wrap coroutine.yield
		io.close io.flush io.input io.lines io.open io.output io.read io.tmpfile io.type io.write
		io.stdin io.stdout io.stderr
		os.clock os.date os.difftime os.execute os.exit os.getenv os.remove os.rename
		os.setlocale os.time os.tmpname
		coroutine.running package.cpath package.loaded package.loadlib package.path
		package.preload package.seeall io.popen
		debug.debug debug.getfenv debug.gethook debug.getinfo debug.getlocal
		debug.getmetatable debug.getregistry debug.getupvalue debug.setfenv
		debug.sethook debug.setlocal debug.setmetatable debug.setupvalue debug.traceback]])
	
	mainthread.execute( function()
		editor:Connect(wxstc.wxEVT_STC_MARGINCLICK, function( event )
			local line = editor:LineFromPosition(event:GetPosition())
			local margin = event:GetMargin()
			if margin == 1 then
				owner:toggleBreakpoint( line )
			elseif margin == 2 then
				--[[if wx.wxGetKeyState(wx.WXK_SHIFT) and wx.wxGetKeyState(wx.WXK_CONTROL) then
					FoldSome()
				else
					local level = editor:GetFoldLevel(line)
					if HasBit(level, wxstc.wxSTC_FOLDLEVELHEADERFLAG) then
						editor:ToggleFold(line)
					end
				end]]
			end
		end )
	end )
	
	return editor
end

local function splitMarkers( m )
	local maxMarker = 31
	local res = {}
	for marker = maxMarker, 0, -1 do
		local value = 2^marker
		res[marker] = m >= value
		if res[marker] then
			m = m - value
		end
	end
	return res
end

local meta = { __index = {} }

function new( parent )
	local res = {}
	setmetatable( res, meta )
	res.editor = createEditor( parent, res )
	return res
end

function meta.__index:toggleBreakpoint( line )
	local editor = self.editor
	
	--[[local m = editor:MarkerGet(line)
	m = splitMarkers( m )
	
	if m[markers.breakpoint] then
		editor:MarkerDelete( line, markers.breakpoint )
	else
		editor:MarkerAdd( line, markers.breakpoint )
	end]]
	self.breakpointCallback( line + 1 )
end

function meta.__index:destroy()
	-- nothing to do (the parent widget will destroy the editor widget)
end
