package logger

import (
	"log"
	"os"
)

type Logger struct {
	*log.Logger
	name    string
	file    string
	verbose bool
}

// DEBUG/WARNING will only be written to file
// INFO/ERROR will be written to file and printed to stdout/stderr
const (
	DEBUG = iota
	INFO
	WARNING
	ERROR
)

var levels = map[int]string{
	DEBUG:   "[DEBUG]",
	INFO:    "[INFO]",
	WARNING: "[WARNING]",
	ERROR:   "[ERROR]",
}

var outLogger = log.New(os.Stdout, "", log.LstdFlags)
var errLogger = log.New(os.Stderr, "", log.LstdFlags)

// log to stdout
func OutLog(v ...interface{}) {
	outLogger.Println(v...)
}

// log to stderr
func ErrLog(v ...interface{}) {
	errLogger.Println(v...)
}

// initialize a logger
func NewLogger(name, file string, verbose bool) *Logger {
	logger := Logger{
		Logger:  log.New(os.Stderr, "", log.LstdFlags),
		name:    name,
		file:    file,
		verbose: verbose,
	}
	return &logger
}

// write something to the logger
func (logger *Logger) log(level int, v ...interface{}) {
	f, err := os.OpenFile(logger.file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		ErrLog(err.Error())
	}
	defer f.Close()

	levelStr := levels[level]
	args := []interface{}{levelStr + ":"}
	args = append(args, v...)

	logger.SetOutput(f)
	logger.Println(args)
}

// write debug information to the logfile
func (logger *Logger) Debug(v ...interface{}) {
	if logger.verbose {
		logger.log(DEBUG, v...)
	}
}

// write information to the logfile and to stdout
func (logger *Logger) Info(v ...interface{}) {
	OutLog(append([]interface{}{"[" + logger.name + "]"}, v...))
	logger.log(INFO, v...)
}

// write warnings to the logfile
func (logger *Logger) Warning(v ...interface{}) {
	logger.log(WARNING, v...)
}

// write errors to the logfile and to stderr
func (logger *Logger) Error(v ...interface{}) {
	ErrLog(append([]interface{}{"[" + logger.name + "]"}, v...))
	logger.log(ERROR, v...)
}
