// Copyright 2015 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.

// This file contains the remaining vestiges of
// $GOROOT/src/go/internal/gcimporter/bimport.go.

package gcimporter

import (
	"fmt"
	"go/token"
	"go/types"
	"sync"
)

func errorf(format string, args ...interface{}) {
	panic(fmt.Sprintf(format, args...))
}

const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go

// Synthesize a token.Pos
type fakeFileSet struct {
	fset  *token.FileSet
	files map[string]*fileInfo
}

type fileInfo struct {
	file     *token.File
	lastline int
}

const maxlines = 64 * 1024

func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
	// TODO(mdempsky): Make use of column.

	// Since we don't know the set of needed file positions, we reserve maxlines
	// positions per file. We delay calling token.File.SetLines until all
	// positions have been calculated (by way of fakeFileSet.setLines), so that
	// we can avoid setting unnecessary lines. See also golang/go#46586.
	f := s.files[file]
	if f == nil {
		f = &fileInfo{file: s.fset.AddFile(file, -1, maxlines)}
		s.files[file] = f
	}
	if line > maxlines {
		line = 1
	}
	if line > f.lastline {
		f.lastline = line
	}

	// Return a fake position assuming that f.file consists only of newlines.
	return token.Pos(f.file.Base() + line - 1)
}

func (s *fakeFileSet) setLines() {
	fakeLinesOnce.Do(func() {
		fakeLines = make([]int, maxlines)
		for i := range fakeLines {
			fakeLines[i] = i
		}
	})
	for _, f := range s.files {
		f.file.SetLines(fakeLines[:f.lastline])
	}
}

var (
	fakeLines     []int
	fakeLinesOnce sync.Once
)

func chanDir(d int) types.ChanDir {
	// tag values must match the constants in cmd/compile/internal/gc/go.go
	switch d {
	case 1 /* Crecv */ :
		return types.RecvOnly
	case 2 /* Csend */ :
		return types.SendOnly
	case 3 /* Cboth */ :
		return types.SendRecv
	default:
		errorf("unexpected channel dir %d", d)
		return 0
	}
}

var predeclOnce sync.Once
var predecl []types.Type // initialized lazily

func predeclared() []types.Type {
	predeclOnce.Do(func() {
		// initialize lazily to be sure that all
		// elements have been initialized before
		predecl = []types.Type{ // basic types
			types.Typ[types.Bool],
			types.Typ[types.Int],
			types.Typ[types.Int8],
			types.Typ[types.Int16],
			types.Typ[types.Int32],
			types.Typ[types.Int64],
			types.Typ[types.Uint],
			types.Typ[types.Uint8],
			types.Typ[types.Uint16],
			types.Typ[types.Uint32],
			types.Typ[types.Uint64],
			types.Typ[types.Uintptr],
			types.Typ[types.Float32],
			types.Typ[types.Float64],
			types.Typ[types.Complex64],
			types.Typ[types.Complex128],
			types.Typ[types.String],

			// basic type aliases
			types.Universe.Lookup("byte").Type(),
			types.Universe.Lookup("rune").Type(),

			// error
			types.Universe.Lookup("error").Type(),

			// untyped types
			types.Typ[types.UntypedBool],
			types.Typ[types.UntypedInt],
			types.Typ[types.UntypedRune],
			types.Typ[types.UntypedFloat],
			types.Typ[types.UntypedComplex],
			types.Typ[types.UntypedString],
			types.Typ[types.UntypedNil],

			// package unsafe
			types.Typ[types.UnsafePointer],

			// invalid type
			types.Typ[types.Invalid], // only appears in packages with errors

			// used internally by gc; never used by this package or in .a files
			anyType{},
		}
		predecl = append(predecl, additionalPredeclared()...)
	})
	return predecl
}

type anyType struct{}

func (t anyType) Underlying() types.Type { return t }
func (t anyType) String() string         { return "any" }
