// Copyright 2016 Google Inc. All rights reserved.
//
// 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.

package web

import (
	"bytes"
	"log"
	"net/url"
	"strconv"
	"strings"
	"text/template"

	"github.com/sourcegraph/zoekt"
)

func (s *Server) formatResults(result *zoekt.SearchResult, query string, localPrint bool) ([]*FileMatch, error) {
	var fmatches []*FileMatch

	templateMap := map[string]*template.Template{}
	fragmentMap := map[string]*template.Template{}
	if !localPrint {
		for repo, str := range result.RepoURLs {
			if str != "" {
				templateMap[repo] = s.getTextTemplate(str)
			}
		}
		for repo, str := range result.LineFragments {
			if str != "" {
				fragmentMap[repo] = s.getTextTemplate(str)
			}
		}
	}
	getFragment := func(repo string, linenum int) string {
		tpl := fragmentMap[repo]

		if tpl == nil || localPrint {
			return "#l" + strconv.Itoa(linenum)
		}

		var buf bytes.Buffer
		if err := tpl.Execute(&buf, map[string]string{
			"LineNumber": strconv.Itoa(linenum),
		}); err != nil {
			log.Printf("fragment template: %v", err)
			return ""
		}
		return buf.String()
	}
	getURL := func(repo, filename string, branches []string, version string) string {
		tpl := templateMap[repo]
		if localPrint || tpl == nil {
			v := make(url.Values)
			v.Add("r", repo)
			v.Add("f", filename)
			v.Add("q", query)
			if len(branches) > 0 {
				v.Add("b", branches[0])
			}
			return "print?" + v.Encode()
		}

		var buf bytes.Buffer
		b := ""
		if len(branches) > 0 {
			b = branches[0]
		}
		err := tpl.Execute(&buf, map[string]string{
			"Branch":  b,
			"Version": version,
			"Path":    filename,
		})
		if err != nil {
			log.Printf("url template: %v", err)
			return ""
		}
		return buf.String()
	}

	// hash => result-id
	seenFiles := map[string]string{}
	for _, f := range result.Files {
		fMatch := FileMatch{
			FileName:   f.FileName,
			Repo:       f.Repository,
			ResultID:   f.Repository + ":" + f.FileName,
			Branches:   f.Branches,
			Language:   f.Language,
			Score:      f.Score,
			ScoreDebug: f.Debug,
		}

		if dup, ok := seenFiles[string(f.Checksum)]; ok {
			fMatch.DuplicateID = dup
		} else {
			seenFiles[string(f.Checksum)] = fMatch.ResultID
		}

		if f.SubRepositoryName != "" {
			fn := strings.TrimPrefix(fMatch.FileName[len(f.SubRepositoryPath):], "/")
			fMatch.URL = getURL(f.SubRepositoryName, fn, f.Branches, f.Version)
		} else {
			fMatch.URL = getURL(f.Repository, f.FileName, f.Branches, f.Version)
		}

		for _, m := range f.LineMatches {
			fragment := getFragment(f.Repository, m.LineNumber)
			if !strings.HasPrefix(fragment, "#") && !strings.HasPrefix(fragment, ";") {
				// TODO - remove this is backward compatibility glue.
				fragment = "#" + fragment
			}
			md := Match{
				FileName: f.FileName,
				LineNum:  m.LineNumber,
				URL:      fMatch.URL + fragment,

				Score:      m.Score,
				ScoreDebug: m.DebugScore,
			}

			md.Before = string(m.Before)
			md.After = string(m.After)
			lastEnd := 0
			line := m.Line
			for i, f := range m.LineFragments {
				l := f.LineOffset
				e := l + f.MatchLength

				frag := Fragment{
					Pre:   string(line[lastEnd:l]),
					Match: string(line[l:e]),
				}
				if i == len(m.LineFragments)-1 {
					frag.Post = string(m.Line[e:])
				}

				md.Fragments = append(md.Fragments, frag)
				lastEnd = e
			}
			fMatch.Matches = append(fMatch.Matches, md)
		}
		fmatches = append(fmatches, &fMatch)
	}
	return fmatches, nil
}
