// Package astcopy implements Go AST reflection-free deep copy operations.
package astcopy

import (
	"go/ast"

	"golang.org/x/exp/typeparams"
)

// Node returns x node deep copy.
// Copy of nil argument is nil.
func Node(x ast.Node) ast.Node {
	return copyNode(x)
}

// NodeList returns xs node slice deep copy.
// Copy of nil argument is nil.
func NodeList(xs []ast.Node) []ast.Node {
	if xs == nil {
		return nil
	}
	cp := make([]ast.Node, len(xs))
	for i := range xs {
		cp[i] = copyNode(xs[i])
	}
	return cp
}

// Expr returns x expression deep copy.
// Copy of nil argument is nil.
func Expr(x ast.Expr) ast.Expr {
	return copyExpr(x)
}

// ExprList returns xs expression slice deep copy.
// Copy of nil argument is nil.
func ExprList(xs []ast.Expr) []ast.Expr {
	if xs == nil {
		return nil
	}
	cp := make([]ast.Expr, len(xs))
	for i := range xs {
		cp[i] = copyExpr(xs[i])
	}
	return cp
}

// Stmt returns x statement deep copy.
// Copy of nil argument is nil.
func Stmt(x ast.Stmt) ast.Stmt {
	return copyStmt(x)
}

// StmtList returns xs statement slice deep copy.
// Copy of nil argument is nil.
func StmtList(xs []ast.Stmt) []ast.Stmt {
	if xs == nil {
		return nil
	}
	cp := make([]ast.Stmt, len(xs))
	for i := range xs {
		cp[i] = copyStmt(xs[i])
	}
	return cp
}

// Decl returns x declaration deep copy.
// Copy of nil argument is nil.
func Decl(x ast.Decl) ast.Decl {
	return copyDecl(x)
}

// DeclList returns xs declaration slice deep copy.
// Copy of nil argument is nil.
func DeclList(xs []ast.Decl) []ast.Decl {
	if xs == nil {
		return nil
	}
	cp := make([]ast.Decl, len(xs))
	for i := range xs {
		cp[i] = copyDecl(xs[i])
	}
	return cp
}

// BadExpr returns x deep copy.
// Copy of nil argument is nil.
func BadExpr(x *ast.BadExpr) *ast.BadExpr {
	if x == nil {
		return nil
	}
	cp := *x
	return &cp
}

// Ident returns x deep copy.
// Copy of nil argument is nil.
func Ident(x *ast.Ident) *ast.Ident {
	if x == nil {
		return nil
	}
	cp := *x
	return &cp
}

// IdentList returns xs identifier slice deep copy.
// Copy of nil argument is nil.
func IdentList(xs []*ast.Ident) []*ast.Ident {
	if xs == nil {
		return nil
	}
	cp := make([]*ast.Ident, len(xs))
	for i := range xs {
		cp[i] = Ident(xs[i])
	}
	return cp
}

// Ellipsis returns x deep copy.
// Copy of nil argument is nil.
func Ellipsis(x *ast.Ellipsis) *ast.Ellipsis {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Elt = copyExpr(x.Elt)
	return &cp
}

// BasicLit returns x deep copy.
// Copy of nil argument is nil.
func BasicLit(x *ast.BasicLit) *ast.BasicLit {
	if x == nil {
		return nil
	}
	cp := *x
	return &cp
}

// FuncLit returns x deep copy.
// Copy of nil argument is nil.
func FuncLit(x *ast.FuncLit) *ast.FuncLit {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Type = FuncType(x.Type)
	cp.Body = BlockStmt(x.Body)
	return &cp
}

// CompositeLit returns x deep copy.
// Copy of nil argument is nil.
func CompositeLit(x *ast.CompositeLit) *ast.CompositeLit {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Type = copyExpr(x.Type)
	cp.Elts = ExprList(x.Elts)
	return &cp
}

// ParenExpr returns x deep copy.
// Copy of nil argument is nil.
func ParenExpr(x *ast.ParenExpr) *ast.ParenExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	return &cp
}

// SelectorExpr returns x deep copy.
// Copy of nil argument is nil.
func SelectorExpr(x *ast.SelectorExpr) *ast.SelectorExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	cp.Sel = Ident(x.Sel)
	return &cp
}

// IndexExpr returns x deep copy.
// Copy of nil argument is nil.
func IndexExpr(x *ast.IndexExpr) *ast.IndexExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	cp.Index = copyExpr(x.Index)
	return &cp
}

