// Copyright 2014-2019 Ulrich Kunitz. 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"
	"io/ioutil"
	"math/rand"
	"testing"

	"github.com/ulikunitz/xz/internal/randtxt"
)

var testString = `LZMA decoder test example
=========================
! LZMA ! Decoder ! TEST !
=========================
! TEST ! LZMA ! Decoder !
=========================
---- Test Line 1 --------
=========================
---- Test Line 2 --------
=========================
=== End of test file ====
=========================
`

func cycle(t *testing.T, n int) {
	t.Logf("cycle(t,%d)", n)
	if n > len(testString) {
		t.Fatalf("cycle: n=%d larger than len(testString)=%d", n,
			len(testString))
	}
	const dictCap = MinDictCap
	m, err := newHashTable(dictCap, 4)
	if err != nil {
		t.Fatal(err)
	}
	encoderDict, err := newEncoderDict(dictCap, dictCap+1024, m)
	if err != nil {
		t.Fatal(err)
	}
	props := Properties{2, 0, 2}
	if err := props.verify(); err != nil {
		t.Fatalf("properties error %s", err)
	}
	state := newState(props)
	var buf bytes.Buffer
	w, err := newEncoder(&buf, state, encoderDict, eosMarker)
	if err != nil {
		t.Fatalf("newEncoder error %s", err)
	}
	orig := []byte(testString)[:n]
	t.Logf("len(orig) %d", len(orig))
	k, err := w.Write(orig)
	if err != nil {
		t.Fatalf("w.Write error %s", err)
	}
	if k != len(orig) {
		t.Fatalf("w.Write returned %d; want %d", k, len(orig))
	}
	if err = w.Close(); err != nil {
		t.Fatalf("w.Close error %s", err)
	}
	t.Logf("buf.Len() %d len(orig) %d", buf.Len(), len(orig))
	decoderDict, err := newDecoderDict(dictCap)
	if err != nil {
		t.Fatalf("newDecoderDict error %s", err)
	}
	state.Reset()
	r, err := newDecoder(&buf, state, decoderDict, -1)
	if err != nil {
		t.Fatalf("newDecoder error %s", err)
	}
	decoded, err := ioutil.ReadAll(r)
	if err != nil {
		t.Fatalf("ReadAll(lr) error %s", err)
	}
	t.Logf("decoded: %s", decoded)
	if len(orig) != len(decoded) {
		t.Fatalf("length decoded is %d; want %d", len(decoded),
			len(orig))
	}
	if !bytes.Equal(orig, decoded) {
		t.Fatalf("decoded file differs from original")
	}
}

func TestEncoderCycle1(t *testing.T) {
	cycle(t, len(testString))
}

func TestEncoderCycle2(t *testing.T) {
	buf := new(bytes.Buffer)
	const txtlen = 50000
	io.CopyN(buf, randtxt.NewReader(rand.NewSource(42)), txtlen)
	txt := buf.String()
	buf.Reset()
	const dictCap = MinDictCap
	m, err := newHashTable(dictCap, 4)
	if err != nil {
		t.Fatal(err)
	}
	encoderDict, err := newEncoderDict(dictCap, dictCap+1024, m)
	if err != nil {
		t.Fatal(err)
	}
	props := Properties{3, 0, 2}
	if err := props.verify(); err != nil {
		t.Fatalf("properties error %s", err)
	}
	state := newState(props)
	lbw := &LimitedByteWriter{BW: buf, N: 100}
	w, err := newEncoder(lbw, state, encoderDict, 0)
	if err != nil {
		t.Fatalf("NewEncoder error %s", err)
	}
	_, err = io.WriteString(w, txt)
	if err != nil && err != ErrLimit {
		t.Fatalf("WriteString error %s", err)
	}
	if err = w.Close(); err != nil {
		t.Fatalf("w.Close error %s", err)
	}
	n := w.Compressed()
	txt = txt[:n]
	decoderDict, err := newDecoderDict(dictCap)
	if err != nil {
		t.Fatalf("NewDecoderDict error %s", err)
	}
	state.Reset()
	r, err := newDecoder(buf, state, decoderDict, n)
	if err != nil {
		t.Fatalf("NewDecoder error %s", err)
	}
	out := new(bytes.Buffer)
	if _, err = io.Copy(out, r); err != nil {
		t.Fatalf("decompress copy error %s", err)
	}
	got := out.String()
	t.Logf("%s", got)
	if len(got) != int(n) {
		t.Fatalf("len(got) %d; want %d", len(got), n)
	}
	if got != txt {
		t.Fatalf("got and txt differ")
	}
}
