// Copyright 2016 The G3N Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gui

import (
	"github.com/g3n/engine/core"
	"github.com/g3n/engine/geometry"
	"github.com/g3n/engine/gls"
	"github.com/g3n/engine/graphic"
	"github.com/g3n/engine/material"
	"github.com/g3n/engine/math32"
)

/*********************************************

 Panel areas:
 +------------------------------------------+
 |  Margin area                             |
 |  +------------------------------------+  |
 |  |  Border area                       |  |
 |  |  +------------------------------+  |  |
 |  |  | Padding area                 |  |  |
 |  |  |  +------------------------+  |  |  |
 |  |  |  | Content area           |  |  |  |
 |  |  |  |                        |  |  |  |
 |  |  |  |                        |  |  |  |
 |  |  |  +------------------------+  |  |  |
 |  |  |                              |  |  |
 |  |  +------------------------------+  |  |
 |  |                                    |  |
 |  +------------------------------------+  |
 |                                          |
 +------------------------------------------+

*********************************************/

// IPanel is the interface for all panel types
type IPanel interface {
	graphic.IGraphic
	GetPanel() *Panel
	SetRoot(*Root)
	LostKeyFocus()
	TotalHeight() float32
}

type Panel struct {
	graphic.Graphic                     // Embedded graphic
	root            *Root               // pointer to root container
	width           float32             // external width in pixels
	height          float32             // external height in pixels
	mat             *material.Material  // panel material
	marginSizes     BorderSizes         // external margin sizes in pixel coordinates
	borderSizes     BorderSizes         // border sizes in pixel coordinates
	paddingSizes    BorderSizes         // padding sizes in pixel coordinates
	content         Rect                // current content rectangle in pixel coordinates
	modelMatrixUni  gls.UniformMatrix4f // pointer to model matrix uniform
	borderColorUni  gls.Uniform4f       // pointer to border color uniform
	paddingColorUni gls.Uniform4f       // pointer to padding color uniform
	contentColorUni gls.Uniform4f       // pointer to content color uniform
	boundsUni       gls.Uniform4f       // pointer to bounds uniform (texture coordinates)
	borderUni       gls.Uniform4f       // pointer to border uniform (texture coordinates)
	paddingUni      gls.Uniform4f       // pointer to padding uniform (texture coordinates)
	contentUni      gls.Uniform4f       // pointer to content uniform (texture coordinates)
	pospix          math32.Vector3      // absolute position in pixels
	xmin            float32             // minimum absolute x this panel can use
	xmax            float32             // maximum absolute x this panel can use
	ymin            float32             // minimum absolute y this panel can use
	ymax            float32             // maximum absolute y this panel can use
	nextChildZ      float32             // Z coordinate of next child added
	bounded         bool                // panel is bounded by its parent
	enabled         bool                // enable event processing
	cursorEnter     bool                // mouse enter dispatched
	layout          ILayout             // current layout for children
	layoutParams    interface{}         // current layout parameters used by container panel
}

const (
	deltaZ = -0.00001
)

// NewPanel creates and returns a pointer to a new panel with the
// specified dimensions in pixels
func NewPanel(width, height float32) *Panel {

	p := new(Panel)
	p.Initialize(width, height)
	return p
}

// Initialize initializes this panel and is normally used by other types which embed a panel.
func (p *Panel) Initialize(width, height float32) {

	p.width = width
	p.height = height
	p.nextChildZ = deltaZ

	// Builds array with vertex positions and texture coordinates
	positions := math32.NewArrayF32(0, 20)
	positions.Append(
		0, 0, 0, 0, 1,
		0, -1, 0, 0, 0,
		1, -1, 0, 1, 0,
		1, 0, 0, 1, 1,
	)
	// Builds array of indices
	indices := math32.NewArrayU32(0, 6)
	indices.Append(0, 1, 2, 0, 2, 3)

	// Creates geometry
	geom := geometry.NewGeometry()
	geom.SetIndices(indices)
	geom.AddVBO(gls.NewVBO().
		AddAttrib("VertexPosition", 3).
		AddAttrib("VertexTexcoord", 2).
		SetBuffer(positions),
	)

	// Initialize material
	p.mat = material.NewMaterial()
	p.mat.SetShader("shaderPanel")

	// Initialize graphic
	p.Graphic.Init(geom, gls.TRIANGLES)
	p.AddMaterial(p, p.mat, 0, 0)

	// Creates and adds uniform
	p.modelMatrixUni.Init("ModelMatrix")
	p.borderColorUni.Init("BorderColor")
	p.paddingColorUni.Init("PaddingColor")
	p.contentColorUni.Init("ContentColor")
	p.boundsUni.Init("Bounds")
	p.borderUni.Init("Border")
	p.paddingUni.Init("Padding")
	p.contentUni.Init("Content")

	// Set defaults
	p.borderColorUni.Set(0, 0, 0, 1)
	p.bounded = true
	p.enabled = true

	p.resize(width, height)
}

