/*
 * Copyright 2014-2020 Li Kexian
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Go module for domain whois information parsing
 * https://www.likexian.com/
 */

package whoisparser

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/likexian/gokit/assert"
	"github.com/likexian/gokit/xslice"
)

// Prepare do prepare the whois info for parsing
func Prepare(text, ext string) (string, bool) {
	text = strings.Replace(text, "\r", "", -1)
	text = strings.Replace(text, "\t", " ", -1)
	text = strings.TrimSpace(text)

	switch ext {
	case "":
		return prepareTLD(text), true
	case "edu":
		return prepareEDU(text), true
	case "int":
		return prepareINT(text), true
	case "mo":
		return prepareMO(text), true
	case "hk":
		return prepareHK(text), true
	case "tw":
		return prepareTW(text), true
	case "ch":
		return prepareCH(text), true
	case "it":
		return prepareIT(text), true
	case "fr", "re", "tf", "yt", "pm", "wf":
		return prepareFR(text), true
	case "ru", "su":
		return prepareRU(text), true
	case "fi":
		return prepareFI(text), true
	case "jp":
		return prepareJP(text), true
	case "uk":
		return prepareUK(text), true
	case "kr":
		return prepareKR(text), true
	case "nz":
		return prepareNZ(text), true
	case "tk":
		return prepareTK(text), true
	case "nl":
		return prepareNL(text), true
	case "eu":
		return prepareEU(text), true
	case "br":
		return prepareBR(text), true
	case "ir", "xn--mgba3a4f16a":
		return prepareIR(text), true
	case "rs":
		return prepareRS(text), true
	default:
		return text, false
	}
}

