ScrollView = {}
ScrollView_mt = { __index = ScrollView }
setmetatable(ScrollView, Control_mt)

function ScrollView.Create(root, parentObject, control)
	-- Create object
	local scrollView = Control.Create(root, parentObject)
	setmetatable(scrollView, ScrollView_mt)
	
	-- Set constants
	scrollView.kScrollbarWidth = 20
	scrollView.kWheelSpeed = 0.0003

	-- Set attribtues
	scrollView.control = control -- nodegrid
	
	scrollView.scrollPosX = 0
	scrollView.scrollPosY = 0
	
	scrollView.vScrollbar = Scrollbar.Create(root, self, function(value) scrollView.scrollPosY = value; scrollView:UpdateScrollPos() end, true, false)
	scrollView.hScrollbar = Scrollbar.Create(root, self, function(value) scrollView.scrollPosX = value; scrollView:UpdateScrollPos() end, false, true)
	
	-- State variables
	scrollView.canvasSize = GPoint(0, 0)
	scrollView.rect = GRect() -- this is the rect for the whole view (including scrollbars)
	
	scrollView.hScrollVisible = false
	scrollView.vScrollVisible = false
	
	-- Layout rects
	scrollView.innerRect = GRect()
	scrollView.hScrollRect = GRect()
	scrollView.vScrollRect = GRect()
	
	return scrollView
end

function ScrollView:UpdateLayout(rect)
	-- Get canvas size
	local newCanvasSize = self.control:GetCanvasSize(rect)
	
	-- Check if anything changed
	if (newCanvasSize.x ~= self.canvasSize.x or newCanvasSize.y ~= self.canvasSize.y or not self.rect:IsEqual(rect)) then
		-- Set state variables
		self.rect:Copy(rect)
		self.canvasSize = newCanvasSize

		-- Compute sizes
		self.hScrollVisible = (self.canvasSize.x > self.rect:GetWidth())
		self.vScrollVisible = (self.canvasSize.y > self.rect:GetHeight())
		
		-- Update layout
		self.innerRect:Copy(rect)
		self.hScrollRect:Copy(rect)
		self.vScrollRect:Copy(rect)
		
		if (self.hScrollVisible) then
			-- Update rects
			local hSplitPos = rect.bottom - self.kScrollbarWidth
			self.innerRect.bottom = math.max(rect.top, hSplitPos)
			self.hScrollRect.top = hSplitPos
			self.vScrollRect.bottom = math.max(rect.top, hSplitPos)
			
			-- Set scrollbar size
			self.hScrollbar.barSize = math.min(1, self.rect:GetWidth() / self.canvasSize.x)
		else
			self.hScrollRect.top = rect.bottom
		end
		
		if (self.vScrollVisible) then
			-- Update rects
			local vSplitPos = rect.right - self.kScrollbarWidth
			self.innerRect.right = math.max(rect.left, vSplitPos)
			self.vScrollRect.left = vSplitPos
			self.hScrollRect.right = math.max(rect.left, vSplitPos) 
			
			-- Set scrollbar size
			self.vScrollbar.barSize = math.min(1, self.rect:GetHeight() / self.canvasSize.y)
		else
			self.vScrollRect.right = rect.right
		end
		
		self:UpdateScrollPos()
	end
end

function ScrollView:UpdateScrollPos()
	local scrollOffset = GPoint(0, 0)
	
	if (self.hScrollVisible) then
		scrollOffset.x = -(self.canvasSize.x - self.innerRect:GetWidth())*self.scrollPosX
	end
	
	if (self.vScrollVisible) then
		scrollOffset.y = -(self.canvasSize.y - self.innerRect:GetHeight())*self.scrollPosY
	end

	self.control:SetScrollOffset(scrollOffset)
	
	if (self.control.SetScrollPos) then
		self.control:SetScrollPos(GPoint(self.scrollPosX, self.scrollPosY))
	end
end

function ScrollView:SetScrollPos(scrollPos)
	self.scrollPosX = scrollPos.x
	self.scrollPosY = scrollPos.y
	
	self:UpdateScrollPos()
	self.hScrollbar.pos = self.scrollPosX
	self.vScrollbar.pos = self.scrollPosY
end

function ScrollView:HandleEvent(window, rect, event)
	-- Update layout
	self:UpdateLayout(rect)

	self.isHot = rect:IsPointInside(event.pos) and not self:IsOtherControlActive()
	
	-- Call control
	self.control:HandleEvent(window, self.innerRect, event)
	
	-- Update layout (may have changed, e.g. by adding element to list)
	self:UpdateLayout(rect)
	
	-- Call scrollbars
	if (self.hScrollVisible) then
		self.hScrollbar:HandleEvent(window, self.hScrollRect, event)
	end
		
	if (self.vScrollVisible) then
		self.vScrollbar:HandleEvent(window, self.vScrollRect, event)
	end

	if (event.type==kGEventMouseWheel and self.isHot) then
		--print('scroll' .. event.wheelDelta .. '\n')
		local speed = event.wheelDelta * self.kWheelSpeed
		speed = 0 - speed -- invert mouse

		if (event:HasModifier(kGEventModifierShift)) then			
			self.scrollPosX = math.min(math.max(self.scrollPosX + speed, 0), 1)
		else
			self.scrollPosY = math.min(math.max(self.scrollPosY + speed, 0), 1)	
		end
		self:SetScrollPos(GPoint(self.scrollPosX, self.scrollPosY))
	end
end

function ScrollView:Draw(window, rect)
	self:UpdateLayout(rect)
	self.control:Draw(window, self.innerRect)
	
	if (self.hScrollVisible) then
		self.hScrollbar:Draw(window, self.hScrollRect)
	end
		
	if (self.vScrollVisible) then
		self.vScrollbar:Draw(window, self.vScrollRect)
	end
end