package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/url"
	"path"
	"strings"
	"time"
)

var GopherTimeout time.Duration = time.Duration(settings.TimeOutSeconds) * time.Second

func RetrieveGopher(u *url.URL) (string, error) {
	conn, err := net.DialTimeout("tcp", u.Host, time.Duration(settings.TimeOutSeconds) * time.Second)
	if err != nil {
		return ConvertErrorToHTML(50, "Unable to connect to host"), nil 
	}

	gophertype := '1'
	send := "/"
	if len(u.Path) > 2 {
		send = u.Path[2:]
		gophertype = rune(u.Path[1])
	} else if len(u.Path) == 2 {
		gophertype = rune(u.Path[1])
	}
	if u.RawQuery != "" {
		send = send + "?" + u.RawQuery
	}

	_, err = conn.Write([]byte(send+"\n"))
	if err != nil {
		return ConvertErrorToHTML(50, "Unable to make request to host; connection closed"), nil
	}

	result, err := ioutil.ReadAll(conn)
	if err != nil {
		return ConvertErrorToHTML(50, "Unable to read response from host"), nil
	}

	return ParseGopherBody(string(result), u, gophertype)
}

func EditHTMLFile(html string) string {
	bodyStart := strings.Index(html, "<body")
	if bodyStart > -1 {
		html = html[bodyStart:]
		bodyStartEnd := strings.Index(html, ">")
		if bodyStartEnd > -1 {
			html = html[bodyStartEnd+1:]
		}
	}
	bodyEnd := strings.Index(html, "</body>")
	if bodyEnd > -1 {
		html = html[:bodyEnd]
	}
	html = strings.ReplaceAll(html, "<script", `<span style="display: none;"`)
	html = strings.ReplaceAll(html, "</script", "</span")
	return html
}

func ParseGopherBody(body string, u *url.URL, gophertype rune) (string, error) {
	switch gophertype {
	case 'h':
		// - Make robust enough to handle URL links
		//   - Open in system browser?
		// - Remove all scripts and all head info/body tags
		return EditHTMLFile(body), nil
	case '0':
		return strings.ReplaceAll(body, "\n", "<br>\n"), nil
	case '1':
		return ParseGopherMap(body), nil
	case '7':
		return RenderSearchForm("Search/query?", u)
	case 'g':
		return MakeImage(body, "image/gif"), nil
	case 'I':
		ft := strings.ToLower(path.Ext(u.Path))
		mime := "image/jpeg"
		switch ft {
		case ".png":
			mime = "image/png"
		case ".tif", ".tiff":
			mime = "image/tiff"
		case ".bmp":
			mime = "image/bmp"
		case ".gif":
			mime = "image/gif"
		case ".ico":
			mime = "image/vnd.microsoft.icon"
		case ".webp":
			mime = "image/webp"
		}
		return MakeImage(body, mime), nil
		// Make this more robust/support png at least
	default:
		appCtrl.SaveFile(body)
		return "", fmt.Errorf("File Saved")
	}
	return "", nil
}

func ParseGopherMap(gophermap string) string {
	var out strings.Builder
	linkFormat := "<a href=\"gopher://%s:%s/%c%s\" class=\"gopher type-%c\">%s</a><br>\n"
	urlLinkFormat := "<a href=\"%s\" class=\"gopher type-W\">%s</a><br>\n"
	splitMap := strings.Split(gophermap, "\n")
	for _, ln := range splitMap {
		ln = strings.TrimSpace(ln)
		lnSplit := strings.Split(ln, "\t")
		if len(lnSplit) < 4 {
			continue
		}
		gophertype := rune(lnSplit[0][0])
		msg := " "
		if len(lnSplit[0]) > 1 {
			msg = lnSplit[0][1:]
		}

		// Once query support is added to the client remove '7'
		if gophertype == 't' || gophertype == '2' || gophertype == 'T' {
			continue
		}
		if gophertype == 'i' {
			out.WriteString(`<pre class="gopher">`)
			out.WriteString(msg)
			out.WriteString("</pre>\n")
		} else {
			if strings.HasPrefix(strings.ToUpper(lnSplit[1]), "URL:") && len(lnSplit[1]) > 4 {
				out.WriteString(fmt.Sprintf(urlLinkFormat, lnSplit[1][4:], msg))
			} else {
				out.WriteString(fmt.Sprintf(linkFormat, lnSplit[2], lnSplit[3], gophertype, lnSplit[1], gophertype, msg))
			}
		}
	}

	return out.String()
}

