package objloader

import (
	"os"
	"zworld/library/zos"
	"zworld/plugin/math/vec3"
)

type FByteView = zos.FByteView
type FVertex struct {
	pos int32
	nor int32
	tex int32
}
type FMesh struct {
	Positions []vec3.T
	Normals   []vec3.T
	TexCoords []vec3.T
	Faces     [][]FVertex
	Indices   []uint16
}
type FObjLoader struct {
	reader *zos.FReader
	mesh   *FMesh
}

func (l *FObjLoader) GetTexCoord(bv *FByteView) error {
	x := bv.GetFloat()
	y := bv.GetFloat()
	if err := bv.Err(); err != nil {
		return err
	}
	w := bv.GetFloat()
	if bv.Err() != nil {
		w = 0
	}
	v := vec3.T{X: x, Y: y, Z: w}
	l.mesh.TexCoords = append(l.mesh.TexCoords, v)
	return nil
}
func (l *FObjLoader) GetPosition(bv *FByteView) error {
	x := bv.GetFloat()
	y := bv.GetFloat()
	z := bv.GetFloat()
	if err := bv.Err(); err != nil {
		return err
	}
	v := vec3.T{X: x, Y: y, Z: z}
	l.mesh.Positions = append(l.mesh.Positions, v)
	return nil
}
func (l *FObjLoader) GetNormal(bv *FByteView) error {
	x := bv.GetFloat()
	y := bv.GetFloat()
	z := bv.GetFloat()
	if err := bv.Err(); err != nil {
		return err
	}
	v := vec3.T{X: x, Y: y, Z: z}
	l.mesh.Normals = append(l.mesh.Normals, v)
	return nil
}
func (l *FObjLoader) GetFace(b *FByteView) error {
	var faces []FVertex
	for i := 0; i < 3; i++ {
		line := b.GetToken()
		bv := zos.NewByteView(line)
		bv.SetSign('/')
		v := FVertex{}
		v.pos = bv.GetInt()
		if err := bv.Err(); err != nil {
			return err
		}
		v.nor = bv.GetInt()
		v.tex = bv.GetInt()
		faces = append(faces, v)
	}
	l.mesh.Faces = append(l.mesh.Faces, faces)
	return nil
}
func (l *FObjLoader) Parse() error {
	for line, err := l.reader.ReadLine(); err == nil; line, err = l.reader.ReadLine() {
		bv := zos.NewByteView(line)
		bv.SkipWhitespace()
		token := string(bv.ReadTokenBySpace())
		//zlog.Infof("line {}", string(line))
		switch token {
		case "v":
			err = l.GetPosition(bv)
		case "vt":
			err = l.GetTexCoord(bv)
		case "vn":
			err = l.GetNormal(bv)
		case "f":
			err = l.GetFace(bv)
		}
		if err != nil {
			return err
		}
	}
	return nil
}
func LoadObj(filename string) (err error, mesh *FMesh) {
	file, err := os.Open(filename)
	if err != nil {
		return err, nil
	}
	l := &FObjLoader{reader: zos.NewReader(file), mesh: &FMesh{}}
	err = l.Parse()
	return err, l.mesh
}
