package d2ui

import (
	"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
	"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
	"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
)

// static check that UIFrame implements Widget
var _ Widget = &UIFrame{}

type frameOrientation = int

// Frame orientations
const (
	FrameLeft frameOrientation = iota
	FrameRight
)

// UIFrame is a representation of a ui panel that occupies the left or right half of the screen
// when it is visible.
type UIFrame struct {
	*BaseWidget
	frame            *Sprite
	frameOrientation frameOrientation
	*d2util.Logger
}

// frame indices into dc6 images for panels
const (
	leftFrameTopLeft = iota
	leftFrameTopRight
	leftFrameMiddleLeft
	leftFrameBottomLeft
	leftFrameBottomRight
	rightFrameTopLeft
	rightFrameTopRight
	rightFrameMiddleRight
	rightFrameBottomRight
	rightFrameBottomLeft
)

// NewUIFrame creates a new Frame instance
func (ui *UIManager) NewUIFrame(frameOrientation frameOrientation) *UIFrame {
	var originX, originY = 0, 0

	switch frameOrientation {
	case FrameLeft:
		originX = 0
		originY = 0
	case FrameRight:
		originX = 400
		originY = 0
	}

	base := NewBaseWidget(ui)
	base.SetVisible(false)
	base.SetPosition(originX, originY)

	frame := &UIFrame{
		BaseWidget:       base,
		frameOrientation: frameOrientation,
		Logger:           ui.Logger,
	}

	sprite, err := ui.NewSprite(d2resource.Frame, d2resource.PaletteSky)
	if err != nil {
		frame.Error(err.Error())
	}

	frame.frame = sprite
	frame.calculateSize()

	ui.addWidget(frame)

	return frame
}

func (u *UIFrame) calculateSize() {
	var framesWidth, framesHeight []int

	if u.frameOrientation == FrameLeft {
		framesWidth = []int{
			leftFrameTopLeft,
			leftFrameTopRight,
		}
		framesHeight = []int{
			leftFrameTopLeft,
			leftFrameMiddleLeft,
			leftFrameBottomLeft,
		}
	} else if u.frameOrientation == FrameRight {
		framesWidth = []int{
			rightFrameTopLeft,
			rightFrameTopRight,
		}
		framesHeight = []int{
			rightFrameTopRight,
			rightFrameMiddleRight,
			rightFrameBottomRight,
		}
	}

	for i := range framesWidth {
		w, _, err := u.frame.GetFrameSize(framesWidth[i])
		if err != nil {
			u.Error(err.Error())
		}

		u.width += w
	}

	for i := range framesHeight {
		_, h, err := u.frame.GetFrameSize(framesHeight[i])
		if err != nil {
			u.Error(err.Error())
		}

		u.height += h
	}
}

// Render the frame to the target surface
func (u *UIFrame) Render(target d2interface.Surface) {
	switch u.frameOrientation {
	case FrameLeft:
		if err := u.renderLeft(target); err != nil {
			u.Error("Render error" + err.Error())
		}
	case FrameRight:
		if err := u.renderRight(target); err != nil {
			u.Error("Render error" + err.Error())
		}
	}
}

func (u *UIFrame) renderLeft(target d2interface.Surface) error {
	// the frame pieces we are interested in.
	framePieces := []int{
		leftFrameTopLeft,
		leftFrameTopRight,
		leftFrameMiddleLeft,
		leftFrameBottomLeft,
		leftFrameBottomRight,
	}

	// the frame coordinates
	coord := make(map[int]*struct{ x, y int })

	startX, startY := u.GetPosition()
	currentX, currentY := startX, startY

	// first determine the coordinates for each frame
	// the order that we check is important
	for _, piece := range framePieces {
		width, height, err := u.frame.GetFrameSize(piece)
		if err != nil {
			return err
		}

		c := &struct{ x, y int }{}

		switch piece {
		case leftFrameTopLeft:
			c.x, c.y = currentX, currentY+height
			currentX, currentY = currentX+width, currentY+height
		case leftFrameTopRight:
			c.x, c.y = currentX, startY+height
			currentX = startX
		case leftFrameMiddleLeft:
			c.x, c.y = currentX, currentY+height
			currentY += height
		case leftFrameBottomLeft:
			c.x, c.y = currentX, currentY+height
			currentX += width
		case leftFrameBottomRight:
			c.x, c.y = currentX, currentY+height
		}

		coord[piece] = c
	}

	// now render the pieces with the coordinates
	for idx, c := range coord {
		err := u.renderFramePiece(target, c.x, c.y, idx)
		if err != nil {
			return err
		}
	}

	return nil
}

func (u *UIFrame) renderRight(target d2interface.Surface) error {
	// the frame pieces we are interested in.
	framePieces := []int{
		rightFrameTopLeft,
		rightFrameTopRight,
		rightFrameMiddleRight,
		rightFrameBottomRight,
		rightFrameBottomLeft,
	}

	// the frame coordinates
	coord := make(map[int]*struct{ x, y int })

	startX, startY := u.GetPosition()
	currentX, currentY := startX, startY

	// first determine the coordinates for each frame
	// the order that we check is important
	for _, piece := range framePieces {
		width, height, err := u.frame.GetFrameSize(piece)
		if err != nil {
			return err
		}

		c := &struct{ x, y int }{}

		switch piece {
		case rightFrameTopLeft:
			c.x, c.y = currentX, currentY+height
			currentX += width
		case rightFrameTopRight:
			c.x, c.y = currentX, currentY+height
			currentX += width
			currentY += height
		case rightFrameMiddleRight:
			c.x, c.y = currentX-width, currentY+height
			currentY += height
		case rightFrameBottomRight:
			c.x, c.y = currentX-width, currentY+height
			currentX -= width
		case rightFrameBottomLeft:
			c.x, c.y = currentX-width, currentY+height
			currentX += width
		}

		coord[piece] = c
	}

	// now render the pieces with the coordinates
	for idx, c := range coord {
		err := u.renderFramePiece(target, c.x, c.y, idx)
		if err != nil {
			return err
		}
	}

	return nil
}

// GetFrameBounds returns the maximum width and height of all frames in sprite.
func (u *UIFrame) GetFrameBounds() (width, height int) {
	return u.frame.GetFrameBounds()
}

// GetFrameCount returns the number of frames in the sprite
func (u *UIFrame) GetFrameCount() int {
	return u.frame.GetFrameCount()
}

func (u *UIFrame) renderFramePiece(sfc d2interface.Surface, x, y, idx int) error {
	if err := u.frame.SetCurrentFrame(idx); err != nil {
		return err
	}

	u.frame.SetPosition(x, y)

	u.frame.Render(sfc)

	return nil
}

// Advance is a no-op
func (u *UIFrame) Advance(elapsed float64) error {
	return nil
}
