package log

import "io"

// RawLogger is the building brick of the package.
type RawLogger interface {
	Log(*Entry)
}

// RawLoggerFunc is a RawLogger implementation using a function.
type RawLoggerFunc func(*Entry)

// Log implements RawLogger
func (f RawLoggerFunc) Log(entry *Entry) { f(entry) }

// Logger adds convenience methods to a RawLogger.
type Logger struct{ inner RawLogger }

func NewLogger(inner RawLogger) *Logger {
	return &Logger{inner}
}

func (l *Logger) Log(entry *Entry) { l.inner.Log(entry) }

func (l *Logger) Debug(message string)    { l.inner.Log(NewEntry(DEBUG, message)) }
func (l *Logger) Info(message string)     { l.inner.Log(NewEntry(INFO, message)) }
func (l *Logger) Notice(message string)   { l.inner.Log(NewEntry(NOTICE, message)) }
func (l *Logger) Warning(message string)  { l.inner.Log(NewEntry(WARNING, message)) }
func (l *Logger) Error(message string)    { l.inner.Log(NewEntry(ERROR, message)) }
func (l *Logger) Critical(message string) { l.inner.Log(NewEntry(CRITICAL, message)) }

func (l *Logger) Debugf(template string, args ...interface{}) {
	l.inner.Log(NewEntryf(DEBUG, template, args...))
}

func (l *Logger) Infof(template string, args ...interface{}) {
	l.inner.Log(NewEntryf(INFO, template, args...))
}

func (l *Logger) Noticef(template string, args ...interface{}) {
	l.inner.Log(NewEntryf(NOTICE, template, args...))
}

func (l *Logger) Warningf(template string, args ...interface{}) {
	l.inner.Log(NewEntryf(WARNING, template, args...))
}

func (l *Logger) Errorf(template string, args ...interface{}) {
	l.inner.Log(NewEntryf(ERROR, template, args...))
}

func (l *Logger) Criticalf(template string, args ...interface{}) {
	l.inner.Log(NewEntryf(CRITICAL, template, args...))
}

func (l *Logger) WithField(name string, value interface{}) *Logger {
	return &Logger{RawLoggerFunc(func(entry *Entry) {
		entry.Fields = entry.Fields.With(name, value)
		l.inner.Log(entry)
	})}
}

func (l *Logger) WithCategory(category string) *Logger {
	return &Logger{RawLoggerFunc(func(entry *Entry) {
		entry.Category = category
		l.inner.Log(entry)
	})}
}

type LogWriter struct {
	minLevel  Level
	writer    io.Writer
	formatter Formatter
}

func NewLogWriter(minLevel Level, writer io.Writer, formatter Formatter) *LogWriter {
	return &LogWriter{minLevel, writer, formatter}
}

func (w *LogWriter) Log(entry *Entry) {
	if entry.Level < w.minLevel {
		return
	}
	if bytes, err := w.formatter.Format(entry); err != nil {
		errorHandler(err)
	} else if _, err := w.writer.Write(bytes); err != nil {
		errorHandler(err)
	}
}