// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package source

import (
	"bytes"
	"reflect"
	"strings"
	"testing"
)

// This file is a copy of go/doc/comment_test.go with the exception for
// the test cases for TestEmphasize and TestCommentEscape

var headingTests = []struct {
	line string
	ok   bool
}{
	{"Section", true},
	{"A typical usage", true},
	{"ΔΛΞ is Greek", true},
	{"Foo 42", true},
	{"", false},
	{"section", false},
	{"A typical usage:", false},
	{"This code:", false},
	{"δ is Greek", false},
	{"Foo §", false},
	{"Fermat's Last Sentence", true},
	{"Fermat's", true},
	{"'sX", false},
	{"Ted 'Too' Bar", false},
	{"Use n+m", false},
	{"Scanning:", false},
	{"N:M", false},
}

func TestIsHeading(t *testing.T) {
	for _, tt := range headingTests {
		if h := heading(tt.line); (len(h) > 0) != tt.ok {
			t.Errorf("isHeading(%q) = %v, want %v", tt.line, h, tt.ok)
		}
	}
}

var blocksTests = []struct {
	in   string
	out  []block
	text string
}{
	{
		in: `Para 1.
Para 1 line 2.

Para 2.

Section

Para 3.

	pre
	pre1

Para 4.

	pre
	pre1

	pre2

Para 5.


	pre


	pre1
	pre2

Para 6.
	pre
	pre2
`,
		out: []block{
			{opPara, []string{"Para 1.\n", "Para 1 line 2.\n"}},
			{opPara, []string{"Para 2.\n"}},
			{opHead, []string{"Section"}},
			{opPara, []string{"Para 3.\n"}},
			{opPre, []string{"pre\n", "pre1\n"}},
			{opPara, []string{"Para 4.\n"}},
			{opPre, []string{"pre\n", "pre1\n", "\n", "pre2\n"}},
			{opPara, []string{"Para 5.\n"}},
			{opPre, []string{"pre\n", "\n", "\n", "pre1\n", "pre2\n"}},
			{opPara, []string{"Para 6.\n"}},
			{opPre, []string{"pre\n", "pre2\n"}},
		},
		text: `.   Para 1. Para 1 line 2.

.   Para 2.


.   Section

.   Para 3.

$	pre
$	pre1

.   Para 4.

$	pre
$	pre1

$	pre2

.   Para 5.

$	pre


$	pre1
$	pre2

.   Para 6.

$	pre
$	pre2
`,
	},
	{
		in: "Para.\n\tshould not be ``escaped''",
		out: []block{
			{opPara, []string{"Para.\n"}},
			{opPre, []string{"should not be ``escaped''"}},
		},
		text: ".   Para.\n\n$	should not be ``escaped''",
	},
	{
		in: "// A very long line of 46 char for line wrapping.",
		out: []block{
			{opPara, []string{"// A very long line of 46 char for line wrapping."}},
		},
		text: `.   // A very long line of 46 char for line
.   // wrapping.
`,
	},
	{
		in: `/* A very long line of 46 char for line wrapping.
A very long line of 46 char for line wrapping. */`,
		out: []block{
			{opPara, []string{"/* A very long line of 46 char for line wrapping.\n", "A very long line of 46 char for line wrapping. */"}},
		},
		text: `.   /* A very long line of 46 char for line
.   wrapping. A very long line of 46 char
.   for line wrapping. */
`,
	},
}

func TestBlocks(t *testing.T) {
	for i, tt := range blocksTests {
		b := blocks(tt.in)
		if !reflect.DeepEqual(b, tt.out) {
			t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, b, tt.out)
		}
	}
}

