/*
 * Copyright 2021 CloudWeGo Authors
 *
 * 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 rpcinfo

type plainRPCInfo struct {
	// use anonymous structs to force objects to be read-only
	from struct{ EndpointInfo }
	to   struct{ EndpointInfo }
	inv  struct{ Invocation }
	cfg  struct{ RPCConfig }
}

func (p *plainRPCInfo) From() EndpointInfo {
	return &p.from
}

func (p *plainRPCInfo) To() EndpointInfo {
	return &p.to
}

func (p *plainRPCInfo) Invocation() Invocation {
	return &p.inv
}

func (p *plainRPCInfo) Config() RPCConfig {
	return &p.cfg
}

func (p *plainRPCInfo) Stats() RPCStats {
	return nil
}

func freeze(ri RPCInfo) RPCInfo {
	p := new(plainRPCInfo)
	p.from.EndpointInfo = copyEndpointInfo(ri.From())
	p.to.EndpointInfo = copyEndpointInfo(ri.To())
	p.inv.Invocation = copyInvocation(ri.Invocation())
	p.cfg.RPCConfig = copyRPCConfig(ri.Config())
	return p
}

func copyMap(src map[string]string) map[string]string {
	dst := make(map[string]string, len(src))
	for k, v := range src {
		dst[k] = v
	}
	return dst
}

func copyEndpointInfo(ei EndpointInfo) EndpointInfo {
	if ei == nil {
		return nil
	}
	p := &endpointInfo{
		serviceName: ei.ServiceName(),
		method:      ei.Method(),
		address:     ei.Address(),
	}
	if v, ok := ei.(*endpointInfo); ok {
		p.tags = copyMap(v.tags)
	} else {
		p.tags = make(map[string]string)
	}
	return p
}

func copyInvocation(i Invocation) Invocation {
	if i == nil {
		return nil
	}
	ink := i.(*invocation)
	nink := &invocation{
		packageName:   ink.PackageName(),
		serviceName:   ink.ServiceName(),
		methodName:    ink.MethodName(),
		methodInfo:    ink.MethodInfo(),
		streamingMode: ink.StreamingMode(),
		seqID:         ink.SeqID(),
		// ignore extra info to users
	}
	nink.SetBizStatusErr(ink.BizStatusErr())
	return nink
}

func copyRPCConfig(cfg RPCConfig) RPCConfig {
	if cfg == nil {
		return nil
	}
	return &rpcConfig{
		rpcTimeout:        cfg.RPCTimeout(),
		connectTimeout:    cfg.ConnectTimeout(),
		readWriteTimeout:  cfg.ReadWriteTimeout(),
		ioBufferSize:      cfg.IOBufferSize(),
		transportProtocol: cfg.TransportProtocol(),
		interactionMode:   cfg.InteractionMode(),
	}
}