// GetPanel satisfies the IPanel interface and
// returns pointer to this panel
func (pan *Panel) GetPanel() *Panel {

	return pan
}

// SetRoot satisfies the IPanel interface
// and sets the pointer to this panel root container
func (pan *Panel) SetRoot(root *Root) {

	pan.root = root
}

// LostKeyFocus satisfies the IPanel interface and is called by gui root
// container when the panel loses the key focus
func (p *Panel) LostKeyFocus() {

}

// TotalHeight satisfies the IPanel interface and returns the total
// height of this panel considering visible not bounded children
func (p *Panel) TotalHeight() float32 {

	return p.Height()
}

// SetSelected satisfies the IPanel interface and is normally called
// by a list container to change the panel visual appearance
func (p *Panel) SetSelected2(state bool) {

}

// SetHighlighted satisfies the IPanel interface and is normally called
// by a list container to change the panel visual appearance
func (p *Panel) SetHighlighted2(state bool) {

}

// Material returns a pointer for this panel core.Material
func (p *Panel) Material() *material.Material {

	return p.mat
}

// SetTopChild sets the Z coordinate of the specified panel to
// be on top of all other children of this panel.
// The function does not check if the specified panel is a
// child of this one.
func (p *Panel) SetTopChild(ipan IPanel) {

	ipan.GetPanel().SetPositionZ(p.nextChildZ)
}

// SetTopChild sets this panel to be on the foreground
// in relation to all its siblings.
func (p *Panel) SetForeground() {

	// internal function to calculate the total minimum Z
	// for a panel hierarchy
	var getTopZ func(*Panel) float32
	getTopZ = func(pan *Panel) float32 {
		topZ := pan.Position().Z
		for _, iobj := range pan.Children() {
			child := iobj.(IPanel).GetPanel()
			cz := pan.Position().Z + getTopZ(child)
			if cz < topZ {
				topZ = cz
			}
		}
		return topZ
	}

	// Find the child of this panel with the minimum Z
	par := p.Parent().(IPanel).GetPanel()
	topZ := float32(10)
	for _, iobj := range par.Children() {
		child := iobj.(IPanel).GetPanel()
		cz := getTopZ(child)
		if cz < topZ {
			topZ = cz
		}
	}
	if p.Position().Z > topZ {
		p.SetPositionZ(topZ + deltaZ)
	}
}

// SetPosition sets this panel absolute position in pixel coordinates
// from left to right and from top to bottom of the screen.
func (p *Panel) SetPosition(x, y float32) {

	p.Node.SetPositionX(math32.Round(x))
	p.Node.SetPositionY(math32.Round(y))
}

// SetSize sets this panel external width and height in pixels.
func (p *Panel) SetSize(width, height float32) {

	if width < 0 {
		log.Warn("Invalid panel width:%v", width)
		width = 0
	}
	if height < 0 {
		log.Warn("Invalid panel height:%v", height)
		height = 0
	}
	p.resize(width, height)
}

// SetWidth sets this panel external width in pixels.
// The internal panel areas and positions are recalculated
func (p *Panel) SetWidth(width float32) {

	p.SetSize(width, p.height)
}

// SetHeight sets this panel external height in pixels.
// The internal panel areas and positions are recalculated
func (p *Panel) SetHeight(height float32) {

	p.SetSize(p.width, height)
}

// SetContentAspectWidth sets the width of the content area of the panel
// to the specified value and adjusts its height to keep the same aspect radio.
func (p *Panel) SetContentAspectWidth(width float32) {

	aspect := p.content.Width / p.content.Height
	height := width / aspect
	p.SetContentSize(width, height)
}

// SetContentAspectHeight sets the height of the content area of the panel
// to the specified value and adjusts its width to keep the same aspect ratio.
func (p *Panel) SetContentAspectHeight(height float32) {

	aspect := p.content.Width / p.content.Height
	width := height / aspect
	p.SetContentSize(width, height)
}

// Width returns the current panel external width in pixels
func (p *Panel) Width() float32 {

	return p.width
}