// IndexListExpr returns x deep copy.
// Copy of nil argument is nil.
func IndexListExpr(x *typeparams.IndexListExpr) *typeparams.IndexListExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	cp.Indices = ExprList(x.Indices)
	return &cp
}

// SliceExpr returns x deep copy.
// Copy of nil argument is nil.
func SliceExpr(x *ast.SliceExpr) *ast.SliceExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	cp.Low = copyExpr(x.Low)
	cp.High = copyExpr(x.High)
	cp.Max = copyExpr(x.Max)
	return &cp
}

// TypeAssertExpr returns x deep copy.
// Copy of nil argument is nil.
func TypeAssertExpr(x *ast.TypeAssertExpr) *ast.TypeAssertExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	cp.Type = copyExpr(x.Type)
	return &cp
}

// CallExpr returns x deep copy.
// Copy of nil argument is nil.
func CallExpr(x *ast.CallExpr) *ast.CallExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Fun = copyExpr(x.Fun)
	cp.Args = ExprList(x.Args)
	return &cp
}

// StarExpr returns x deep copy.
// Copy of nil argument is nil.
func StarExpr(x *ast.StarExpr) *ast.StarExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	return &cp
}

// UnaryExpr returns x deep copy.
// Copy of nil argument is nil.
func UnaryExpr(x *ast.UnaryExpr) *ast.UnaryExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	return &cp
}

// BinaryExpr returns x deep copy.
// Copy of nil argument is nil.
func BinaryExpr(x *ast.BinaryExpr) *ast.BinaryExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	cp.Y = copyExpr(x.Y)
	return &cp
}

// KeyValueExpr returns x deep copy.
// Copy of nil argument is nil.
func KeyValueExpr(x *ast.KeyValueExpr) *ast.KeyValueExpr {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Key = copyExpr(x.Key)
	cp.Value = copyExpr(x.Value)
	return &cp
}

// ArrayType returns x deep copy.
// Copy of nil argument is nil.
func ArrayType(x *ast.ArrayType) *ast.ArrayType {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Len = copyExpr(x.Len)
	cp.Elt = copyExpr(x.Elt)
	return &cp
}

// StructType returns x deep copy.
// Copy of nil argument is nil.
func StructType(x *ast.StructType) *ast.StructType {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Fields = FieldList(x.Fields)
	return &cp
}

// Field returns x deep copy.
// Copy of nil argument is nil.
func Field(x *ast.Field) *ast.Field {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Names = IdentList(x.Names)
	cp.Type = copyExpr(x.Type)
	cp.Tag = BasicLit(x.Tag)
	cp.Doc = CommentGroup(x.Doc)
	cp.Comment = CommentGroup(x.Comment)
	return &cp
}

// FieldList returns x deep copy.
// Copy of nil argument is nil.
func FieldList(x *ast.FieldList) *ast.FieldList {
	if x == nil {
		return nil
	}
	cp := *x
	if x.List != nil {
		cp.List = make([]*ast.Field, len(x.List))
		for i := range x.List {
			cp.List[i] = Field(x.List[i])
		}
	}
	return &cp
}

// InterfaceType returns x deep copy.
// Copy of nil argument is nil.
func InterfaceType(x *ast.InterfaceType) *ast.InterfaceType {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Methods = FieldList(x.Methods)
	return &cp
}

// MapType returns x deep copy.
// Copy of nil argument is nil.
func MapType(x *ast.MapType) *ast.MapType {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Key = copyExpr(x.Key)
	cp.Value = copyExpr(x.Value)
	return &cp
}

// ChanType returns x deep copy.
// Copy of nil argument is nil.
func ChanType(x *ast.ChanType) *ast.ChanType {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Value = copyExpr(x.Value)
	return &cp
}

// BlockStmt returns x deep copy.
// Copy of nil argument is nil.
func BlockStmt(x *ast.BlockStmt) *ast.BlockStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.List = StmtList(x.List)
	return &cp
}

// ImportSpec returns x deep copy.
// Copy of nil argument is nil.
func ImportSpec(x *ast.ImportSpec) *ast.ImportSpec {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Name = Ident(x.Name)
	cp.Path = BasicLit(x.Path)
	cp.Doc = CommentGroup(x.Doc)
	cp.Comment = CommentGroup(x.Comment)
	return &cp
}

