package d2dc6

import (
	"github.com/OpenDiablo2/OpenDiablo2/d2common"
)

// DC6 represents a DC6 file.
type DC6 struct {
	Version            int32
	Flags              uint32
	Encoding           uint32
	Termination        []byte // 4 bytes
	Directions         uint32
	FramesPerDirection uint32
	FramePointers      []uint32    // size is Directions*FramesPerDirection
	Frames             []*DC6Frame // size is Directions*FramesPerDirection
}

// Load uses restruct to read the binary dc6 data into structs then parses image data from the frame data.
func Load(data []byte) (*DC6, error) {
	const (
		terminationSize = 4
		terminatorSize  = 3
	)

	r := d2common.CreateStreamReader(data)

	var dc DC6
	dc.Version = r.GetInt32()
	dc.Flags = r.GetUInt32()
	dc.Encoding = r.GetUInt32()
	dc.Termination = r.ReadBytes(terminationSize)
	dc.Directions = r.GetUInt32()
	dc.FramesPerDirection = r.GetUInt32()

	frameCount := int(dc.Directions * dc.FramesPerDirection)

	dc.FramePointers = make([]uint32, frameCount)
	for i := 0; i < frameCount; i++ {
		dc.FramePointers[i] = r.GetUInt32()
	}

	dc.Frames = make([]*DC6Frame, frameCount)
	for i := 0; i < frameCount; i++ {
		frame := &DC6Frame{
			Flipped:   r.GetUInt32(),
			Width:     r.GetUInt32(),
			Height:    r.GetUInt32(),
			OffsetX:   r.GetInt32(),
			OffsetY:   r.GetInt32(),
			Unknown:   r.GetUInt32(),
			NextBlock: r.GetUInt32(),
			Length:    r.GetUInt32(),
		}
		frame.FrameData = r.ReadBytes(int(frame.Length))
		frame.Terminator = r.ReadBytes(terminatorSize)
		dc.Frames[i] = frame
	}

	return &dc, nil
}

// DecodeFrame decodes the given frame to an indexed color texture
func (d *DC6) DecodeFrame(frameIndex int) []byte {
	frame := d.Frames[frameIndex]

	indexData := make([]byte, frame.Width*frame.Height)
	x := 0
	y := int(frame.Height) - 1
	offset := 0

	for {
		b := int(frame.FrameData[offset])
		offset++

		if b == 0x80 {
			if y == 0 {
				break
			}

			y--

			x = 0
		} else if b&0x80 > 0 {
			transparentPixels := b & 0x7f
			x += transparentPixels
		} else {
			for i := 0; i < b; i++ {
				indexData[x+y*int(frame.Width)+i] = frame.FrameData[offset]
				offset++
			}

			x += b
		}
	}

	return indexData
}