// This has been modified from go/doc to use markdown links instead of html ones
// and use markdown escaping instead oh html
var emphasizeTests = []struct {
	in, out string
}{
	{"", ""},
	{"http://[::1]:8080/foo.txt", `[http\:\/\/\[\:\:1\]\:8080\/foo\.txt](http://[::1]:8080/foo.txt)`},
	{"before (https://www.google.com) after", `before \([https\:\/\/www\.google\.com](https://www.google.com)\) after`},
	{"before https://www.google.com:30/x/y/z:b::c. After", `before [https\:\/\/www\.google\.com\:30\/x\/y\/z\:b\:\:c](https://www.google.com:30/x/y/z:b::c)\. After`},
	{"http://www.google.com/path/:;!-/?query=%34b#093124", `[http\:\/\/www\.google\.com\/path\/\:\;\!\-\/\?query\=\%34b\#093124](http://www.google.com/path/:;!-/?query=%34b#093124)`},
	{"http://www.google.com/path/:;!-/?query=%34bar#093124", `[http\:\/\/www\.google\.com\/path\/\:\;\!\-\/\?query\=\%34bar\#093124](http://www.google.com/path/:;!-/?query=%34bar#093124)`},
	{"http://www.google.com/index.html! After", `[http\:\/\/www\.google\.com\/index\.html](http://www.google.com/index.html)\! After`},
	{"http://www.google.com/", `[http\:\/\/www\.google\.com\/](http://www.google.com/)`},
	{"https://www.google.com/", `[https\:\/\/www\.google\.com\/](https://www.google.com/)`},
	{"http://www.google.com/path.", `[http\:\/\/www\.google\.com\/path](http://www.google.com/path)\.`},
	{"http://en.wikipedia.org/wiki/Camellia_(cipher)", `[http\:\/\/en\.wikipedia\.org\/wiki\/Camellia\_\(cipher\)](http://en.wikipedia.org/wiki/Camellia_\(cipher\))`},
	{"(http://www.google.com/)", `\([http\:\/\/www\.google\.com\/](http://www.google.com/)\)`},
	{"http://gmail.com)", `[http\:\/\/gmail\.com](http://gmail.com)\)`},
	{"((http://gmail.com))", `\(\([http\:\/\/gmail\.com](http://gmail.com)\)\)`},
	{"http://gmail.com ((http://gmail.com)) ()", `[http\:\/\/gmail\.com](http://gmail.com) \(\([http\:\/\/gmail\.com](http://gmail.com)\)\) \(\)`},
	{"Foo bar http://example.com/ quux!", `Foo bar [http\:\/\/example\.com\/](http://example.com/) quux\!`},
	{"Hello http://example.com/%2f/ /world.", `Hello [http\:\/\/example\.com\/\%2f\/](http://example.com/%2f/) \/world\.`},
	{"Lorem http: ipsum //host/path", `Lorem http\: ipsum \/\/host\/path`},
	{"javascript://is/not/linked", `javascript\:\/\/is\/not\/linked`},
	{"http://foo", `[http\:\/\/foo](http://foo)`},
	{"art by [[https://www.example.com/person/][Person Name]]", `art by \[\[[https\:\/\/www\.example\.com\/person\/](https://www.example.com/person/)\]\[Person Name\]\]`},
	{"please visit (http://golang.org/)", `please visit \([http\:\/\/golang\.org\/](http://golang.org/)\)`},
	{"please visit http://golang.org/hello())", `please visit [http\:\/\/golang\.org\/hello\(\)](http://golang.org/hello\(\))\)`},
	{"http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD", `[http\:\/\/git\.qemu\.org\/\?p\=qemu\.git\;a\=blob\;f\=qapi\-schema\.json\;hb\=HEAD](http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD)`},
	{"https://foo.bar/bal/x(])", `[https\:\/\/foo\.bar\/bal\/x\(](https://foo.bar/bal/x\()\]\)`},
	{"foo [ http://bar(])", `foo \[ [http\:\/\/bar\(](http://bar\()\]\)`},
}

func TestEmphasize(t *testing.T) {
	for i, tt := range emphasizeTests {
		var buf bytes.Buffer
		emphasize(&buf, tt.in, true)
		out := buf.String()
		if out != tt.out {
			t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, out, tt.out)
		}
	}
}

func TestCommentEscape(t *testing.T) {
	//ldquo -> ulquo and rdquo -> urquo
	commentTests := []struct {
		in, out string
	}{
		{"typically invoked as ``go tool asm'',", "typically invoked as " + ulquo + "go tool asm" + urquo + ","},
		{"For more detail, run ``go help test'' and ``go help testflag''", "For more detail, run " + ulquo + "go help test" + urquo + " and " + ulquo + "go help testflag" + urquo}}
	for i, tt := range commentTests {
		var buf strings.Builder
		commentEscape(&buf, tt.in, true)
		out := buf.String()
		if out != tt.out {
			t.Errorf("#%d: mismatch\nhave: %q\nwant: %q", i, out, tt.out)
		}
	}
}

func TestCommentToMarkdown(t *testing.T) {
	tests := []struct {
		in, out string
	}{
		{
			in:  "F declaration.\n",
			out: "F declaration\\.\n",
		},
		{
			in: `
F declaration. Lorem ipsum dolor sit amet.
Etiam mattis eros at orci mollis molestie.
`,
			out: `
F declaration\. Lorem ipsum dolor sit amet\.
Etiam mattis eros at orci mollis molestie\.
`,
		},
		{
			in: `
F declaration.

Lorem ipsum dolor sit amet.
Sed id dui turpis.




Aenean tempus velit non auctor eleifend.
Aenean efficitur a sem id ultricies.


Phasellus efficitur mauris et viverra bibendum.
`,
			out: `
F declaration\.

Lorem ipsum dolor sit amet\.
Sed id dui turpis\.

Aenean tempus velit non auctor eleifend\.
Aenean efficitur a sem id ultricies\.

Phasellus efficitur mauris et viverra bibendum\.
`,
		},
		{
			in: `
F declaration.

Aenean tempus velit non auctor eleifend.

Section

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

  func foo() {}


  func bar() {}

Fusce lorem lacus.

    func foo() {}

    func bar() {}

Maecenas in lobortis lectus.

	func foo() {}

	func bar() {}

Phasellus efficitur mauris et viverra bibendum.
`,
			out: `
F declaration\.

Aenean tempus velit non auctor eleifend\.

### Section

Lorem ipsum dolor sit amet, consectetur adipiscing elit\.

    func foo() {}


    func bar() {}

Fusce lorem lacus\.

    func foo() {}

    func bar() {}

Maecenas in lobortis lectus\.

    func foo() {}

    func bar() {}

Phasellus efficitur mauris et viverra bibendum\.
`,
		},
		{
			in: `
F declaration.

	func foo() {
		fmt.Println("foo")
	}
	func bar() {
		fmt.Println("bar")
	}
`,
			out: `
F declaration\.

    func foo() {
    	fmt.Println("foo")
    }
    func bar() {
    	fmt.Println("bar")
    }
`,
		},
	}
	for i, tt := range tests {
		// Comments start with new lines for better readability. So, we should trim them.
		tt.in = strings.TrimPrefix(tt.in, "\n")
		tt.out = strings.TrimPrefix(tt.out, "\n")

		if out := CommentToMarkdown(tt.in); out != tt.out {
			t.Errorf("#%d: mismatch\nhave: %q\nwant: %q", i, out, tt.out)
		}
	}
}
