-- ************************************ Object ************************************ --
BalloonDialog = getClass("BalloonDialog")

SpriteExt = getClass("SpriteExt")

ghf.GameNode.getNext = SpriteExt.getNext
function ghf.GameNode:traverse(f)
	local node = self
	while (node) do
		local r = f(self, node)
		if (r) then return r end
		node = node:getNext(self)
	end
end

Object = getClass("Object")

function Object:addTasks(t)
	t = taskSystem:wrapSingleTable(t)
	t = table.copy(t, 1)
	for k,v in pairs(t) do
		v.actor = self
	end
	taskSystem:addTasks(t)
end

Object.addTask = Object.addTasks

function Object:onMessagePositionChanged()
	if (isTable(self.messageDialogs)) then
		local p = self.messageDialogOffset or {x = self.width/2 - self.anchor.x, y = -self.anchor.y}
		for _,v in pairs(self.messageDialogs) do
			if (v.setTipPosition) then
				local dx = self.screenX - ((v.parent and v.parent.screenX) or 0)
				local dy = self.screenY - ((v.parent and v.parent.screenY) or 0)
				v:setTipPosition(dx + p.x + v.offset.x, dy + p.y + v.offset.y)

				local left = v.screenX - v.anchor.x
				local right = left + v.width
				if (left < 0) then
					v.x = v.x - left
				elseif (right > 1024) then
					v.x = v.x + (1024 - right)
				end
			end
		end
	end
end

function Object:showInputMessage(desc)
	assert(false, "Text typing in dialog bubbles has not been implemented yet!")
	return self:showMessageDialog(desc)
end

function Object:showChoiceMessage(desc)
	desc = table.copy(desc)
	desc.content = desc.content or { children = {}, }
	desc.spacing = desc.spacing or 20
	desc.width = desc.width or 40
	local num = table.count(desc.choices)
	local curX = -((num / 2) * desc.width + ((num - 1) / 2) * desc.spacing)
	local aChoice
	for k,v in pairs(desc.choices) do
		aChoice = k
		table.insert(desc.content.children, { class="Button", name=k, image=v, x=curX, y=0, anchor="topleft", })
		curX = curX + desc.width + desc.spacing
	end
	desc.onDebugSkip = function(self)
		self:getChild(aChoice):onClick()
	end
	return self:showMessageDialog(desc)
end

function Object:showYesNoMessage(desc)
	desc = table.copy(desc)
	desc.yesImage = desc.yesImage or "BALLOON_YES:default"
	desc.noImage = desc.noImage or "BALLOON_NO:default"
	desc.spacing = desc.spacing or 20
	desc.content = desc.content or {
		children = {
			{ class="Button", name="yes", image=desc.yesImage, x=-desc.spacing/2, anchor="topright", },
			{ class="Button", name="no", image=desc.noImage, x=desc.spacing/2, anchor="topleft", },
		},
	}
	desc.onDebugSkip = function(self)
		self:getChild("yes"):onClick()
	end
	return self:showMessageDialog(desc)
end

function Object:showOkMessage(desc)
    desc = table.copy(desc)
	desc.image = desc.image or "BALLOON_OK:default"
	desc.content = desc.content or {
		children = {
			{ class="Button", name="ok", image=desc.image, anchor="top", },
		},
	}
	desc.onDebugSkip = function(self)
		self:getChild("ok"):onClick()
	end
	return self:showMessageDialog(desc)
end

function Object:showMessageDialog(desc)
	desc = table.copy(desc)
	if (not desc.text) then
		desc.textId = desc.textId or ""
	end
	desc.dialogId = desc.dialogId or desc.textId or math.Random(999999)
	desc.tipType = desc.tipType or "point"
	desc.tipSide = desc.tipSide or "center"
	desc.labelWidth = desc.labelWidth or 200
	if (desc.content) then
		desc.content.y = desc.content.y or 10
	end

	local dialogDef = { class="BalloonDialog",
		owner = self,
		dialogId = desc.dialogId,
		anchor = "top",
		topPadding = 20,
		sidePadding = 20,
		bottomPadding = 20,
		textId = desc.textId,
		tipType = desc.tipType,
		tipSide = desc.tipSide,
		children = {
			{ class="Label",
				name="message",
				text = desc.text,
				maxWidth=desc.labelWidth,
				wrapStyle="wordwrap",
				font=desc.font or "FONT_BALLOON_DIALOG:default",
				anchor=desc.anchor or "top",
				roundPos=false,
			},
		},
		content = desc.content,
		offset = toPoint(desc.offset),
		onDebugSkip = desc.onDebugSkip,
	}

	local dlg = self:newChild(dialogDef)
    return self:setupBalloonDialog(dlg, desc)
