// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you 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 plugins

import (
	"fmt"
	"path/filepath"

	"github.com/apache/skywalking-go/plugins/core/instrument"
	"github.com/apache/skywalking-go/tools/go-agent/tools"

	"github.com/dave/dst"
)

var EnhanceInstanceField = "skywalkingDynamicField"

type InstanceEnhance struct {
	packageName string
	typeSpec    *dst.TypeSpec
}

func NewInstanceEnhance(typeSpec *dst.TypeSpec, basePackage string, p *instrument.Point) *InstanceEnhance {
	pkgName := filepath.Base(filepath.Join(basePackage, p.PackagePath))
	if p.PackageName != "" {
		pkgName = p.PackageName
	}
	return &InstanceEnhance{
		packageName: pkgName,
		typeSpec:    typeSpec,
	}
}

func (i *InstanceEnhance) PackageName() string {
	return i.packageName
}

func (i *InstanceEnhance) EnhanceField() {
	structType := i.typeSpec.Type.(*dst.StructType)
	structType.Fields.List = append(structType.Fields.List, &dst.Field{
		Names: []*dst.Ident{dst.NewIdent(EnhanceInstanceField)},
		Type:  dst.NewIdent("interface{}"),
	})
}

func (i *InstanceEnhance) BuildImports(decl *dst.GenDecl) {
}

func (i *InstanceEnhance) BuildForDelegator() []dst.Decl {
	return []dst.Decl{
		&dst.FuncDecl{
			Name: &dst.Ident{Name: "GetSkyWalkingDynamicField"},
			Recv: &dst.FieldList{
				List: []*dst.Field{
					{
						Names: []*dst.Ident{dst.NewIdent("receiver")},
						Type:  &dst.StarExpr{X: &dst.SelectorExpr{X: dst.NewIdent(i.packageName), Sel: dst.NewIdent(i.typeSpec.Name.Name)}},
					},
				},
			},
			Type: &dst.FuncType{
				Params: &dst.FieldList{},
				Results: &dst.FieldList{
					List: []*dst.Field{
						{Type: dst.NewIdent("interface{}")},
					},
				},
			},
			Body: &dst.BlockStmt{
				List: tools.GoStringToStats(fmt.Sprintf("return receiver.%s", EnhanceInstanceField)),
			},
		},
		&dst.FuncDecl{
			Name: &dst.Ident{Name: "SetSkyWalkingDynamicField"},
			Recv: &dst.FieldList{
				List: []*dst.Field{
					{
						Names: []*dst.Ident{dst.NewIdent("receiver")},
						Type:  &dst.StarExpr{X: &dst.SelectorExpr{X: dst.NewIdent(i.packageName), Sel: dst.NewIdent(i.typeSpec.Name.Name)}},
					},
				},
			},
			Type: &dst.FuncType{
				Params: &dst.FieldList{
					List: []*dst.Field{
						{
							Names: []*dst.Ident{dst.NewIdent("param")},
							Type:  dst.NewIdent("interface{}"),
						},
					},
				},
				Results: &dst.FieldList{},
			},
			Body: &dst.BlockStmt{
				List: tools.GoStringToStats(fmt.Sprintf("receiver.%s = param", EnhanceInstanceField)),
			},
		},
	}
}

func (i *InstanceEnhance) ReplaceFileContent(path, content string) string {
	return content
}

func (i *InstanceEnhance) InitFunctions() []*EnhanceInitFunction {
	return nil
}
