// Copyright (c) 2010, Andrei Vieru. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package lzma

import (
	"bytes"
	"io"
	"log"
	"testing"
)

func pipe(t *testing.T, efunc func(io.WriteCloser), dfunc func(io.ReadCloser), size int64) {
	level := 3
	pr, pw := io.Pipe()
	go func() {
		defer pw.Close()
		ze := NewWriterSizeLevel(pw, size, level)
		defer ze.Close()
		efunc(ze)
	}()
	defer pr.Close()
	zd := NewReader(pr)
	defer zd.Close()
	dfunc(zd)
}

func testEmpty(t *testing.T, sizeIsKnown bool) {
	size := int64(-1)
	if sizeIsKnown == true {
		size = 0
	}
	pipe(t,
		func(w io.WriteCloser) {},
		func(r io.ReadCloser) {
			b, err := io.ReadAll(r)
			if err != nil {
				t.Fatalf("%v", err)
			}
			if len(b) != 0 {
				t.Fatalf("did not read an empty slice")
			}
		},
		size)
}

func TestEmpty1(t *testing.T) {
	testEmpty(t, true)
}

func TestEmpty2(t *testing.T) {
	testEmpty(t, false)
}

func testBoth(t *testing.T, sizeIsKnown bool) {
	size := int64(-1)
	payload := []byte("lzmalzmalzma")
	if sizeIsKnown == true {
		size = int64(len(payload))
	}
	pipe(t,
		func(w io.WriteCloser) {
			n, err := w.Write(payload)
			if err != nil {
				t.Fatalf("%v", err)
			}
			if n != len(payload) {
				t.Fatalf("wrote %d bytes, want %d bytes", n, len(payload))
			}
		},
		func(r io.ReadCloser) {
			b, err := io.ReadAll(r)
			if err != nil {
				t.Fatalf("%v", err)
			}
			if string(b) != string(payload) {
				t.Fatalf("payload is %s, want %s", string(b), string(payload))
			}
		},
		size)
}

func TestBoth1(t *testing.T) {
	testBoth(t, true)
}

func TestBoth2(t *testing.T) {
	testBoth(t, false)
}

func TestEncoder(t *testing.T) {
	b := new(bytes.Buffer)
	for _, tt := range lzmaTests {
		if tt.err == nil {
			pr, pw := io.Pipe()
			defer pr.Close()
			in := bytes.NewBuffer([]byte(tt.raw))
			size := int64(-1)
			if tt.size == true {
				size = int64(len([]byte(tt.raw)))
			}
			go func() {
				defer pw.Close()
				w := NewWriterSizeLevel(pw, size, tt.level)
				defer w.Close()
				_, err := io.Copy(w, in)
				if err != nil {
					t.Errorf("%v", err)
				}
			}()
			b.Reset()
			_, err := io.Copy(b, pr)
			if err != nil {
				t.Errorf("%v", err)
			}
			res := b.Bytes()
			if bytes.Equal(res, tt.lzma) == false {
				t.Errorf("%s: got %d-byte %q, want %d-byte %q", tt.descr, len(res), string(res), len(tt.lzma), string(tt.lzma))
			}
		}
	}
}

func BenchmarkEncoder(b *testing.B) {

	buf := new(bytes.Buffer)
	start := make(chan bool)
	for b.Loop() {
		in := bytes.NewBuffer(bench.raw)
		pr, pw := io.Pipe()
		defer pr.Close()
		go func() {
			defer pw.Close()
			w := NewWriterLevel(pw, bench.level)
			defer w.Close()
			start <- true
			n, err := io.Copy(w, in)
			if err != nil {
				log.Fatalf("%v", err)
			}
			b.SetBytes(n)
		}()
		buf.Reset()
		<-start // wait for encoder to start encoding before starting the timer
		b.StartTimer()
		_, err := io.Copy(buf, pr)
		b.StopTimer()
		if err != nil {
			log.Fatalf("%v", err)
		}
	}
	if bytes.Equal(buf.Bytes(), bench.lzma) == false { // check only once, not at every iteration
		log.Fatalf("%s: got %d-byte %q, want %d-byte %q", bench.descr, len(buf.Bytes()), buf.String(), len(bench.lzma), string(bench.lzma))
	}
}