// ValueSpec returns x deep copy.
// Copy of nil argument is nil.
func ValueSpec(x *ast.ValueSpec) *ast.ValueSpec {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Names = IdentList(x.Names)
	cp.Values = ExprList(x.Values)
	cp.Type = copyExpr(x.Type)
	cp.Doc = CommentGroup(x.Doc)
	cp.Comment = CommentGroup(x.Comment)
	return &cp
}

// Spec returns x deep copy.
// Copy of nil argument is nil.
func Spec(x ast.Spec) ast.Spec {
	if x == nil {
		return nil
	}

	switch x := x.(type) {
	case *ast.ImportSpec:
		return ImportSpec(x)
	case *ast.ValueSpec:
		return ValueSpec(x)
	case *ast.TypeSpec:
		return TypeSpec(x)
	default:
		panic("unhandled spec")
	}
}

// SpecList returns xs spec slice deep copy.
// Copy of nil argument is nil.
func SpecList(xs []ast.Spec) []ast.Spec {
	if xs == nil {
		return nil
	}
	cp := make([]ast.Spec, len(xs))
	for i := range xs {
		cp[i] = Spec(xs[i])
	}
	return cp
}

// BadStmt returns x deep copy.
// Copy of nil argument is nil.
func BadStmt(x *ast.BadStmt) *ast.BadStmt {
	if x == nil {
		return nil
	}
	cp := *x
	return &cp
}

// DeclStmt returns x deep copy.
// Copy of nil argument is nil.
func DeclStmt(x *ast.DeclStmt) *ast.DeclStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Decl = copyDecl(x.Decl)
	return &cp
}

// EmptyStmt returns x deep copy.
// Copy of nil argument is nil.
func EmptyStmt(x *ast.EmptyStmt) *ast.EmptyStmt {
	if x == nil {
		return nil
	}
	cp := *x
	return &cp
}

// LabeledStmt returns x deep copy.
// Copy of nil argument is nil.
func LabeledStmt(x *ast.LabeledStmt) *ast.LabeledStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Label = Ident(x.Label)
	cp.Stmt = copyStmt(x.Stmt)
	return &cp
}

// ExprStmt returns x deep copy.
// Copy of nil argument is nil.
func ExprStmt(x *ast.ExprStmt) *ast.ExprStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	return &cp
}

// SendStmt returns x deep copy.
// Copy of nil argument is nil.
func SendStmt(x *ast.SendStmt) *ast.SendStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Chan = copyExpr(x.Chan)
	cp.Value = copyExpr(x.Value)
	return &cp
}

// IncDecStmt returns x deep copy.
// Copy of nil argument is nil.
func IncDecStmt(x *ast.IncDecStmt) *ast.IncDecStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.X = copyExpr(x.X)
	return &cp
}

// AssignStmt returns x deep copy.
// Copy of nil argument is nil.
func AssignStmt(x *ast.AssignStmt) *ast.AssignStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Lhs = ExprList(x.Lhs)
	cp.Rhs = ExprList(x.Rhs)
	return &cp
}

// GoStmt returns x deep copy.
// Copy of nil argument is nil.
func GoStmt(x *ast.GoStmt) *ast.GoStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Call = CallExpr(x.Call)
	return &cp
}

// DeferStmt returns x deep copy.
// Copy of nil argument is nil.
func DeferStmt(x *ast.DeferStmt) *ast.DeferStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Call = CallExpr(x.Call)
	return &cp
}

// ReturnStmt returns x deep copy.
// Copy of nil argument is nil.
func ReturnStmt(x *ast.ReturnStmt) *ast.ReturnStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Results = ExprList(x.Results)
	return &cp
}

// BranchStmt returns x deep copy.
// Copy of nil argument is nil.
func BranchStmt(x *ast.BranchStmt) *ast.BranchStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Label = Ident(x.Label)
	return &cp
}

// IfStmt returns x deep copy.
// Copy of nil argument is nil.
func IfStmt(x *ast.IfStmt) *ast.IfStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Init = copyStmt(x.Init)
	cp.Cond = copyExpr(x.Cond)
	cp.Body = BlockStmt(x.Body)
	cp.Else = copyStmt(x.Else)
	return &cp
}