end

function Object:setupBalloonDialog(dlg, desc)
    desc = desc or {}
	desc.onClick = desc.onClick or function(sender)
		local handled = false
		if (desc.task and desc.task.onMessageClick) then
			handled = handled or desc.task:onMessageClick(sender.name, desc.dialogId, self)
		end
		handled = handled or self:onMessageClick(sender.name, desc.dialogId)
		handled = handled or level:onMessageClick(sender.name, desc.dialogId, self)
		handled = handled or (level.event and level.event:onMessageClick(sender.name, desc.dialogId, self))
		self:hideMessageDialog(dlg, false, desc.delayBeforeHide)
	end

	self.messageDialogs = self.messageDialogs or {}
	local oldDlg = self.messageDialogs[desc.dialogId]
	if (isTable(oldDlg)) then
		print("Warning: Removing message with dup ID: "..tostring(oldDlg.dialogId))
		self:removeMessageDialog(oldDlg)
	end
	self.messageDialogs[desc.dialogId] = dlg
	
	dlg:traverse(function(self, node) node.onClick = desc.onClick end)
	dlg:onShow()
	if (dlg.name == "") then
		dlg.name = desc.dialogId or "Unnamed Message Dialog"
	end
	self:onMessagePositionChanged()
	return dlg
end

function Object:getMessageDialogAndId(dlg)
	local theDialog, theDialogId

	if (isTable(dlg)) then
		theDialog = dlg
		if (self.messageDialogs) then
			for k,v in pairs(self.messageDialogs) do
				if (v == theDialog) then
					theDialogId = k
					break
				end
			end
		end
		if (theDialogId == nil) then
			theDialogId = theDialog.dialogId
		end

	elseif (isString(dlg)) then
		theDialogId = dlg
		if (self.messageDialogs) then
			theDialog = self.messageDialogs[dlg]
			if (theDialog == nil) then
				for k,v in pairs(self.messageDialogs) do
					if (v.dialogId == theDialogId) then
						theDialog = v
						break
					end
				end
			end
		end
	end

	return theDialog, theDialogId
end

function Object:findMessageDialog(dlg)
	if (isTable(dlg)) then
		dlg = dlg.dialogId or dlg.textId
	end
	return (self.messageDialogs and dlg and self.messageDialogs[dlg])
end

function Object:removeMessageDialog(dlg, all)
	all = all ~= false
	local theDialog, theDialogId = self:getMessageDialogAndId(dlg)

	if (theDialog and theDialog.removeFromParent) then
		theDialog:removeFromParent()
	end
	if (theDialogId and self.messageDialogs) then
		self.messageDialogs[theDialogId] = nil
		if (all) then
			for k,v in pairs(self.messageDialogs) do
				if (v.dialogId == theDialogId) then
					self.messageDialogs[k] = nil
					if (v.removeFromParent) then
						v:removeFromParent()
					end
				end
			end
		end
	end
end

function Object:hideMessageDialog(dlg, all, duration)
	all = all ~= false
	duration = duration or 0
	local theDialog, theDialogId = self:getMessageDialogAndId(dlg)

	if (theDialog and theDialog.onHide) then
		theDialog:onHide(duration)
	end
	if (theDialogId and all and self.messageDialogs) then
		for k,v in pairs(self.messageDialogs) do
			if (v ~= theDialog and v.dialogId == theDialogId and v.onHide) then
				v:onHide(duration)
			end
		end
	end
end