// prepareTLD do prepare the tld domain
func prepareTLD(text string) string {
	token := ""
	result := ""

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			token = ""
			continue
		}
		if strings.Contains(v, ":") {
			vs := strings.Split(v, ":")
			if strings.TrimSpace(vs[0]) == "organisation" {
				if token == "" {
					token = "registrant"
				}
			}
			if strings.TrimSpace(vs[0]) == "contact" {
				token = strings.TrimSpace(vs[1])
			} else {
				if token != "" {
					v = fmt.Sprintf("%s %s", token, v)
				}
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareEDU do prepare the .edu domain
func prepareEDU(text string) string {
	tokens := map[string][]string{
		"Registrant:": {
			"Organization",
			"Address",
			"Address",
			"Address",
			"Address",
		},
		"Administrative Contact:": {
			"Name",
			"Organization",
			"Address",
			"Address",
			"Address",
			"Address",
			"Phone",
			"Email",
		},
		"Technical Contact:": {
			"Name",
			"Organization",
			"Address",
			"Address",
			"Address",
			"Address",
			"Phone",
			"Email",
		},
	}

	token := ""
	index := 0

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		if strings.HasSuffix(v, ":") {
			token = ""
			index = 0
		}
		if _, ok := tokens[v]; ok {
			token = v
		} else {
			if token == "" {
				result += "\n" + v
			} else {
				// address ending now jump to phone
				if tokens[token][index] == "Address" && strings.HasPrefix(v, "+") {
					found := xslice.Index(tokens[token], "Phone")
					if found == -1 {
						token = ""
						index = 0
						continue
					}
					index = found
				}
				result += fmt.Sprintf("\n%s %s: %s", token[:len(token)-1], tokens[token][index], v)
				index += 1
			}
		}
	}

	return result
}

// prepareINT do prepare the .int domain
func prepareINT(text string) string {
	token := ""
	result := ""

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			token = ""
			continue
		}
		if strings.Contains(v, ":") {
			vs := strings.Split(v, ":")
			if strings.TrimSpace(vs[0]) == "organisation" {
				if token == "" {
					token = "registrant"
				}
			}
			if strings.TrimSpace(vs[0]) == "contact" {
				token = strings.TrimSpace(vs[1])
			} else {
				if token != "" {
					v = fmt.Sprintf("%s %s", token, v)
				}
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareMO do prepare the .mo domain
func prepareMO(text string) string {
	tokens := map[string]string{
		"Registrant:":           "Registrant",
		"Admin Contact(s):":     "Admin",
		"Billing Contact(s):":   "Billing",
		"Technical Contact(s):": "Technical",
	}

	token := ""
	result := ""

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			token = ""
			continue
		}
		if v[0] == '-' {
			continue
		}
		for _, s := range []string{"Record created on", "Record expires on"} {
			if strings.HasPrefix(v, s) {
				v = strings.Replace(v, s, s+":", 1)
			}
		}
		if _, ok := tokens[v]; ok {
			token = tokens[v]
		} else {
			if token != "" {
				v = fmt.Sprintf("%s %s", token, v)
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareHK do prepare the .hk domain
func prepareHK(text string) string {
	tokens := map[string]string{
		"Registrant Contact Information:":     "Registrant",
		"Administrative Contact Information:": "Admin",
		"Technical Contact Information:":      "Technical",
		"Name Servers Information:":           "Name Servers:",
	}

	dateTokens := []string{
		"Domain Name Commencement Date",
		"Expiry Date",
	}

	token := ""
	addressToken := false
	text = strings.Replace(text, "\n\n", "\n", -1)

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			token = ""
			continue
		}
		field := ""
		if strings.Contains(v, ":") {
			vs := strings.SplitN(v, ":", 2)
			field = strings.TrimSpace(vs[0])
			if strings.Contains(field, "(") {
				field = strings.Split(field, "(")[0]
				v = fmt.Sprintf("%s: %s", field, vs[1])
			}
			addressToken = field == "Address"
			if field == "Registrar Contact Information" {
				re := regexp.MustCompile(`Email\:\s+([^\s]+)(\s+Hotline\:(.*))?`)
				m := re.FindStringSubmatch(vs[1])
				if len(m) == 4 {
					v = ""
					if m[1] != "" {
						v += "Registrar Contact Email: " + m[1] + "\n"
					}
					if m[3] != "" {
						v += "Registrar Contact Phone: " + m[3] + "\n"
					}
					v = strings.TrimSpace(v)
				}
			}
			if field == "Family name" {
				vv := strings.TrimSpace(vs[1])
				if vv != "" && vv != "." {
					result += " " + vv
				}
				continue
			}
		} else {
			if addressToken {
				result += ", " + v
				continue
			}
		}
		if _, ok := tokens[v]; ok {
			token = tokens[v]
		} else {
			if token != "" && !assert.IsContains(dateTokens, field) {
				v = fmt.Sprintf("%s %s", token, v)
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareTW do prepare the .tw domain
func prepareTW(text string) string {
	tokens := map[string][]string{
		"Registrant:": {
			"Organization",
			"Name,Email",
			"Phone",
			"Fax",
			"Address",
			"Address",
			"Address",
		},
		"Administrative Contact:": {
			"Name,Email",
			"Phone",
			"Fax",
		},
		"Technical Contact:": {
			"Name,Email",
			"Phone",
			"Fax",
		},
		"Contact:": {
			"Name",
			"Email",
		},
	}

	token := ""
	index := -1

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		for _, s := range []string{"Record created on", "Record expires on"} {
			if strings.HasPrefix(v, s) {
				v = strings.Replace(v, s, s+":", 1)
			}
		}
		if strings.Contains(v, ":") {
			token = ""
			index = -1
		}
		if _, ok := tokens[v]; ok {
			token = v
		} else {
			if token == "" {
				result += "\n" + v
			} else {
				index += 1
				tokenName := token[:len(token)-1]
				indexName := tokens[token][index]
				if tokenName == "Contact" {
					tokenName = "Registrant Contact"
				}
				if strings.Contains(indexName, ",") {
					ins := strings.Split(indexName, ",")
					re := regexp.MustCompile(`(.*)\s+([^\s]+@[^\s]+)`)
					m := re.FindStringSubmatch(v)
					if len(m) == 3 {
						result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[0], strings.TrimSpace(m[1]))
						result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[1], strings.TrimSpace(m[2]))
					} else {
						result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[0], strings.TrimSpace(v))
					}
					continue
				}
				result += fmt.Sprintf("\n%s %s: %s", tokenName, indexName, v)
			}
		}
	}

	return result
}

// prepareCH do prepare the .ch domain
func prepareCH(text string) string {
	tokens := []string{
		"Domain name",
		"Holder",
		"Technical contact",
		"Registrar",
		"DNSSEC",
		"Name servers",
		"First registration date",
	}

	splits := map[string]string{
		"Holder":            "Registrant organization, Registrant name, Registrant street",
		"Technical contact": "Technical organization, Technical name, Technical street",
	}

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		found := false
		for _, t := range tokens {
			if strings.HasPrefix(strings.ToLower(v)+" ", strings.ToLower(t+" ")) {
				found = true
				result += fmt.Sprintf("\n%s: %s", strings.TrimSpace(t), strings.TrimSpace(v[len(t):]))
				break
			}
		}
		if !found {
			result += ", " + v
		}
	}

	results := []string{}
	for _, v := range strings.Split(result, "\n") {
		if !strings.Contains(v, ":") {
			continue
		}
		vs := strings.Split(v, ":")
		if sp, ok := splits[vs[0]]; ok {
			vv := strings.Split(vs[1], ", ")
			ss := strings.Split(sp, ", ")
			if len(vv) > len(ss) {
				vv[len(ss)-1] = strings.Join(vv[len(ss)-1:], ", ")
				vv = vv[:len(ss)]
			}
			for k := range vv {
				results = append(results, fmt.Sprintf("%s: %s", ss[k], vv[k]))
			}
		} else {
			results = append(results, v)
		}
	}

	text = strings.Join(results, "\n")
	text = strings.Replace(text, ": ,", ":", -1)

	return text
}

// prepareIT do prepare the .it domain
func prepareIT(text string) string {
	topTokens := []string{
		"Registrant",
		"Admin Contact",
		"Technical Contacts",
		"Registrar",
		"Nameservers",
	}

	topToken := ""
	subToken := ""

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		if assert.IsContains(topTokens, v) {
			topToken = v + " "
			subToken = ""
		} else {
			if v[0] != '*' && strings.Contains(v, ":") {
				vs := strings.Split(v, ":")
				subToken = vs[0]
			} else {
				if subToken != "" {
					result += ", " + v
					continue
				}
			}
			if topToken != "" && !strings.Contains(v, ":") {
				result += fmt.Sprintf("\n%s: %s", topToken, v)
			} else {
				result += fmt.Sprintf("\n%s%s", topToken, v)
			}
		}
	}

	return result
}

// prepareFR do prepare the .fr domain
func prepareFR(text string) string {
	dsToken := "dsl-id"
	hdlToken := "nic-hdl"
	regToken := "registrar"

	tokens := map[string]string{
		"holder-c": "holder",
		"admin-c":  "admin",
		"tech-c":   "tech",
	}

	token := ""
	newBlock := false
	hdls := map[string]string{}

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			newBlock = true
			continue
		}

		vs := strings.Split(v, ":")
		if newBlock && strings.TrimSpace(vs[0]) == regToken {
			token = regToken + " "
			v = fmt.Sprintf("name: %s", strings.TrimSpace(vs[1]))
		}

		newBlock = false
		if t, ok := tokens[strings.TrimSpace(vs[0])]; ok {
			hdls[t] = strings.TrimSpace(vs[1])
		}

		if strings.TrimSpace(vs[0]) == dsToken && strings.TrimSpace(vs[1]) != "" {
			v += "\nDNSSEC: signed"
		}

		if strings.TrimSpace(vs[0]) == hdlToken {
			for _, kk := range Keys(hdls) {
				if strings.TrimSpace(vs[1]) == hdls[kk] {
					token = kk + " "
					delete(hdls, kk)
					break
				}
			}
		}

		result += fmt.Sprintf("\n%s%s", token, v)
	}

	return result
}

