package docker

import (
	"fmt"
	"io"

	"github.com/golang/glog"

	"github.com/openshift/origin/pkg/cmd/util/prefixwriter"
)

const (
	taskNamePrefix = "-- "
	taskIndent     = "   "
)

// TaskPrinter is a helper for start task output
type TaskPrinter struct {
	taskWriter *taskWriter
	out        io.Writer
}

// NewTaskPrinter creates a new TaskPrinter
func NewTaskPrinter(out io.Writer) *TaskPrinter {
	return &TaskPrinter{
		out: out,
	}
}

// StartTask writes out the header for a task
func (p *TaskPrinter) StartTask(name string) {
	fmt.Fprintf(p.out, "%s%s ... ", taskNamePrefix, name)
	if glog.V(1) {
		fmt.Fprintf(p.out, "\n")
	}
}

// Success writes out a success marker for a task
func (p *TaskPrinter) Success() {
	if (p.taskWriter != nil && p.taskWriter.used) || bool(glog.V(1)) {
		return
	}
	fmt.Fprintf(p.out, "OK\n")
}

// TaskWriter is a writer that can be used to write task output
func (p *TaskPrinter) TaskWriter() io.Writer {
	p.taskWriter = &taskWriter{w: p.out}
	return prefixwriter.New(taskIndent, p.taskWriter)
}

// Failure writes out a failure marker for a task and outputs the error
// that caused the failure
func (p *TaskPrinter) Failure(err error) {
	fmt.Fprintf(p.out, "FAIL\n")
	PrintError(err, prefixwriter.New(taskIndent, p.out))
}

type hasCause interface {
	Cause() error
}

type hasDetails interface {
	Details() string
}

type hasSolution interface {
	Solution() string
}

func PrintError(err error, out io.Writer) {
	fmt.Fprintf(out, "Error: %v\n", err)
	if d, ok := err.(hasDetails); ok && len(d.Details()) > 0 {
		fmt.Fprintf(out, "Details:\n")
		w := prefixwriter.New("  ", out)
		fmt.Fprintf(w, d.Details())
	}
	if s, ok := err.(hasSolution); ok && len(s.Solution()) > 0 {
		fmt.Fprintf(out, "Solution:\n")
		w := prefixwriter.New("  ", out)
		fmt.Fprintf(w, s.Solution())
		fmt.Fprintf(w, "\n")
	}
	if c, ok := err.(hasCause); ok && c.Cause() != nil {
		fmt.Fprintf(out, "Caused By:\n")
		w := prefixwriter.New("  ", out)
		PrintError(c.Cause(), w)
	}
}

type taskWriter struct {
	w    io.Writer
	used bool
}

func (t *taskWriter) Write(p []byte) (n int, err error) {
	if !t.used {
		t.used = true
		t.w.Write([]byte("\n"))
	}
	return t.w.Write(p)
}