// Height returns the current panel external height in pixels
func (p *Panel) Height() float32 {

	return p.height
}

// ContentWidth returns the current width of the content area in pixels
func (p *Panel) ContentWidth() float32 {

	return p.content.Width
}

// ContentHeight returns the current height of the content area in pixels
func (p *Panel) ContentHeight() float32 {

	return p.content.Height
}

// SetMargins set this panel margin sizes in pixels
// and recalculates the panel external size
func (p *Panel) SetMargins(top, right, bottom, left float32) {

	p.marginSizes.Set(top, right, bottom, left)
	p.resize(p.calcWidth(), p.calcHeight())
}

// SetMarginsFrom sets this panel margins sizes from the specified
// BorderSizes pointer and recalculates the panel external size
func (p *Panel) SetMarginsFrom(src *BorderSizes) {

	p.marginSizes = *src
	p.resize(p.calcWidth(), p.calcHeight())
}

// Margins returns the current margin sizes in pixels
func (p *Panel) Margins() BorderSizes {

	return p.marginSizes
}

// SetBorders sets this panel border sizes in pixels
// and recalculates the panel external size
func (p *Panel) SetBorders(top, right, bottom, left float32) {

	p.borderSizes.Set(top, right, bottom, left)
	p.resize(p.calcWidth(), p.calcHeight())
}

// SetBordersFrom sets this panel border sizes from the specified
// BorderSizes pointer and recalculates the panel size
func (p *Panel) SetBordersFrom(src *BorderSizes) {

	p.borderSizes = *src
	p.resize(p.calcWidth(), p.calcHeight())
}

// Borders returns this panel current border sizes
func (p *Panel) Borders() BorderSizes {

	return p.borderSizes
}

// SetPaddings sets the panel padding sizes in pixels
func (p *Panel) SetPaddings(top, right, bottom, left float32) {

	p.paddingSizes.Set(top, right, bottom, left)
	p.resize(p.calcWidth(), p.calcHeight())
}

// SetPaddingsFrom sets this panel padding sizes from the specified
// BorderSizes pointer and recalculates the panel size
func (p *Panel) SetPaddingsFrom(src *BorderSizes) {

	p.paddingSizes = *src
	p.resize(p.calcWidth(), p.calcHeight())
}

// Paddings returns this panel padding sizes in pixels
func (p *Panel) Paddings() BorderSizes {

	return p.paddingSizes
}

// SetBordersColor sets the color of this panel borders
// The borders opacity is set to 1.0 (full opaque)
func (p *Panel) SetBordersColor(color *math32.Color) {

	p.borderColorUni.Set(color.R, color.G, color.B, 1)
}

// SetBordersColor4 sets the color and opacity of this panel borders
func (p *Panel) SetBordersColor4(color *math32.Color4) {

	p.borderColorUni.Set(color.R, color.G, color.B, color.A)
}

// BorderColor4 returns current border color
func (p *Panel) BordersColor4() math32.Color4 {

	var color math32.Color4
	p.borderColorUni.SetColor4(&color)
	return color
}

// SetPaddingsColor sets the color of this panel paddings.
func (p *Panel) SetPaddingsColor(color *math32.Color) {

	p.paddingColorUni.Set(color.R, color.G, color.B, 1.0)
}

// SetColor sets the color of the panel paddings and content area
func (p *Panel) SetColor(color *math32.Color) *Panel {

	p.paddingColorUni.Set(color.R, color.G, color.B, 1.0)
	p.contentColorUni.Set(color.R, color.G, color.B, 1.0)
	return p
}

// SetColor4 sets the color of the panel paddings and content area
func (p *Panel) SetColor4(color *math32.Color4) *Panel {

	p.paddingColorUni.Set(color.R, color.G, color.B, color.A)
	p.contentColorUni.Set(color.R, color.G, color.B, color.A)
	return p
}

// Color4 returns the current color of the panel content area
func (p *Panel) Color4() math32.Color4 {

	return p.contentColorUni.GetColor4()
}

// SetContentSize sets this panel content size to the specified dimensions.
// The external size of the panel may increase or decrease to acomodate
// the new content size.
func (p *Panel) SetContentSize(width, height float32) {

	// Calculates the new desired external width and height
	eWidth := width +
		p.paddingSizes.Left + p.paddingSizes.Right +
		p.borderSizes.Left + p.borderSizes.Right +
		p.marginSizes.Left + p.marginSizes.Right
	eHeight := height +
		p.paddingSizes.Top + p.paddingSizes.Bottom +
		p.borderSizes.Top + p.borderSizes.Bottom +
		p.marginSizes.Top + p.marginSizes.Bottom
	p.resize(eWidth, eHeight)
}