// prepareRU do prepare the .ru domain
func prepareRU(text string) string {
	tokens := map[string]string{
		"person": "Registrant Name",
		"e-mail": "Registrant Email",
		"org":    "Registrant Organization",
	}

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		if !strings.Contains(v, ":") {
			continue
		}
		vs := strings.Split(v, ":")
		if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
			v = fmt.Sprintf("%s: %s", vv, vs[1])
		}
		result += v + "\n"
	}

	return result
}

// prepareJP do prepare the .jp domain
func prepareJP(text string) string {
	replacer := regexp.MustCompile(`\n\[(.+?)\][\ ]*(.+?)?`)
	text = replacer.ReplaceAllString(text, "\n$1: $2")

	adminToken := "Contact Information"
	addressToken := "Postal Address"

	token := ""
	prefixToken := ""

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		if strings.Contains(v, ":") {
			vs := strings.Split(v, ":")
			token = strings.TrimSpace(vs[0])
			if token == adminToken {
				prefixToken = "admin "
			}
		} else {
			if token == addressToken {
				result += ", " + v
				continue
			}
		}
		result += "\n" + prefixToken + v
	}

	return result
}

// prepareUK do prepare the .uk domain
func prepareUK(text string) string {
	tokens := map[string]string{
		"URL": "Registrar URL",
	}

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		if strings.Contains(v, ":") {
			vs := strings.SplitN(v, ":", 2)
			if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
				v = fmt.Sprintf("%s: %s", vv, vs[1])
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareKR do prepare the .kr domain
func prepareKR(text string) string {
	english := "# ENGLISH"
	tokens := map[string]string{
		"Administrative Contact(AC)": "Administrative Contact Name",
		"AC E-Mail":                  "Administrative Contact E-Mail",
		"AC Phone Number":            "Administrative Contact Phone Number",
		"Authorized Agency":          "Registrar Name",
	}

	pos := strings.Index(text, english)
	if pos != -1 {
		text = text[pos+len(english):]
	}

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		if v[0] == '\'' || v[0] == '-' {
			continue
		}
		if strings.Contains(v, ":") {
			vs := strings.SplitN(v, ":", 2)
			if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
				v = fmt.Sprintf("%s: %s", vv, vs[1])
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareNZ do prepare the .nz domain
func prepareNZ(text string) string {
	result := ""

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if strings.Contains(v, ":") {
			vs := strings.SplitN(v, ":", 2)
			if strings.HasPrefix(strings.TrimSpace(vs[0]), "ns_name_") {
				v = fmt.Sprintf("name server: %s", vs[1])
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareTK do prepare the .tk domain
func prepareTK(text string) string {
	tokens := map[string]string{
		"Domain name:":        "Domain",
		"Domain Nameservers:": "Nameservers",
		"Owner contact:":      "Registrant",
		"Admin contact:":      "Admin",
		"Billing contact:":    "Billing",
		"Tech contact:":       "Technical",
		"Organisation:":       "Registrant",
	}

	fields := map[string][]string{
		"Registrant": {
			"Organization",
			"Name",
			"Address",
			"Address",
			"Address",
		},
	}

	token := ""
	result := ""
	index := 0

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			token = ""
			continue
		}
		if _, ok := tokens[v]; ok {
			token = tokens[v]
			continue
		}
		if token == "Domain" && strings.Contains(v, " is ") {
			vv := strings.Split(v, " is ")
			v = fmt.Sprintf("Name: %s\nStatus: %s", vv[0], vv[1])
		} else if token == "Registrant" && !strings.Contains(v, ":") {
			v = fmt.Sprintf("%s: %s", fields[token][index], v)
			index += 1
		}
		if token != "" {
			if !strings.Contains(v, ":") {
				v = fmt.Sprintf("%s: %s", token, v)
			} else {
				v = fmt.Sprintf("%s %s", token, v)
			}
		}
		result += "\n" + strings.TrimSpace(v)
	}

	return result
}

// prepareNL do prepare the .nl domain
func prepareNL(text string) string {
	tokens := map[string][]string{
		"Reseller:": {
			"Name",
			"Address",
			"Address",
			"Address",
			"Address",
		},
		"Registrar:": {
			"Name",
			"Address",
			"Address",
			"Address",
			"Address",
		},
	}

	token := ""
	index := 0

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		if strings.HasSuffix(v, ":") {
			token = ""
			index = 0
		}
		if _, ok := tokens[v]; ok {
			token = v
		} else {
			if token == "" {
				result += "\n" + v
			} else {
				result += fmt.Sprintf("\n%s %s: %s", token[:len(token)-1], tokens[token][index], v)
				index += 1
			}
		}
	}

	return result
}

// prepareEU do prepare the .eu domain
func prepareEU(text string) string {
	tokens := map[string]string{
		"Registrant:":   "Registrant",
		"Technical:":    "Technical",
		"Registrar:":    "Registrar",
		"Onsite(s):":    "Onsite",
		"Name servers:": "Name servers",
	}

	token := ""
	result := ""

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			token = ""
			continue
		}
		if _, ok := tokens[v]; ok {
			token = tokens[v]
			continue
		} else {
			if token != "" {
				if strings.Contains(v, ":") {
					v = fmt.Sprintf("%s %s", token, v)
				} else {
					if strings.HasPrefix(v, "Visit www.eurid.eu") {
						continue
					}
					v = fmt.Sprintf("%s: %s", token, v)
				}
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareBR do prepare the .br domain
func prepareBR(text string) string {
	hdlToken := "nic-hdl-br"
	tokens := map[string]string{
		"owner-c":   "registrant",
		"admin-c":   "admin",
		"tech-c":    "tech",
		"billing-c": "billing",
	}

	token := ""
	hdlMap := map[string]string{}

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			token = ""
			continue
		}
		if strings.Contains(v, ":") {
			vs := strings.SplitN(v, ":", 2)
			if strings.TrimSpace(vs[0]) == hdlToken {
				token = strings.TrimSpace(vs[1])
				hdlMap[token] = ""
			}
		}
		if token != "" {
			hdlMap[token] += "\n" + v
		}
	}

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		if strings.Contains(v, ":") {
			vs := strings.SplitN(v, ":", 2)
			if strings.TrimSpace(vs[0]) == "owner" {
				v = fmt.Sprintf("registrant organization: %s", vs[1])
			}
			if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
				for _, tt := range strings.Split(hdlMap[strings.TrimSpace(vs[1])], "\n") {
					if strings.TrimSpace(tt) == "" {
						continue
					}
					result += fmt.Sprintf("\n%s %s", vv, tt)
				}
				continue
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareIR do prepare the .ir domain
func prepareIR(text string) string {
	hdlToken := "nic-hdl"
	tokens := map[string]string{
		"holder-c": "registrant",
		"admin-c":  "admin",
		"tech-c":   "tech",
		"bill-c":   "billing",
	}

	token := ""
	hdlMap := map[string]string{}

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			token = ""
			continue
		}
		if strings.Contains(v, ":") {
			vs := strings.SplitN(v, ":", 2)
			if strings.TrimSpace(vs[0]) == hdlToken {
				token = strings.TrimSpace(vs[1])
				hdlMap[token] = ""
			}
		}
		if token != "" {
			hdlMap[token] += "\n" + v
		}
	}

	result := ""
	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if v == "" {
			continue
		}
		if strings.Contains(v, ":") {
			vs := strings.SplitN(v, ":", 2)
			if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
				for _, tt := range strings.Split(hdlMap[strings.TrimSpace(vs[1])], "\n") {
					if strings.TrimSpace(tt) == "" {
						continue
					}
					result += fmt.Sprintf("\n%s %s", vv, tt)
				}
				continue
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareFI do prepare the .fi domain
func prepareFI(text string) string {
	tokens := map[string]string{
		"Holder":    "Registrant",
		"Registrar": "Registrar",
		"Tech":      "Technical",
	}

	token := ""
	result := ""

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if len(v) == 0 {
			continue
		}
		if v[0] == '>' {
			token = ""
		}
		if _, ok := tokens[v]; ok {
			token = tokens[v]
		} else {
			if strings.Contains(v, ":") {
				vv := strings.SplitN(v, ":", 2)
				vv[0] = strings.Trim(vv[0], ".")
				if token == "Registrar" && vv[0] == "registrar" {
					vv[0] = "name"
				}
				v = fmt.Sprintf("%s: %s", vv[0], vv[1])
			}
			if token != "" {
				v = fmt.Sprintf("%s %s", token, v)
			}
		}
		result += "\n" + v
	}

	return result
}

// prepareRS do prepare the .rs domain
func prepareRS(text string) string {
	tokens := map[string]string{
		"Registrant":             "Registrant",
		"Administrative contact": "Administrative",
		"Technical contact":      "Technical",
	}

	token := ""
	result := ""

	for _, v := range strings.Split(text, "\n") {
		v = strings.TrimSpace(v)
		if len(v) == 0 {
			token = ""
			continue
		}
		if strings.Contains(v, ":") {
			vv := strings.SplitN(v, ":", 2)
			vv[0] = strings.TrimSpace(vv[0])
			if t, ok := tokens[vv[0]]; ok {
				token = t
			} else if token != "" {
				v = fmt.Sprintf("%s %s", token, v)
			}
		}
		result += "\n" + v
	}

	return result
}
