// go-qrcode
// Copyright 2014 Tom Harwood

package qrcode

import (
	"bytes"
	"flag"
	"fmt"
	"math/rand"
	"os/exec"
	"strings"
	"testing"
)

// These tests use zbarimg to decode generated QR Codes to ensure they are
// readable. sudo apt-get install zbar-tools, or download from
// http://zbar.sourceforge.net.
//
// By default these tests are disabled to avoid a dependency on zbarimg if
// you're not running the tests. Use the -test-decode flag (go test
// -test-decode) to enable.

var testDecode *bool = flag.Bool("test-decode",
	false,
	"Enable decode tests. Requires zbarimg installed.")

var testDecodeFuzz *bool = flag.Bool("test-decode-fuzz",
	false,
	"Enable decode fuzz tests. Requires zbarimg installed.")

func TestDecodeBasic(t *testing.T) {
	if !*testDecode {
		t.Skip("Decode tests not enabled")
	}

	tests := []struct {
		content        string
		numRepetitions int
		level          RecoveryLevel
	}{
		{
			"A",
			1,
			Low,
		},
		{
			"A",
			1,
			Medium,
		},
		{
			"A",
			1,
			High,
		},
		{
			"A",
			1,
			Highest,
		},
		{
			"01234567",
			1,
			Medium,
		},
	}

	for _, test := range tests {
		content := strings.Repeat(test.content, test.numRepetitions)

		q, err := New(content, test.level)
		if err != nil {
			t.Error(err.Error())
		}

		err = zbarimgCheck(q)

		if err != nil {
			t.Error(err.Error())
		}
	}
}

func TestDecodeAllVersionLevels(t *testing.T) {
	if !*testDecode {
		t.Skip("Decode tests not enabled")
	}

	for version := 1; version <= 40; version++ {
		for _, level := range []RecoveryLevel{Low, Medium, High, Highest} {
			t.Logf("Version=%d Level=%d",
				version,
				level)

			q, err := NewWithForcedVersion(
				fmt.Sprintf("v-%d l-%d", version, level), version, level)
			if err != nil {
				t.Fatal(err.Error())
				return
			}

			err = zbarimgCheck(q)

			if err != nil {
				t.Errorf("Version=%d Level=%d, err=%s, expected success",
					version,
					level,
					err.Error())
				continue
			}
		}
	}
}

func TestDecodeAllCharacters(t *testing.T) {
	if !*testDecode {
		t.Skip("Decode tests not enabled")
	}

	var content string

	// zbarimg has trouble with null bytes, hence start from ASCII 1.
	for i := 1; i < 256; i++ {
		content += string(i)
	}

	q, err := New(content, Low)
	if err != nil {
		t.Error(err.Error())
	}

	err = zbarimgCheck(q)

	if err != nil {
		t.Error(err.Error())
	}
}

func TestDecodeFuzz(t *testing.T) {
	if !*testDecodeFuzz {
		t.Skip("Decode fuzz tests not enabled")
	}

	r := rand.New(rand.NewSource(0))

	const iterations int = 32
	const maxLength int = 128

	for i := 0; i < iterations; i++ {
		len := r.Intn(maxLength-1) + 1

		var content string
		for j := 0; j < len; j++ {
			// zbarimg seems to have trouble with special characters, test printable
			// characters only for now.
			content += string(32 + r.Intn(94))
		}

		for _, level := range []RecoveryLevel{Low, Medium, High, Highest} {
			q, err := New(content, level)
			if err != nil {
				t.Error(err.Error())
			}

			err = zbarimgCheck(q)

			if err != nil {
				t.Error(err.Error())
			}
		}
	}
}

func zbarimgCheck(q *QRCode) error {
	s, err := zbarimgDecode(q)
	if err != nil {
		return err
	}

	if s != q.Content {
		q.WriteFile(256, fmt.Sprintf("%x.png", q.Content))
		return fmt.Errorf("got '%s' (%x) expected '%s' (%x)", s, s, q.Content, q.Content)
	}

	return nil
}

func zbarimgDecode(q *QRCode) (string, error) {
	var png []byte

	// 512x512px
	png, err := q.PNG(512)
	if err != nil {
		return "", err
	}

	cmd := exec.Command("zbarimg", "--quiet", "-Sdisable",
		"-Sqrcode.enable", "-")

	var out bytes.Buffer

	cmd.Stdin = bytes.NewBuffer(png)
	cmd.Stdout = &out

	err = cmd.Run()

	if err != nil {
		return "", err
	}

	return strings.TrimSuffix(strings.TrimPrefix(out.String(), "QR-Code:"), "\n"), nil
}

func BenchmarkDecodeTest(b *testing.B) {
	if !*testDecode {
		b.Skip("Decode benchmarks not enabled")
	}

	for n := 0; n < b.N; n++ {
		q, err := New("content", Medium)
		if err != nil {
			b.Error(err.Error())
		}

		err = zbarimgCheck(q)

		if err != nil {
			b.Error(err.Error())
		}
	}
}