// SetContentWidth sets this panel content width to the specified dimension in pixels.
// The external size of the panel may increase or decrease to acomodate the new width
func (p *Panel) SetContentWidth(width float32) {

	p.SetContentSize(width, p.content.Height)
}

// SetContentHeight sets this panel content height to the specified dimension in pixels.
// The external size of the panel may increase or decrease to acomodate the new width
func (p *Panel) SetContentHeight(height float32) {

	p.SetContentSize(p.content.Width, height)
}

// MinWidth returns the minimum width of this panel (ContentWidth = 0)
func (p *Panel) MinWidth() float32 {

	return p.paddingSizes.Left + p.paddingSizes.Right +
		p.borderSizes.Left + p.borderSizes.Right +
		p.marginSizes.Left + p.marginSizes.Right
}

// MinHeight returns the minimum height of this panel (ContentHeight = 0)
func (p *Panel) MinHeight() float32 {

	return p.paddingSizes.Top + p.paddingSizes.Bottom +
		p.borderSizes.Top + p.borderSizes.Bottom +
		p.marginSizes.Top + p.marginSizes.Bottom
}

// Add adds a child panel to this one
func (p *Panel) Add(ichild IPanel) *Panel {

	p.Node.Add(ichild)
	node := ichild.GetPanel()
	node.SetParent(p)
	node.SetPositionZ(p.nextChildZ)
	p.nextChildZ += deltaZ
	if p.layout != nil {
		p.layout.Recalc(p)
	}
	p.Dispatch(OnChild, nil)
	return p
}

// Remove removes the specified child from this panel
func (p *Panel) Remove(ichild IPanel) bool {

	res := p.Node.Remove(ichild)
	if res {
		if p.layout != nil {
			p.layout.Recalc(p)
		}
		p.Dispatch(OnChild, nil)
	}
	return res
}

// Bounded returns this panel bounded state
func (p *Panel) Bounded() bool {

	return p.bounded
}

// SetBounded sets this panel bounded state
func (p *Panel) SetBounded(bounded bool) {

	p.bounded = bounded
}

// UpdateMatrixWorld overrides the standard Node version which is called by
// the Engine before rendering the frame.
func (p *Panel) UpdateMatrixWorld() {

	par := p.Parent()
	if par == nil {
		p.updateBounds(nil)
	} else {
		par, ok := par.(*Panel)
		if ok {
			p.updateBounds(par)
		} else {
			p.updateBounds(nil)
		}
	}
	// Update this panel children
	for _, ichild := range p.Children() {
		ichild.UpdateMatrixWorld()
	}
}

// ContainsPosition returns indication if this panel contains
// the specified screen position in pixels.
func (p *Panel) ContainsPosition(x, y float32) bool {

	if x < p.pospix.X || x >= (p.pospix.X+p.width) {
		return false
	}
	if y < p.pospix.Y || y >= (p.pospix.Y+p.height) {
		return false
	}
	return true
}

//// CancelMouse is called by the gui root panel when a mouse event occurs
//// outside of this panel and the panel is not in focus.
//func (p *Panel) CancelMouse(m *Root, mev MouseEvent) {
//
//	if !p.enabled {
//		return
//	}
//	// If OnMouseEnter previously sent, sends OnMouseLeave
//	if p.cursorEnter {
//		p.DispatchPrefix(OnMouseLeave, &mev)
//		p.cursorEnter = false
//	}
//	// If click outside the panel
//	if mev.Button >= 0 {
//		if mev.Action == core.ActionPress {
//			p.DispatchPrefix(OnMouseButtonOut, &mev)
//		}
//	}
//}

// SetEnabled sets the panel enabled state
// A disabled panel do not process key or mouse events.
func (p *Panel) SetEnabled(state bool) {

	p.enabled = state
	p.Dispatch(OnEnable, nil)
}

// Enabled returns the current enabled state of this panel
func (p *Panel) Enabled() bool {

	return p.enabled
}

// SetLayout sets the layout to use to position the children of this panel
// To remove the layout, call this function passing nil as parameter.
func (p *Panel) SetLayout(ilayout ILayout) {

	p.layout = ilayout
	if p.layout != nil {
		p.layout.Recalc(p)
	}
}

