// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) from github.com/robertkrimen/dbg

/*
Package dbg is a println/printf/log-debugging utility library.

    import (
        Dbg "github.com/robertkrimen/dbg"
    )

    dbg, dbgf := Dbg.New()

    dbg("Emit some debug stuff", []byte{120, 121, 122, 122, 121}, math.Pi)
    # "2013/01/28 16:50:03 Emit some debug stuff [120 121 122 122 121] 3.141592653589793"

    dbgf("With a %s formatting %.2f", "little", math.Pi)
    # "2013/01/28 16:51:55 With a little formatting (3.14)"

    dbgf("%/fatal//A fatal debug statement: should not be here")
    # "A fatal debug statement: should not be here"
    # ...and then, os.Exit(1)

    dbgf("%/panic//Can also panic %s", "this")
    # "Can also panic this"
    # ...as a panic, equivalent to: panic("Can also panic this")

    dbgf("Any %s arguments without a corresponding %%", "extra", "are treated like arguments to dbg()")
    # "2013/01/28 17:14:40 Any extra arguments (without a corresponding %) are treated like arguments to dbg()"

    dbgf("%d %d", 1, 2, 3, 4, 5)
    # "2013/01/28 17:16:32 Another example: 1 2 3 4 5"

    dbgf("%@: Include the function name for a little context (via %s)", "%@")
    # "2013... github.com/robertkrimen/dbg.TestSynopsis: Include the function name for a little context (via %@)"

By default, dbg uses log (log.Println, log.Printf, log.Panic, etc.) for output.
However, you can also provide your own output destination by invoking dbg.New with
a customization function:

    import (
        "bytes"
        Dbg "github.com/robertkrimen/dbg"
        "os"
    )

    # dbg to os.Stderr
    dbg, dbgf := Dbg.New(func(dbgr *Dbgr) {
        dbgr.SetOutput(os.Stderr)
    })

    # A slightly contrived example:
    var buffer bytes.Buffer
    dbg, dbgf := New(func(dbgr *Dbgr) {
        dbgr.SetOutput(&buffer)
    })

*/
package dbg

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"regexp"
	"runtime"
	"strings"
	"unicode"
)

type _frmt struct {
	ctl          string
	format       string
	operandCount int
	panic        bool
	fatal        bool
	check        bool
}

var (
	ctlTest = regexp.MustCompile(`^\s*%/`)
	ctlScan = regexp.MustCompile(`%?/(panic|fatal|check)(?:\s|$)`)
)

func operandCount(format string) int {
	count := 0
	end := len(format)
	for at := 0; at < end; {
		for at < end && format[at] != '%' {
			at++
		}
		at++
		if at < end {
			if format[at] != '%' && format[at] != '@' {
				count++
			}
			at++
		}
	}
	return count
}

func parseFormat(format string) (frmt _frmt) {
	if ctlTest.MatchString(format) {
		format = strings.TrimLeftFunc(format, unicode.IsSpace)
		index := strings.Index(format, "//")
		if index != -1 {
			frmt.ctl = format[0:index]
			format = format[index+2:] // Skip the second slash via +2 (instead of +1)
		} else {
			frmt.ctl = format
			format = ""
		}
		for _, tmp := range ctlScan.FindAllStringSubmatch(frmt.ctl, -1) {
			for _, value := range tmp[1:] {
				switch value {
				case "panic":
					frmt.panic = true
				case "fatal":
					frmt.fatal = true
				case "check":
					frmt.check = true
				}
			}
		}
	}
	frmt.format = format
	frmt.operandCount = operandCount(format)
	return
}

type Dbgr struct {
	emit _emit
}

type DbgFunction func(values ...interface{})

func NewDbgr() *Dbgr {
	self := &Dbgr{}
	return self
}

/*
New will create and return a pair of debugging functions. You can customize where
they output to by passing in an (optional) customization function:

    import (
        Dbg "github.com/robertkrimen/dbg"
        "os"
    )

    # dbg to os.Stderr
    dbg, dbgf := Dbg.New(func(dbgr *Dbgr) {
        dbgr.SetOutput(os.Stderr)
    })

*/
func New(options ...interface{}) (dbg DbgFunction, dbgf DbgFunction) {
	dbgr := NewDbgr()
	if len(options) > 0 {
		if fn, ok := options[0].(func(*Dbgr)); ok {
			fn(dbgr)
		}
	}
	return dbgr.DbgDbgf()
}

func (self Dbgr) Dbg(values ...interface{}) {
	self.getEmit().emit(_frmt{}, "", values...)
}

func (self Dbgr) Dbgf(values ...interface{}) {
	self.dbgf(values...)
}

func (self Dbgr) DbgDbgf() (dbg DbgFunction, dbgf DbgFunction) {
	dbg = func(vl ...interface{}) {
		self.Dbg(vl...)
	}
	dbgf = func(vl ...interface{}) {
		self.dbgf(vl...)
	}
	return dbg, dbgf // Redundant, but...
}

