package nori

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"image/color"
	"io"
)

var end = binary.LittleEndian

type Reader struct {
	nori *Nori

	r *bufio.Reader
}

func NewReader(rd io.Reader) *Reader {
	o := &Reader{
		nori: &Nori{},
		r:    bufio.NewReader(rd),
	}
	return o
}

func (n *Reader) Decode() (*Nori, error) {
	if _, err := io.ReadFull(n, n.nori.lastSignature[:]); err != nil {
		return n.nori, err
	}
	if sig, target := string(n.nori.lastSignature[:]), "NORI"; sig != target {
		return n.nori, fmt.Errorf("expected header %s, got %s", target, sig)
	}
	if err := binary.Read(n, end, &n.nori.Version); err != nil {
		return n.nori, err
	}
	if err := binary.Read(n, end, n.nori.Params[:]); err != nil {
		return n.nori, err
	}
	if err := binary.Read(n, end, &n.nori.AnimationCount); err != nil {
		return n.nori, err
	}
	if err := binary.Read(n, end, &n.nori.SizeNoGawi); err != nil {
		return n.nori, err
	}
	if err := binary.Read(n, end, &n.nori.TotalSize); err != nil {
		return n.nori, err
	}
	if err := n.decodeGawi(); err != nil {
		return n.nori, err
	}
	return n.nori, nil
}

func (n *Reader) decodeGawi() error {
	g := &GawiSection{}
	n.nori.Gawi = g
	if _, err := io.ReadFull(n, n.nori.lastSignature[:]); err != nil {
		return err
	}
	if sig, target := string(n.nori.lastSignature[:]), "GAWI"; sig != target {
		return fmt.Errorf("bad sig: want %s, got %s", target, sig)
	}
	if err := binary.Read(n, end, &g.Version); err != nil {
		return err
	}
	if g.Version != 300 {
		return fmt.Errorf("bad ver: want 300, got %d", g.Version)
	}
	if err := binary.Read(n, end, &g.Bpp); err != nil {
		return err
	}
	good := false
	for _, v := range [3]uint32{8, 16, 24} {
		if g.Bpp == v {
			good = true
		}
	}
	if !good {
		return fmt.Errorf("bad bpp: %d", g.Bpp)
	}
	var container uint32
	if err := binary.Read(n, end, &container); err != nil {
		return err
	}
	g.IsCompressed = (container == 1)
	if err := binary.Read(n, end, &container); err != nil {
		return err
	}
	g.HasPalette = (container > 0)

	if err := binary.Read(n, end, g.Params[:]); err != nil {
		return err
	}
	if err := binary.Read(n, end, &g.BmpCount); err != nil {
		return err
	}
	if err := binary.Read(n, end, &g.Size); err != nil {
		return err
	}
	if g.HasPalette {
		if err := n.decodePalette(); err != nil {
			return err
		}
	}
	if _, err := n.r.Discard(4 * int(g.BmpCount)); err != nil {
		return err
	}
	g.Images = make([]*Image, int(g.BmpCount))
	for i := range g.Images {
		g.Images[i] = &Image{Bpp: g.Bpp}
		if err := n.decodeImage(g.Images[i]); err != nil {
			return err
		}
	}
	if _, err := n.r.Discard(4 * int(n.nori.AnimationCount)); err != nil {
		return err
	}
	n.nori.Animations = make([]*Animation, n.nori.AnimationCount)
	for i := range n.nori.Animations {
		n.nori.Animations[i] = &Animation{}
		if err := n.nori.Animations[i].Decode(n.r, int(n.nori.Version)); err != nil {
			return err
		}
	}
	return nil
}

func (n *Reader) decodePalette() error {
	if _, err := io.ReadFull(n, n.nori.lastSignature[:]); err != nil {
		return err
	}
	if sig, target := string(n.nori.lastSignature[:]), "PAL_"; sig != target {
		return fmt.Errorf("bad sig: want %s, got %s", target, sig)
	}
	cp := &PaletteSection{
		Palette: [256]color.Color{},
	}
	n.nori.Gawi.Palette = cp
	if err := binary.Read(n, end, &cp.Version); err != nil {
		return err
	}
	if err := binary.Read(n, end, cp.Params[:]); err != nil {
		return err
	}
	var cast uint32
	if err := binary.Read(n, end, &cast); err != nil {
		return err
	}
	cp.Divided = (cast > 0)
	if err := binary.Read(n, end, &cp.DataLength); err != nil {
		return err
	}
	for i := 0; i < 256; i++ {
		rgb := [3]byte{}
		_, err := io.ReadFull(n, rgb[:])
		if err != nil {
			return err
		}
		alpha := uint8(0xff)
		if rgb[0] == 0x00 && rgb[1] == 0xff && rgb[2] == 0x00 {
			alpha = 0x00
		}
		if rgb[0] == 0xff && rgb[2] == 0xff {
			alpha = 0x00
		}
		cp.Palette[i] = &color.RGBA{
			rgb[0], rgb[1], rgb[2],
			alpha,
		}
	}

	if cp.Divided {
		if err := binary.Read(n, end, cp.MainBound[:]); err != nil {
			return err
		}
	}
	return nil
}
func (n *Reader) decodeImage(img *Image) error {
	if err := binary.Read(n, end, &img.Count); err != nil {
		return err
	}
	if img.Count != 1 {
		return fmt.Errorf("img count should be 1")
	}

	if err := binary.Read(n, end, &img.Size); err != nil {
		return err
	}

	if err := binary.Read(n, end, &img.Width); err != nil {
		return err
	}

	if err := binary.Read(n, end, &img.Height); err != nil {
		return err
	}

	if err := binary.Read(n, end, &img.Delay); err != nil {
		return err
	}

	if err := binary.Read(n, end, &img.OffsetX); err != nil {
		return err
	}

	if err := binary.Read(n, end, &img.OffsetY); err != nil {
		return err
	}
	if err := img.Decode(n.r, n.nori.Gawi.Palette); err != nil {
		return err
	}
	return nil
}

func (n *Reader) Read(b []byte) (int, error) {
	return n.r.Read(b)
}