// SetLayoutParams sets the layout parameters for this panel
func (p *Panel) SetLayoutParams(params interface{}) {

	p.layoutParams = params
}

// updateBounds is called by UpdateMatrixWorld() and calculates this panel
// bounds considering the bounds of its parent
func (p *Panel) updateBounds(par *Panel) {

	if par == nil {
		p.pospix = p.Position()
		p.xmin = p.pospix.X
		p.ymin = p.pospix.Y
		p.xmax = p.pospix.X + p.width
		p.ymax = p.pospix.Y + p.height
		p.boundsUni.Set(0, 0, 1, 1)
		return
	}
	// If this panel is bounded to its parent, its coordinates are relative
	// to the parent internal content rectangle.
	if p.bounded {
		p.pospix.X = p.Position().X + par.pospix.X + float32(par.marginSizes.Left+par.borderSizes.Left+par.paddingSizes.Left)
		p.pospix.Y = p.Position().Y + par.pospix.Y + float32(par.marginSizes.Top+par.borderSizes.Top+par.paddingSizes.Top)
		p.pospix.Z = p.Position().Z + par.pospix.Z
		// Otherwise its coordinates are relative to the parent outer coordinates.
	} else {
		p.pospix.X = p.Position().X + par.pospix.X
		p.pospix.Y = p.Position().Y + par.pospix.Y
		p.pospix.Z = p.Position().Z + par.pospix.Z
	}
	// Maximum x,y coordinates for this panel
	p.xmin = p.pospix.X
	p.ymin = p.pospix.Y
	p.xmax = p.pospix.X + p.width
	p.ymax = p.pospix.Y + p.height
	if p.bounded {
		// Get the parent minimum X and Y absolute coordinates in pixels
		pxmin := par.xmin + par.marginSizes.Right + par.borderSizes.Right + par.paddingSizes.Right
		pymin := par.ymin + par.marginSizes.Bottom + par.borderSizes.Bottom + par.paddingSizes.Bottom
		// Get the parent maximum X and Y absolute coordinates in pixels
		pxmax := par.xmax - (par.marginSizes.Right + par.borderSizes.Right + par.paddingSizes.Right)
		pymax := par.ymax - (par.marginSizes.Bottom + par.borderSizes.Bottom + par.paddingSizes.Bottom)
		// Update this panel minimum x and y coordinates.
		if p.xmin < pxmin {
			p.xmin = pxmin
		}
		if p.ymin < pymin {
			p.ymin = pymin
		}
		// Update this panel maximum x and y coordinates.
		if p.xmax > pxmax {
			p.xmax = pxmax
		}
		if p.ymax > pymax {
			p.ymax = pymax
		}
	}
	// Set default values for bounds in texture coordinates
	xmintex := float32(0.0)
	ymintex := float32(0.0)
	xmaxtex := float32(1.0)
	ymaxtex := float32(1.0)
	// If this panel is bounded to its parent, calculates the bounds
	// for clipping in texture coordinates
	if p.bounded {
		if p.pospix.X < p.xmin {
			xmintex = (p.xmin - p.pospix.X) / p.width
		}
		if p.pospix.Y < p.ymin {
			ymintex = (p.ymin - p.pospix.Y) / p.height
		}
		if p.pospix.X+p.width > p.xmax {
			xmaxtex = (p.xmax - p.pospix.X) / p.width
		}
		if p.pospix.Y+p.height > p.ymax {
			ymaxtex = (p.ymax - p.pospix.Y) / p.height
		}
	}
	// Sets bounds uniform
	p.boundsUni.Set(xmintex, ymintex, xmaxtex, ymaxtex)
}

// calcWidth calculates the panel external width in pixels
func (p *Panel) calcWidth() float32 {

	return p.content.Width +
		p.paddingSizes.Left + p.paddingSizes.Right +
		p.borderSizes.Left + p.borderSizes.Right +
		p.marginSizes.Left + p.marginSizes.Right
}

// calcHeight calculates the panel external height in pixels
func (p *Panel) calcHeight() float32 {

	return p.content.Height +
		p.paddingSizes.Top + p.paddingSizes.Bottom +
		p.borderSizes.Top + p.borderSizes.Bottom +
		p.marginSizes.Top + p.marginSizes.Bottom
}

