/*
 * DudelDu
 *
 * Copyright 2016 Matthias Ladkau. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the MIT
 * License, If a copy of the MIT License was not distributed with this
 * file, You can obtain one at https://opensource.org/licenses/MIT.
 */

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"testing"

	"devt.de/krotik/common/fileutil"
	"devt.de/krotik/common/testutil"
	"devt.de/krotik/dudeldu"
	"devt.de/krotik/dudeldu/playlist"
)

const testFilePlaylist = `
/*
Test comment
*/
{
	"/testpath" : [
		{
			"artist" : "artist1",  // 1234
			"title"  : "test1",
			"path"   : "playlisttest/test1.mp3"
		},
		{
			"artist" : "artist2",
			"title"  : "test2",
			"path"   : "playlisttest/test2.mp4"
		},
		{
			"artist" : "artist3",
			"title"  : "test3",
			"path"   : "playlisttest/test3.mp3"
		}
	]
}`

const pdir = "playlisttest"

type TestDebugLogger struct {
	DebugOutput bool
	LogPrint    func(v ...interface{})
}

func (ds *TestDebugLogger) IsDebugOutputEnabled() bool {
	return ds.DebugOutput
}

func (ds *TestDebugLogger) PrintDebug(v ...interface{}) {
	if ds.DebugOutput {
		ds.LogPrint(v...)
	}
}

func TestRequestHandlerFilePlaylist(t *testing.T) {

	// Collect the print output

	var out bytes.Buffer

	debugLogger := &TestDebugLogger{true, func(v ...interface{}) {
		out.WriteString(fmt.Sprint(v...))
		out.WriteString("\n")
	}}

	os.Mkdir(pdir, 0770)
	defer func() {
		os.RemoveAll(pdir)
	}()

	ioutil.WriteFile(pdir+"/test.dpl", []byte(testFilePlaylist), 0644)
	ioutil.WriteFile(pdir+"/test1.mp3", []byte("abcdefgh"), 0644)
	ioutil.WriteFile(pdir+"/test2.mp4", []byte("12345"), 0644)
	ioutil.WriteFile(pdir+"/test3.mp3", []byte("???!!!&&&$$$"), 0644)

	fac, err := playlist.NewFilePlaylistFactory(pdir+"/test.dpl", "")
	if err != nil {
		t.Error(err)
		return
	}

	drh := dudeldu.NewDefaultRequestHandler(fac, false, false, "")
	drh.SetDebugLogger(debugLogger)
	testConn := &testutil.ErrorTestingConnection{}
	dudeldu.MetaDataInterval = 5
	playlist.FrameSize = 5

	drh.ServeRequest(testConn, "/testpath", true, 2, "")

	if testConn.Out.String() != ("ICY 200 OK\r\n" +
		"Content-Type: audio/mpeg\r\n" +
		"icy-name: /testpath\r\n" +
		"icy-metadata: 1\r\n" +
		"icy-metaint: 5\r\n" +
		"\r\n" +
		`cdefg` + string(0x02) + `StreamTitle='test2 - artist2';` + string([]byte{0x0, 0x0}) +
		`h1234` + string(0x02) + `StreamTitle='test3 - artist3';` + string([]byte{0x0, 0x0}) +
		`5???!` + string(0x02) + `StreamTitle='test3 - artist3';` + string([]byte{0x0, 0x0}) +
		`!!&&&` + string(0x02) + `StreamTitle='test3 - artist3';` + string([]byte{0x0, 0x0}) +
		`$$$`) {

		t.Error("Unexpected response:", testConn.Out.String())
		return
	}

}

func TestDudelDuMain(t *testing.T) {

	// Make the fatal a simple print

	fatal = print

	// Make sure out.txt and test.dpl are removed

	defer func() {
		if res, _ := fileutil.PathExists("out.txt"); res {
			os.Remove("out.txt")
		}
		if res, _ := fileutil.PathExists("test.dpl"); res {
			os.Remove("test.dpl")
		}
	}()

	// Reset flags

	flag.CommandLine = &flag.FlagSet{}

	// Test usage text

	os.Args = []string{"dudeldu", "-?", "-port", "9000", "test"}
	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)

	if ret, err := execMain(); err != nil || ret != `
DudelDu `[1:]+dudeldu.ProductVersion+`
Usage of dudeldu [options] <playlist>
  -?	Show this help message
  -auth string
    	Authentication as <user>:<pass>
  -debug
    	Enable extra debugging output
  -fqs int
    	Frame queue size (default 10000)
  -host string
    	Server hostname to listen on (default "127.0.0.1")
  -loop
    	Loop playlists
  -port string
    	Server port to listen on (default "9091")
  -pp string
    	Prefix all paths with a string
  -shuffle
    	Shuffle playlists
  -tps int
    	Thread pool size (default 10)

Authentication can also be defined via the environment variable: DUDELDU_AUTH="<user>:<pass>"
` {
		t.Error("Unexpected output:", "#"+ret+"#", err)
		return
	}

	ioutil.WriteFile("test.dpl", []byte("{}"), 0644)

	lookupEnv = func(key string) (string, bool) {
		if key == "DUDELDU_AUTH" {
			return "web:web", true
		}
		return "", false
	}

	os.Args = []string{"dudeldu", "-port", "-1", "test.dpl"}
	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)

	if ret, err := execMain(); err != nil || ret != `
DudelDu `[1:]+dudeldu.ProductVersion+`
Serving playlist test.dpl on 127.0.0.1:-1
Thread pool size: 10
Frame queue size: 10000
Loop playlist: false
Shuffle playlist: false
Path prefix: 
Required authentication: web:web
listen tcp: invalid port -1
Shutting down
` && ret != `
DudelDu `[1:]+dudeldu.ProductVersion+`
Serving playlist test.dpl on 127.0.0.1:-1
Thread pool size: 10
Frame queue size: 10000
Loop playlist: false
Shuffle playlist: false
Path prefix: 
Required authentication: web:web
listen tcp: address -1: invalid port
Shutting down
` {
		t.Error("Unexpected output:", ret, err)
		return
	}
}

/*
Execute the main function and capture the output.
*/
func execMain() (string, error) {

	// Exchange stderr to a file

	origStdErr := os.Stderr
	outFile, err := os.Create("out.txt")
	if err != nil {
		return "", err
	}
	defer func() {
		outFile.Close()
		os.RemoveAll("out.txt")

		// Put Stderr back

		os.Stderr = origStdErr
	}()

	os.Stderr = outFile

	main()

	outFile.Sync()

	out, err := ioutil.ReadFile("out.txt")
	if err != nil {
		return "", err
	}

	return string(out), nil
}