// CaseClause returns x deep copy.
// Copy of nil argument is nil.
func CaseClause(x *ast.CaseClause) *ast.CaseClause {
	if x == nil {
		return nil
	}
	cp := *x
	cp.List = ExprList(x.List)
	cp.Body = StmtList(x.Body)
	return &cp
}

// SwitchStmt returns x deep copy.
// Copy of nil argument is nil.
func SwitchStmt(x *ast.SwitchStmt) *ast.SwitchStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Init = copyStmt(x.Init)
	cp.Tag = copyExpr(x.Tag)
	cp.Body = BlockStmt(x.Body)
	return &cp
}

// TypeSwitchStmt returns x deep copy.
// Copy of nil argument is nil.
func TypeSwitchStmt(x *ast.TypeSwitchStmt) *ast.TypeSwitchStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Init = copyStmt(x.Init)
	cp.Assign = copyStmt(x.Assign)
	cp.Body = BlockStmt(x.Body)
	return &cp
}

// CommClause returns x deep copy.
// Copy of nil argument is nil.
func CommClause(x *ast.CommClause) *ast.CommClause {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Comm = copyStmt(x.Comm)
	cp.Body = StmtList(x.Body)
	return &cp
}

// SelectStmt returns x deep copy.
// Copy of nil argument is nil.
func SelectStmt(x *ast.SelectStmt) *ast.SelectStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Body = BlockStmt(x.Body)
	return &cp
}

// ForStmt returns x deep copy.
// Copy of nil argument is nil.
func ForStmt(x *ast.ForStmt) *ast.ForStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Init = copyStmt(x.Init)
	cp.Cond = copyExpr(x.Cond)
	cp.Post = copyStmt(x.Post)
	cp.Body = BlockStmt(x.Body)
	return &cp
}

// RangeStmt returns x deep copy.
// Copy of nil argument is nil.
func RangeStmt(x *ast.RangeStmt) *ast.RangeStmt {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Key = copyExpr(x.Key)
	cp.Value = copyExpr(x.Value)
	cp.X = copyExpr(x.X)
	cp.Body = BlockStmt(x.Body)
	return &cp
}

// Comment returns x deep copy.
// Copy of nil argument is nil.
func Comment(x *ast.Comment) *ast.Comment {
	if x == nil {
		return nil
	}
	cp := *x
	return &cp
}

// CommentGroup returns x deep copy.
// Copy of nil argument is nil.
func CommentGroup(x *ast.CommentGroup) *ast.CommentGroup {
	if x == nil {
		return nil
	}
	cp := *x
	if x.List != nil {
		cp.List = make([]*ast.Comment, len(x.List))
		for i := range x.List {
			cp.List[i] = Comment(x.List[i])
		}
	}
	return &cp
}

// File returns x deep copy.
// Copy of nil argument is nil.
func File(x *ast.File) *ast.File {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Doc = CommentGroup(x.Doc)
	cp.Name = Ident(x.Name)
	cp.Decls = DeclList(x.Decls)
	cp.Imports = make([]*ast.ImportSpec, len(x.Imports))
	for i := range x.Imports {
		cp.Imports[i] = ImportSpec(x.Imports[i])
	}
	cp.Unresolved = IdentList(x.Unresolved)
	cp.Comments = make([]*ast.CommentGroup, len(x.Comments))
	for i := range x.Comments {
		cp.Comments[i] = CommentGroup(x.Comments[i])
	}
	return &cp
}

// Package returns x deep copy.
// Copy of nil argument is nil.
func Package(x *ast.Package) *ast.Package {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Files = make(map[string]*ast.File)
	for filename, f := range x.Files {
		cp.Files[filename] = f
	}
	return &cp
}

// BadDecl returns x deep copy.
// Copy of nil argument is nil.
func BadDecl(x *ast.BadDecl) *ast.BadDecl {
	if x == nil {
		return nil
	}
	cp := *x
	return &cp
}

// GenDecl returns x deep copy.
// Copy of nil argument is nil.
func GenDecl(x *ast.GenDecl) *ast.GenDecl {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Specs = SpecList(x.Specs)
	cp.Doc = CommentGroup(x.Doc)
	return &cp
}

// FuncDecl returns x deep copy.
// Copy of nil argument is nil.
func FuncDecl(x *ast.FuncDecl) *ast.FuncDecl {
	if x == nil {
		return nil
	}
	cp := *x
	cp.Recv = FieldList(x.Recv)
	cp.Name = Ident(x.Name)
	cp.Type = FuncType(x.Type)
	cp.Body = BlockStmt(x.Body)
	cp.Doc = CommentGroup(x.Doc)
	return &cp
}

