package shortcircuit

import (
	"fmt"
	"io"
)

// Error indicates that a write was not attempted because
// an earlier write failed due to Err.
type Error struct {
	Err error
}

func (a Error) Error() string {
	return fmt.Sprintf("writer already failed: %v", a.Err)
}

// Unwrap returns the error from the earlier failed write.
func (e Error) Unwrap() error {
	return e.Err
}

// Is returns true if target is an Error.
func (e Error) Is(target error) bool {
	_, ok := target.(Error)
	return ok
}

type Writer struct {
	err error
	n   int64
	w   io.Writer
}

func EnsureWriter(w io.Writer) *Writer {
	scw, ok := w.(*Writer)
	if !ok {
		scw = NewWriter(w)
	}
	return scw
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{
		w: w,
	}
}

// Write attempts to write p to the underlying io.Writer if no earlier call to
// Write has failed. It returns the number of bytes written and an error if
// the write failed. If an earlier call did fail, no bytes will be written and
// err will be an Error containing the error from the failed call.
func (w *Writer) Write(p []byte) (n int, err error) {
	if w.Failed() {
		return 0, Error{Err: w.err}
	}
	n, err = w.w.Write(p)
	w.n += int64(n)
	if err != nil {
		w.err = err
	}
	return n, err
}

// Status returns the total bytes written to the underlying writer,
// and any error resulting from a call to Write.
func (w *Writer) Status() (written int64, err error) {
	return w.n, w.err
}

func (w *Writer) Failed() bool {
	return w.err != nil
}

// ClearError makes this Writer forget any earlier error from Write, so that
// future calls to Write will attempt to write to the underlying io.Writer.
func (s *Writer) ClearError() {
	s.err = nil
}
