// GetMetricTypes returns list of available metric types
// It returns error in case retrieval was not successful
func (c *collector) GetMetricTypes(cfg plugin.ConfigType) ([]plugin.MetricType, error) {
	mts := []plugin.MetricType{}

	var err error
	c.allTenants, err = getTenants(cfg)
	if err != nil {
		return nil, err
	}

	// Generate available namespace for limits
	namespaces := []string{}
	for _, tenantName := range c.allTenants {
		// Construct temporary struct to generate namespace based on tags
		var metrics struct {
			S types.Snapshots `json:"snapshots"`
			V types.Volumes   `json:"volumes"`
			L types.Limits    `json:"limits"`
		}
		current := strings.Join([]string{vendor, fs, name, tenantName}, "/")
		ns.FromCompositionTags(metrics, current, &namespaces)
	}

	for _, namespace := range namespaces {
		mts = append(mts, plugin.MetricType{
			Namespace_: core.NewNamespace(strings.Split(namespace, "/")...),
			Config_:    cfg.ConfigDataNode,
		})
	}

	return mts, nil
}
// CollectMetrics retrieves values of requested metrics
func (d *docker) CollectMetrics(mts []plugin.MetricType) ([]plugin.MetricType, error) {
	var err error
	metrics := []plugin.MetricType{}
	d.list = map[string]dock.APIContainers{}

	// get list of possible network metrics
	networkMetrics := []string{}
	utils.FromCompositionTags(wrapper.NetworkInterface{}, "", &networkMetrics)

	// get list of all running containers
	d.list, err = d.client.ListContainersAsMap()
	if err != nil {
		fmt.Fprintln(os.Stderr, "The list of running containers cannot be retrived, err=", err)
		return nil, err
	}

	// retrieve requested docker ids
	rids, err := d.getRequestedIDs(mts...)
	if err != nil {
		return nil, err
	}

	// for each requested id set adequate item into docker.container struct with stats
	for _, rid := range rids {

		if contSpec, exist := d.list[rid]; !exist {
			return nil, fmt.Errorf("Docker container does not exist, container_id=%s", rid)
		} else {
			stats, err := d.client.GetStatsFromContainer(contSpec.ID, true)
			if err != nil {
				return nil, err
			}

			// set new item to docker.container structure
			d.containers[rid] = containerData{
				ID: contSpec.ID,
				Info: wrapper.Specification{
					Status:     contSpec.Status,
					Created:    time.Unix(contSpec.Created, 0).Format("2006-01-02T15:04:05Z07:00"),
					Image:      contSpec.Image,
					SizeRw:     contSpec.SizeRw,
					SizeRootFs: contSpec.SizeRootFs,
					Labels:     contSpec.Labels,
				},
				Stats: stats,
			}

		}
	}

	for _, mt := range mts {
		ids, err := d.getRequestedIDs(mt)
		if err != nil {
			return nil, err
		}

		for _, id := range ids {
			ns := make([]core.NamespaceElement, len(mt.Namespace()))
			copy(ns, mt.Namespace())
			ns[2].Value = id

			// omit "spec" metrics for root
			if id == "root" && mt.Namespace()[lengthOfNsPrefix].Value == "spec" {
				continue
			}
			isDynamic, indexes := mt.Namespace()[lengthOfNsPrefix:].IsDynamic()

			metricName := mt.Namespace().Strings()[lengthOfNsPrefix:]

			// remove added static element (`value`)
			if metricName[len(metricName)-1] == "value" {
				metricName = metricName[:len(metricName)-1]
			}

			if !isDynamic {
				metric := plugin.MetricType{
					Timestamp_: time.Now(),
					Namespace_: ns,
					Data_:      utils.GetValueByNamespace(d.containers[id], metricName),
					Tags_:      mt.Tags(),
					Config_:    mt.Config(),
					Version_:   VERSION,
				}

				metrics = append(metrics, metric)
				continue

			}

			// take the element of metricName which precedes the first dynamic element
			// e.g. {"filesystem", "*", "usage"}
			// 	-> statsType will be "filesystem",
			// 	-> scope of metricName will be decreased to {"*", "usage"}

			indexOfDynamicElement := indexes[0]
			statsType := metricName[indexOfDynamicElement-1]
			metricName = metricName[indexOfDynamicElement:]

			switch statsType {
			case "filesystem":
				// get docker filesystem statistics
				devices := []string{}

				if metricName[0] == "*" {
					// when device name is requested as as asterisk - take all available filesystem devices
					for deviceName := range d.containers[id].Stats.Filesystem {
						devices = append(devices, deviceName)
					}
				} else {
					// device name is requested explicitly
					device := metricName[0]
					if _, ok := d.containers[id].Stats.Filesystem[device]; !ok {
						return nil, fmt.Errorf("In metric %s the given device name is invalid (no stats for this device)", mt.Namespace().String())
					}

					devices = append(devices, metricName[0])
				}

				for _, device := range devices {
					rns := make([]core.NamespaceElement, len(ns))
					copy(rns, ns)

					rns[indexOfDynamicElement+lengthOfNsPrefix].Value = device

					metric := plugin.MetricType{
						Timestamp_: time.Now(),
						Namespace_: rns,
						Data_:      utils.GetValueByNamespace(d.containers[id].Stats.Filesystem[device], metricName[1:]),
						Tags_:      mt.Tags(),
						Config_:    mt.Config(),
						Version_:   VERSION,
					}
					metrics = append(metrics, metric)
				}

			case "labels":
				// get docker labels
				labelKeys := []string{}
				if metricName[0] == "*" {
					// when label key is requested as an asterisk - take all available labels
					for labelKey := range d.containers[id].Info.Labels {
						labelKeys = append(labelKeys, labelKey)
					}
				} else {
					labelKey := metricName[0]
					if _, ok := d.containers[id].Info.Labels[labelKey]; !ok {
						return nil, fmt.Errorf("In metric %s the given label is invalid (no value for this label key)", mt.Namespace().String())
					}

					labelKeys = append(labelKeys, metricName[0])
				}

				for _, labelKey := range labelKeys {
					rns := make([]core.NamespaceElement, len(ns))
					copy(rns, ns)
					rns[indexOfDynamicElement+lengthOfNsPrefix].Value = utils.ReplaceNotAllowedCharsInNamespacePart(labelKey)
					metric := plugin.MetricType{
						Timestamp_: time.Now(),
						Namespace_: rns,
						Data_:      d.containers[id].Info.Labels[labelKey],
						Tags_:      mt.Tags(),
						Config_:    mt.Config(),
						Version_:   VERSION,
					}

					metrics = append(metrics, metric)
				}

			case "network":
				//get docker network tx/rx statistics
				netInterfaces := []string{}
				ifaceMap := map[string]wrapper.NetworkInterface{}
				for _, iface := range d.containers[id].Stats.Network {
					ifaceMap[iface.Name] = iface
				}

				// support wildcard on interface name
				if metricName[0] == "*" {
					for _, netInterface := range d.containers[id].Stats.Network {
						netInterfaces = append(netInterfaces, netInterface.Name)
					}
				} else {
					netInterface := metricName[0]
					if _, ok := ifaceMap[netInterface]; !ok {
						return nil, fmt.Errorf("In metric %s the given network interface is invalid (no stats for this net interface)", mt.Namespace().String())
					}
					netInterfaces = append(netInterfaces, metricName[0])
				}

				for _, ifaceName := range netInterfaces {
					rns := make([]core.NamespaceElement, len(ns))
					copy(rns, ns)
					rns[indexOfDynamicElement+lengthOfNsPrefix].Value = ifaceName
					metric := plugin.MetricType{
						Timestamp_: time.Now(),
						Namespace_: rns,
						Data_:      utils.GetValueByNamespace(ifaceMap[ifaceName], metricName[1:]),
						Tags_:      mt.Tags(),
						Config_:    mt.Config(),
						Version_:   VERSION,
					}
					metrics = append(metrics, metric)
				}

			case "percpu_usage":
				numOfCPUs := len(d.containers[id].Stats.CgroupStats.CpuStats.CpuUsage.PercpuUsage) - 1
				if metricName[0] == "*" {
					// when cpu ID is requested as an asterisk - take all available
					for cpuID, val := range d.containers[id].Stats.CgroupStats.CpuStats.CpuUsage.PercpuUsage {
						rns := make([]core.NamespaceElement, len(ns))
						copy(rns, ns)

						rns[indexOfDynamicElement+lengthOfNsPrefix].Value = strconv.Itoa(cpuID)

						metric := plugin.MetricType{
							Timestamp_: time.Now(),
							Namespace_: rns,
							Data_:      val,
							Tags_:      mt.Tags(),
							Config_:    mt.Config(),
							Version_:   VERSION,
						}
						metrics = append(metrics, metric)
					}
				} else {
					cpuID, err := strconv.Atoi(metricName[0])
					if err != nil {
						return nil, fmt.Errorf("In metric %s the given cpu id is invalid, err=%v", mt.Namespace().String(), err)
					}
					if cpuID > numOfCPUs || cpuID < 0 {
						return nil, fmt.Errorf("In metric %s the given cpu id is invalid, expected value in range 0-%d", mt.Namespace().String(), numOfCPUs)
					}

					metric := plugin.MetricType{
						Timestamp_: time.Now(),
						Namespace_: ns,
						Data_:      d.containers[id].Stats.CgroupStats.CpuStats.CpuUsage.PercpuUsage[cpuID],
						Tags_:      mt.Tags(),
						Config_:    mt.Config(),
						Version_:   VERSION,
					}
					metrics = append(metrics, metric)
				}

			} // the end of switch statsType
		} // the end of range over ids
	}

	if len(metrics) == 0 {
		return nil, errors.New("No metric found")
	}

	return metrics, nil
}
func getListOfNetworkMetrics() []string {
	metrics := []string{}
	utils.FromCompositionTags(wrapper.NetworkInterface{}, "", &metrics)
	return metrics
}