// resize tries to set the external size of the panel to the specified
// dimensions and recalculates the size and positions of the internal areas.
// The margins, borders and padding sizes are kept and the content
// area size is adjusted. So if the panel is decreased, its minimum
// size is determined by the margins, borders and paddings.
func (p *Panel) resize(width, height float32) {

	var padding Rect
	var border Rect

	width = math32.Round(width)
	height = math32.Round(height)
	// Adjusts content width
	p.content.Width = width -
		p.marginSizes.Left - p.marginSizes.Right -
		p.borderSizes.Left - p.borderSizes.Right -
		p.paddingSizes.Left - p.paddingSizes.Right
	if p.content.Width < 0 {
		p.content.Width = 0
	}
	// Adjust other area widths
	padding.Width = p.paddingSizes.Left + p.content.Width + p.paddingSizes.Right
	border.Width = p.borderSizes.Left + padding.Width + p.borderSizes.Right

	// Adjusts content height
	p.content.Height = height -
		p.marginSizes.Top - p.marginSizes.Bottom -
		p.borderSizes.Top - p.borderSizes.Bottom -
		p.paddingSizes.Top - p.paddingSizes.Bottom
	if p.content.Height < 0 {
		p.content.Height = 0
	}
	// Adjust other area heights
	padding.Height = p.paddingSizes.Top + p.content.Height + p.paddingSizes.Bottom
	border.Height = p.borderSizes.Top + padding.Height + p.borderSizes.Bottom

	// Sets area positions
	border.X = p.marginSizes.Left
	border.Y = p.marginSizes.Top
	padding.X = border.X + p.borderSizes.Left
	padding.Y = border.Y + p.borderSizes.Top
	p.content.X = padding.X + p.paddingSizes.Left
	p.content.Y = padding.Y + p.paddingSizes.Top

	// Sets final panel dimensions (may be different from requested dimensions)
	p.width = p.marginSizes.Left + border.Width + p.marginSizes.Right
	p.height = p.marginSizes.Top + border.Height + p.marginSizes.Bottom

	// Updates border uniform in texture coordinates (0,0 -> 1,1)
	p.borderUni.Set(
		float32(border.X)/float32(p.width),
		float32(border.Y)/float32(p.height),
		float32(border.Width)/float32(p.width),
		float32(border.Height)/float32(p.height),
	)
	// Updates padding uniform in texture coordinates (0,0 -> 1,1)
	p.paddingUni.Set(
		float32(padding.X)/float32(p.width),
		float32(padding.Y)/float32(p.height),
		float32(padding.Width)/float32(p.width),
		float32(padding.Height)/float32(p.height),
	)
	// Updates content uniform in texture coordinates (0,0 -> 1,1)
	p.contentUni.Set(
		float32(p.content.X)/float32(p.width),
		float32(p.content.Y)/float32(p.height),
		float32(p.content.Width)/float32(p.width),
		float32(p.content.Height)/float32(p.height),
	)
	// Update layout and dispatch event
	if p.layout != nil {
		p.layout.Recalc(p)
	}
	p.Dispatch(OnResize, nil)
}

// RenderSetup is called by the Engine before drawing the object
func (p *Panel) RenderSetup(gl *gls.GLS, rinfo *core.RenderInfo) {

	// Get the current viewport width and height
	_, _, width, height := gl.GetViewport()
	fwidth := float32(width)
	fheight := float32(height)

	// Scale the quad for the viewport so it has fixed dimensions in pixels.
	fw := float32(p.width) / fwidth
	fh := float32(p.height) / fheight
	var scale math32.Vector3
	scale.Set(2*fw, 2*fh, 1)

	// Convert absolute position in pixel coordinates from the top/left to
	// standard OpenGL clip coordinates of the quad center
	var posclip math32.Vector3
	posclip.X = (p.pospix.X - fwidth/2) / (fwidth / 2)
	posclip.Y = -(p.pospix.Y - fheight/2) / (fheight / 2)
	posclip.Z = p.pospix.Z
	//log.Debug("panel posclip:%v\n", posclip)

	// Sets the model matrix uniform
	var mm math32.Matrix4
	var quat math32.Quaternion
	quat.SetIdentity()
	mm.Compose(&posclip, &quat, &scale)
	p.modelMatrixUni.SetMatrix4(&mm)

	// Transfer uniforms
	p.borderColorUni.Transfer(gl)
	p.paddingColorUni.Transfer(gl)
	p.contentColorUni.Transfer(gl)
	p.boundsUni.Transfer(gl)
	p.borderUni.Transfer(gl)
	p.paddingUni.Transfer(gl)
	p.contentUni.Transfer(gl)
	p.modelMatrixUni.Transfer(gl)
}
