/*
 *
 * Copyright 2021 gRPC 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 xdsclient

import anypb "github.com/golang/protobuf/ptypes/any"

// UpdateWithMD contains the raw message of the update and the metadata,
// including version, raw message, timestamp.
//
// This is to be used for config dump and CSDS, not directly by users (like
// resolvers/balancers).
type UpdateWithMD struct {
	MD  UpdateMetadata
	Raw *anypb.Any
}

func rawFromCache(s string, cache interface{}) *anypb.Any {
	switch c := cache.(type) {
	case map[string]ListenerUpdate:
		v, ok := c[s]
		if !ok {
			return nil
		}
		return v.Raw
	case map[string]RouteConfigUpdate:
		v, ok := c[s]
		if !ok {
			return nil
		}
		return v.Raw
	case map[string]ClusterUpdate:
		v, ok := c[s]
		if !ok {
			return nil
		}
		return v.Raw
	case map[string]EndpointsUpdate:
		v, ok := c[s]
		if !ok {
			return nil
		}
		return v.Raw
	default:
		return nil
	}
}

func (c *clientImpl) dump(t ResourceType) (string, map[string]UpdateWithMD) {
	c.mu.Lock()
	defer c.mu.Unlock()

	var (
		version string
		md      map[string]UpdateMetadata
		cache   interface{}
	)
	switch t {
	case ListenerResource:
		version = c.ldsVersion
		md = c.ldsMD
		cache = c.ldsCache
	case RouteConfigResource:
		version = c.rdsVersion
		md = c.rdsMD
		cache = c.rdsCache
	case ClusterResource:
		version = c.cdsVersion
		md = c.cdsMD
		cache = c.cdsCache
	case EndpointsResource:
		version = c.edsVersion
		md = c.edsMD
		cache = c.edsCache
	default:
		c.logger.Errorf("dumping resource of unknown type: %v", t)
		return "", nil
	}

	ret := make(map[string]UpdateWithMD, len(md))
	for s, md := range md {
		ret[s] = UpdateWithMD{
			MD:  md,
			Raw: rawFromCache(s, cache),
		}
	}
	return version, ret
}

// DumpLDS returns the status and contents of LDS.
func (c *clientImpl) DumpLDS() (string, map[string]UpdateWithMD) {
	return c.dump(ListenerResource)
}

// DumpRDS returns the status and contents of RDS.
func (c *clientImpl) DumpRDS() (string, map[string]UpdateWithMD) {
	return c.dump(RouteConfigResource)
}

// DumpCDS returns the status and contents of CDS.
func (c *clientImpl) DumpCDS() (string, map[string]UpdateWithMD) {
	return c.dump(ClusterResource)
}

// DumpEDS returns the status and contents of EDS.
func (c *clientImpl) DumpEDS() (string, map[string]UpdateWithMD) {
	return c.dump(EndpointsResource)
}
