// TODO: show that two-non-empty dotjoin can happen, by using an anon struct as a field type
// TODO: don't report removed/changed methods for both value and pointer method sets?

package apidiff

import (
	
	
	
	
)

// objectWithSide contains an object, and information on which side (old or new)
// of the comparison it relates to. This matters when need to express the object's
// package path, relative to the root path of the comparison, as the old and new
// sides can have different roots (e.g. comparing somepackage/v2 vs. somepackage/v3).
type objectWithSide struct {
	object types.Object
	isNew  bool
}

// There can be at most one message for each object or part thereof.
// Parts include interface methods and struct fields.
//
// The part thing is necessary. Method (Func) objects have sufficient info, but field
// Vars do not: they just have a field name and a type, without the enclosing struct.
type messageSet map[objectWithSide]map[string]string

// Add a message for obj and part, overwriting a previous message
// (shouldn't happen).
// obj is required but part can be empty.
func ( messageSet) ( objectWithSide, ,  string) {
	 := []
	if  == nil {
		 = map[string]string{}
		[] = 
	}
	if ,  := [];  &&  !=  {
		fmt.Printf("! second, different message for obj %s, isNew %v, part %q\n", .object, .isNew, )
		fmt.Printf("  first:  %s\n", )
		fmt.Printf("  second: %s\n", )
	}
	[] = 
}

func ( messageSet) (,  string) []string {
	var  []string
	for ,  := range  {
		 := 
		if .isNew {
			 = 
		}

		// Format each object name relative to its own package.
		 := objectString(.object, )
		for ,  := range  {
			var  string

			if strings.HasPrefix(, ",") {
				 =  + 
			} else {
				 = dotjoin(, )
			}
			 = append(, +": "+)
		}
	}
	sort.Strings()
	return 
}

func ( types.Object,  string) string {
	 := .Pkg().Path()

	var  string
	if  ==  {
		// obj is in same package as the diff operation root - no prefix
		 = ""
	} else if strings.HasPrefix(, +"/") {
		// obj is in a child package compared to the diff operation root - use a
		// prefix starting with "./" to emphasise the relative nature
		 = "./" + [len()+1:] + "."
	} else {
		// obj is outside the diff operation root - display full path. This can
		// happen if there is a need to report a change in a type in an unrelated
		// package, because it has been used as the underlying type in a type
		// definition in the package being processed, for example.
		 =  + "."
	}

	if ,  := .(*types.Func);  {
		 := .Type().(*types.Signature)
		if  := .Recv();  != nil {
			 := types.TypeString(.Type(), types.RelativeTo(.Pkg()))
			if [0] == '*' {
				 = "(" +  + ")"
			}
			return fmt.Sprintf("%s%s.%s", , , .Name())
		}
	}
	return fmt.Sprintf("%s%s", , .Name())
}

func (,  string) string {
	if  == "" {
		return 
	}
	if  == "" {
		return 
	}
	return  + "." + 
}