package api

import (
	"fmt"

	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/extensions"

	imageapi "github.com/openshift/origin/pkg/image/api"
)

// DeploymentToPodLogOptions builds a PodLogOptions object out of a DeploymentLogOptions.
// Currently DeploymentLogOptions.Container and DeploymentLogOptions.Previous aren't used
// so they won't be copied to PodLogOptions.
//
// Note that Previous for PodLogOptions is different from Previous for DeploymentLogOptions
// so it shouldn't be included here.
func DeploymentToPodLogOptions(opts *DeploymentLogOptions) *kapi.PodLogOptions {
	return &kapi.PodLogOptions{
		Follow:       opts.Follow,
		SinceSeconds: opts.SinceSeconds,
		SinceTime:    opts.SinceTime,
		Timestamps:   opts.Timestamps,
		TailLines:    opts.TailLines,
		LimitBytes:   opts.LimitBytes,
	}
}

// ScaleFromConfig builds a scale resource out of a deployment config.
func ScaleFromConfig(dc *DeploymentConfig) *extensions.Scale {
	return &extensions.Scale{
		ObjectMeta: kapi.ObjectMeta{
			Name:              dc.Name,
			Namespace:         dc.Namespace,
			UID:               dc.UID,
			ResourceVersion:   dc.ResourceVersion,
			CreationTimestamp: dc.CreationTimestamp,
		},
		Spec: extensions.ScaleSpec{
			Replicas: dc.Spec.Replicas,
		},
		Status: extensions.ScaleStatus{
			Replicas: dc.Status.Replicas,
			Selector: &unversioned.LabelSelector{
				MatchLabels: dc.Spec.Selector,
			},
		},
	}
}

// TemplateImage is a structure for helping a caller iterate over a PodSpec
type TemplateImage struct {
	Image string

	Ref *imageapi.DockerImageReference

	From *kapi.ObjectReference

	Container *kapi.Container
}

// TemplateImageForContainer locates the requested container in a pod spec, returning information about the
// trigger (if it exists), or an error.
func TemplateImageForContainer(pod *kapi.PodSpec, triggerFn TriggeredByFunc, containerName string) (TemplateImage, error) {
	for i := range pod.Containers {
		container := &pod.Containers[i]
		if container.Name != containerName {
			continue
		}
		var ref imageapi.DockerImageReference
		if trigger, ok := triggerFn(container); ok {
			trigger.Image = container.Image
			trigger.Container = container
			return trigger, nil
		}
		ref, err := imageapi.ParseDockerImageReference(container.Image)
		if err != nil {
			return TemplateImage{Image: container.Image, Container: container}, err
		}
		return TemplateImage{Image: container.Image, Ref: &ref, Container: container}, nil
	}
	return TemplateImage{}, fmt.Errorf("no container %q found", containerName)
}

// EachTemplateImage iterates a pod spec, looking for triggers that match each container and invoking
// fn with each located image.
func EachTemplateImage(pod *kapi.PodSpec, triggerFn TriggeredByFunc, fn func(TemplateImage, error)) {
	for i := range pod.Containers {
		container := &pod.Containers[i]
		var ref imageapi.DockerImageReference
		if trigger, ok := triggerFn(container); ok {
			trigger.Image = container.Image
			trigger.Container = container
			fn(trigger, nil)
			continue
		}
		ref, err := imageapi.ParseDockerImageReference(container.Image)
		if err != nil {
			fn(TemplateImage{Image: container.Image, Container: container}, err)
			continue
		}
		fn(TemplateImage{Image: container.Image, Ref: &ref, Container: container}, nil)
	}
}

// TriggeredByFunc returns a TemplateImage or error from the provided container
type TriggeredByFunc func(container *kapi.Container) (TemplateImage, bool)

// IgnoreTriggers ignores the triggers
func IgnoreTriggers(container *kapi.Container) (TemplateImage, bool) {
	return TemplateImage{}, false
}

// DeploymentConfigHasTrigger returns a function that can identify the image for each container.
func DeploymentConfigHasTrigger(config *DeploymentConfig) TriggeredByFunc {
	return func(container *kapi.Container) (TemplateImage, bool) {
		for _, trigger := range config.Spec.Triggers {
			params := trigger.ImageChangeParams
			if params == nil {
				continue
			}
			for _, name := range params.ContainerNames {
				if container.Name == name {
					if len(params.From.Name) == 0 {
						continue
					}
					from := params.From
					if len(from.Namespace) == 0 {
						from.Namespace = config.Namespace
					}
					return TemplateImage{
						Image: container.Image,
						From:  &from,
					}, true
				}
			}
		}
		return TemplateImage{}, false
	}
}