func (self Dbgr) dbgf(values ...interface{}) {

	var frmt _frmt
	if len(values) > 0 {
		tmp := fmt.Sprint(values[0])
		frmt = parseFormat(tmp)
		values = values[1:]
	}

	buffer_f := bytes.Buffer{}
	format := frmt.format
	end := len(format)
	for at := 0; at < end; {
		last := at
		for at < end && format[at] != '%' {
			at++
		}
		if at > last {
			buffer_f.WriteString(format[last:at])
		}
		if at >= end {
			break
		}
		// format[at] == '%'
		at++
		// format[at] == ?
		if format[at] == '@' {
			depth := 2
			pc, _, _, _ := runtime.Caller(depth)
			name := runtime.FuncForPC(pc).Name()
			buffer_f.WriteString(name)
		} else {
			buffer_f.WriteString(format[at-1 : at+1])
		}
		at++
	}

	//values_f := append([]interface{}{}, values[0:frmt.operandCount]...)
	values_f := values[0:frmt.operandCount]
	values_dbg := values[frmt.operandCount:]
	if len(values_dbg) > 0 {
		// Adjust frmt.format:
		// (%v instead of %s because: frmt.check)
		{
			tmp := format
			if len(tmp) > 0 {
				if unicode.IsSpace(rune(tmp[len(tmp)-1])) {
					buffer_f.WriteString("%v")
				} else {
					buffer_f.WriteString(" %v")
				}
			} else if frmt.check {
				// Performing a check, so no output
			} else {
				buffer_f.WriteString("%v")
			}
		}

		// Adjust values_f:
		if !frmt.check {
			tmp := []string{}
			for _, value := range values_dbg {
				tmp = append(tmp, fmt.Sprintf("%v", value))
			}
			// First, make a copy of values_f, so we avoid overwriting values_dbg when appending
			values_f = append([]interface{}{}, values_f...)
			values_f = append(values_f, strings.Join(tmp, " "))
		}
	}

	format = buffer_f.String()
	if frmt.check {
		// We do not actually emit to the log, but panic if
		// a non-nil value is detected (e.g. a non-nil error)
		for _, value := range values_dbg {
			if value != nil {
				if format == "" {
					panic(value)
				} else {
					panic(fmt.Sprintf(format, append(values_f, value)...))
				}
			}
		}
	} else {
		self.getEmit().emit(frmt, format, values_f...)
	}
}

// Idiot-proof &Dbgr{}, etc.
func (self *Dbgr) getEmit() _emit {
	if self.emit == nil {
		self.emit = standardEmit()
	}
	return self.emit
}

// SetOutput will accept the following as a destination for output:
//
//      *log.Logger         Print*/Panic*/Fatal* of the logger
//      io.Writer           -
//      nil                 Reset to the default output (os.Stderr)
//      "log"               Print*/Panic*/Fatal* via the "log" package
//
func (self *Dbgr) SetOutput(output interface{}) {
	if output == nil {
		self.emit = standardEmit()
		return
	}
	switch output := output.(type) {
	case *log.Logger:
		self.emit = _emitLogger{
			logger: output,
		}
		return
	case io.Writer:
		self.emit = _emitWriter{
			writer: output,
		}
		return
	case string:
		if output == "log" {
			self.emit = _emitLog{}
			return
		}
	}
	panic(output)
}

// ======== //
// = emit = //
// ======== //

func standardEmit() _emit {
	return _emitWriter{
		writer: os.Stderr,
	}
}

func ln(tmp string) string {
	length := len(tmp)
	if length > 0 && tmp[length-1] != '\n' {
		return tmp + "\n"
	}
	return tmp
}

type _emit interface {
	emit(_frmt, string, ...interface{})
}

type _emitWriter struct {
	writer io.Writer
}

func (self _emitWriter) emit(frmt _frmt, format string, values ...interface{}) {
	if format == "" {
		fmt.Fprintln(self.writer, values...)
	} else {
		if frmt.panic {
			panic(fmt.Sprintf(format, values...))
		}
		fmt.Fprintf(self.writer, ln(format), values...)
		if frmt.fatal {
			os.Exit(1)
		}
	}
}

type _emitLogger struct {
	logger *log.Logger
}

func (self _emitLogger) emit(frmt _frmt, format string, values ...interface{}) {
	if format == "" {
		self.logger.Println(values...)
	} else {
		if frmt.panic {
			self.logger.Panicf(format, values...)
		} else if frmt.fatal {
			self.logger.Fatalf(format, values...)
		} else {
			self.logger.Printf(format, values...)
		}
	}
}

type _emitLog struct {
}

func (self _emitLog) emit(frmt _frmt, format string, values ...interface{}) {
	if format == "" {
		log.Println(values...)
	} else {
		if frmt.panic {
			log.Panicf(format, values...)
		} else if frmt.fatal {
			log.Fatalf(format, values...)
		} else {
			log.Printf(format, values...)
		}
	}
}
