// containerNextState determines the next state a container should go to.
// It returns: The state it should transition to, a bool indicating whether any
// action is required, and a bool indicating whether a known status change is
// possible.
// 'Stopped, false, true' -> "You can move it to known stopped, but you don't have to call a transition function"
// 'Running, true, true' -> "You can move it to running and you need to call the transition function"
// 'None, false, false' -> "This should not be moved; it has unresolved dependencies or is complete; no knownstatus change"
func (mtask *managedTask) containerNextState(container *api.Container) (api.ContainerStatus, bool, bool) {
	clog := log.New("task", mtask.Task, "container", container)
	containerKnownStatus := container.GetKnownStatus()
	containerDesiredStatus := container.GetDesiredStatus()
	if containerKnownStatus == containerDesiredStatus {
		clog.Debug("Container at desired status", "desired", containerDesiredStatus)
		return api.ContainerStatusNone, false, false
	}
	if containerKnownStatus > containerDesiredStatus {
		clog.Debug("Container past desired status")
		return api.ContainerStatusNone, false, false
	}
	if !dependencygraph.DependenciesAreResolved(container, mtask.Containers) {
		clog.Debug("Can't apply state to container yet; dependencies unresolved", "state", containerDesiredStatus)
		return api.ContainerStatusNone, false, false
	}

	var nextState api.ContainerStatus
	if container.DesiredTerminal() {
		nextState = api.ContainerStopped
		if containerKnownStatus != api.ContainerRunning {
			// If it's not currently running we do not need to do anything to make it become stopped.
			return nextState, false, true
		}
	} else {
		nextState = containerKnownStatus + 1
	}
	return nextState, true, true
}
func linkIsResolved(target *api.Container, link *api.Container) bool {
	targetDesiredStatus := target.GetDesiredStatus()
	if targetDesiredStatus == api.ContainerCreated {
		knownStatus := link.GetKnownStatus()
		return knownStatus == api.ContainerCreated || knownStatus == api.ContainerRunning
	} else if targetDesiredStatus == api.ContainerRunning {
		return link.GetKnownStatus() == api.ContainerRunning
	}
	log.Error("Unexpected desired status", "target", target)
	return false
}
func volumeIsResolved(target *api.Container, volume *api.Container) bool {
	targetDesiredStatus := target.GetDesiredStatus()
	if targetDesiredStatus == api.ContainerCreated || targetDesiredStatus == api.ContainerRunning {
		knownStatus := volume.GetKnownStatus()
		return knownStatus == api.ContainerCreated ||
			knownStatus == api.ContainerRunning ||
			knownStatus == api.ContainerStopped
	}

	log.Error("Unexpected desired status", "target", target)
	return false
}
// verifyStatusResolveable validates that `target` can be resolved given that
// target depends on `dependencies` (which are container names) and there are
// `existingContainers` (map from name to container). The `resolves` function
// passed should return true if the named container is resolved.
func verifyStatusResolveable(target *api.Container, existingContainers map[string]*api.Container, dependencies []string, resolves func(*api.Container, *api.Container) bool) bool {
	targetGoal := target.GetDesiredStatus()
	if targetGoal != api.ContainerRunning && targetGoal != api.ContainerCreated {
		// A container can always stop, die, or reach whatever other statre it
		// wants regardless of what dependencies it has
		return true
	}

	for _, dependency := range dependencies {
		maybeResolves, exists := existingContainers[dependency]
		if !exists {
			return false
		}
		if !resolves(target, maybeResolves) {
			return false
		}
	}
	return true
}
// onRunIsResolved defines a relationship where a target cannot be created until
// 'run' has reached a running state.
func onRunIsResolved(target *api.Container, run *api.Container) bool {
	if target.GetDesiredStatus() >= api.ContainerCreated {
		return run.GetKnownStatus() >= api.ContainerRunning
	}
	return false
}