func copyNode(x ast.Node) ast.Node {
	switch x := x.(type) {
	case ast.Expr:
		return copyExpr(x)
	case ast.Stmt:
		return copyStmt(x)
	case ast.Decl:
		return copyDecl(x)

	case ast.Spec:
		return Spec(x)
	case *ast.FieldList:
		return FieldList(x)
	case *ast.Comment:
		return Comment(x)
	case *ast.CommentGroup:
		return CommentGroup(x)
	case *ast.File:
		return File(x)
	case *ast.Package:
		return Package(x)

	default:
		panic("unhandled node")
	}
}

func copyExpr(x ast.Expr) ast.Expr {
	if x == nil {
		return nil
	}

	switch x := x.(type) {
	case *ast.BadExpr:
		return BadExpr(x)
	case *ast.Ident:
		return Ident(x)
	case *ast.Ellipsis:
		return Ellipsis(x)
	case *ast.BasicLit:
		return BasicLit(x)
	case *ast.FuncLit:
		return FuncLit(x)
	case *ast.CompositeLit:
		return CompositeLit(x)
	case *ast.ParenExpr:
		return ParenExpr(x)
	case *ast.SelectorExpr:
		return SelectorExpr(x)
	case *ast.IndexExpr:
		return IndexExpr(x)
	case *typeparams.IndexListExpr:
		return IndexListExpr(x)
	case *ast.SliceExpr:
		return SliceExpr(x)
	case *ast.TypeAssertExpr:
		return TypeAssertExpr(x)
	case *ast.CallExpr:
		return CallExpr(x)
	case *ast.StarExpr:
		return StarExpr(x)
	case *ast.UnaryExpr:
		return UnaryExpr(x)
	case *ast.BinaryExpr:
		return BinaryExpr(x)
	case *ast.KeyValueExpr:
		return KeyValueExpr(x)
	case *ast.ArrayType:
		return ArrayType(x)
	case *ast.StructType:
		return StructType(x)
	case *ast.FuncType:
		return FuncType(x)
	case *ast.InterfaceType:
		return InterfaceType(x)
	case *ast.MapType:
		return MapType(x)
	case *ast.ChanType:
		return ChanType(x)

	default:
		panic("unhandled expr")
	}
}

func copyStmt(x ast.Stmt) ast.Stmt {
	if x == nil {
		return nil
	}

	switch x := x.(type) {
	case *ast.BadStmt:
		return BadStmt(x)
	case *ast.DeclStmt:
		return DeclStmt(x)
	case *ast.EmptyStmt:
		return EmptyStmt(x)
	case *ast.LabeledStmt:
		return LabeledStmt(x)
	case *ast.ExprStmt:
		return ExprStmt(x)
	case *ast.SendStmt:
		return SendStmt(x)
	case *ast.IncDecStmt:
		return IncDecStmt(x)
	case *ast.AssignStmt:
		return AssignStmt(x)
	case *ast.GoStmt:
		return GoStmt(x)
	case *ast.DeferStmt:
		return DeferStmt(x)
	case *ast.ReturnStmt:
		return ReturnStmt(x)
	case *ast.BranchStmt:
		return BranchStmt(x)
	case *ast.BlockStmt:
		return BlockStmt(x)
	case *ast.IfStmt:
		return IfStmt(x)
	case *ast.CaseClause:
		return CaseClause(x)
	case *ast.SwitchStmt:
		return SwitchStmt(x)
	case *ast.TypeSwitchStmt:
		return TypeSwitchStmt(x)
	case *ast.CommClause:
		return CommClause(x)
	case *ast.SelectStmt:
		return SelectStmt(x)
	case *ast.ForStmt:
		return ForStmt(x)
	case *ast.RangeStmt:
		return RangeStmt(x)

	default:
		panic("unhandled stmt")
	}
}

func copyDecl(x ast.Decl) ast.Decl {
	if x == nil {
		return nil
	}

	switch x := x.(type) {
	case *ast.BadDecl:
		return BadDecl(x)
	case *ast.GenDecl:
		return GenDecl(x)
	case *ast.FuncDecl:
		return FuncDecl(x)

	default:
		panic("unhandled decl")
	}
}
