// logger for omc/ems

package log

import (
	"fmt"
	"io"
	"log"
)

// LogLevel defines a log level
type LogLevel int

// enum all LogLevels
const (
	// following level also match syslog.Priority value
	LOG_TRACE LogLevel = iota
	LOG_DEBUG
	LOG_INFO
	LOG_WARN
	LOG_ERROR
	LOG_FATAL
	LOG_OFF
	LOG_NODEF
)

// default log options
const (
	DEFAULT_LOG_PREFIX = "omc"
	DEFAULT_LOG_FLAG   = log.Lshortfile | log.Ldate | log.Lmicroseconds
	DEFAULT_LOG_LEVEL  = LOG_DEBUG
	DEFAULT_CALL_DEPTH = 3
)

// Logger is a logger interface
type Logger interface {
	Fatal(v ...interface{})
	Fatalf(format string, v ...interface{})
	Error(v ...interface{})
	Errorf(format string, v ...interface{})
	Warn(v ...interface{})
	Warnf(format string, v ...interface{})
	Info(v ...interface{})
	Infof(format string, v ...interface{})
	Debug(v ...interface{})
	Debugf(format string, v ...interface{})
	Trace(v ...interface{})
	Tracef(format string, v ...interface{})

	Level() LogLevel
	LevelString() string
	SetLevel(l LogLevel)
}

var _ Logger = DiscardLogger{}

// DiscardLogger don't log implementation for ILogger
type DiscardLogger struct{}

// Trace empty implementation
func (DiscardLogger) Trace(v ...interface{}) {}

// Tracef empty implementation
func (DiscardLogger) Tracef(format string, v ...interface{}) {}

// Debug empty implementation
func (DiscardLogger) Debug(v ...interface{}) {}

// Debugf empty implementation
func (DiscardLogger) Debugf(format string, v ...interface{}) {}

// Info empty implementation
func (DiscardLogger) Info(v ...interface{}) {}

// Infof empty implementation
func (DiscardLogger) Infof(format string, v ...interface{}) {}

// Warn empty implementation
func (DiscardLogger) Warn(v ...interface{}) {}

// Warnf empty implementation
func (DiscardLogger) Warnf(format string, v ...interface{}) {}

// Error empty implementation
func (DiscardLogger) Error(v ...interface{}) {}

// Errorf empty implementation
func (DiscardLogger) Errorf(format string, v ...interface{}) {}

// Fatal empty implementation
func (DiscardLogger) Fatal(v ...interface{}) {}

// Fatalf empty implementation
func (DiscardLogger) Fatalf(format string, v ...interface{}) {}

// Level empty implementation
func (DiscardLogger) Level() LogLevel {
	return LOG_NODEF
}

// Level empty implementation
func (DiscardLogger) LevelString() string {
	return ""
}

// SetLevel empty implementation
func (DiscardLogger) SetLevel(l LogLevel) {}

// EmsLogger is the default implment of ILogger
type EmsLogger struct {
	TRACE       *log.Logger
	DEBUG       *log.Logger
	INFO        *log.Logger
	WARN        *log.Logger
	ERROR       *log.Logger
	FATAL       *log.Logger
	level       LogLevel
	levelString []string
	depth       int
}

var _ Logger = &EmsLogger{}

// NewEmsLogger use a special io.Writer as logger output
func NewEmsLogger(out io.Writer) *EmsLogger {
	return NewEmsLogger2(out, DEFAULT_LOG_PREFIX, DEFAULT_LOG_FLAG)
}

// NewEmsLogger2 let you customrize your logger prefix and flag
func NewEmsLogger2(out io.Writer, prefix string, flag int) *EmsLogger {
	return NewEmsLogger3(out, prefix, flag, DEFAULT_LOG_LEVEL)
}

// NewEmsLogger3 let you customrize your logger prefix and flag and logLevel
func NewEmsLogger3(out io.Writer, prefix string, flag int, l LogLevel) *EmsLogger {
	return &EmsLogger{
		TRACE:       log.New(out, fmt.Sprintf("[%s] [trace] ", prefix), flag),
		DEBUG:       log.New(out, fmt.Sprintf("[%s] [debug] ", prefix), flag),
		INFO:        log.New(out, fmt.Sprintf("[%s] [info ] ", prefix), flag),
		WARN:        log.New(out, fmt.Sprintf("[%s] [warn ] ", prefix), flag),
		ERROR:       log.New(out, fmt.Sprintf("[%s] [error] ", prefix), flag),
		FATAL:       log.New(out, fmt.Sprintf("[%s] [fatal] ", prefix), flag),
		level:       l,
		levelString: []string{"trace", "debug", "info", "warn", "error", "fatal"},
		depth:       DEFAULT_CALL_DEPTH,
	}
}

// Trace implement ILogger
func (s *EmsLogger) Trace(v ...interface{}) {
	if s.level <= LOG_TRACE {
		_ = s.TRACE.Output(s.depth, fmt.Sprintln(v...))
	}
}

// Tracef implement ILogger
func (s *EmsLogger) Tracef(format string, v ...interface{}) {
	if s.level <= LOG_TRACE {
		_ = s.TRACE.Output(s.depth, fmt.Sprintf(format, v...))
	}
}

// Debug implement ILogger
func (s *EmsLogger) Debug(v ...interface{}) {
	if s.level <= LOG_DEBUG {
		_ = s.DEBUG.Output(s.depth, fmt.Sprintln(v...))
	}
}

// Debugf implement ILogger
func (s *EmsLogger) Debugf(format string, v ...interface{}) {
	if s.level <= LOG_DEBUG {
		_ = s.DEBUG.Output(s.depth, fmt.Sprintf(format, v...))
	}
}

// Info implement ILogger
func (s *EmsLogger) Info(v ...interface{}) {
	if s.level <= LOG_INFO {
		_ = s.INFO.Output(s.depth, fmt.Sprintln(v...))
	}
}

// Infof implement ILogger
func (s *EmsLogger) Infof(format string, v ...interface{}) {
	if s.level <= LOG_INFO {
		_ = s.INFO.Output(s.depth, fmt.Sprintf(format, v...))
	}
}

// Warn implement ILogger
func (s *EmsLogger) Warn(v ...interface{}) {
	if s.level <= LOG_WARN {
		_ = s.WARN.Output(s.depth, fmt.Sprintln(v...))
	}
}

// Warnf implement ILogger
func (s *EmsLogger) Warnf(format string, v ...interface{}) {
	if s.level <= LOG_WARN {
		_ = s.WARN.Output(s.depth, fmt.Sprintf(format, v...))
	}
}

// Error implement ILogger
func (s *EmsLogger) Error(v ...interface{}) {
	if s.level <= LOG_ERROR {
		_ = s.ERROR.Output(s.depth, fmt.Sprintln(v...))
	}
}

// Errorf implement ILogger
func (s *EmsLogger) Errorf(format string, v ...interface{}) {
	if s.level <= LOG_ERROR {
		_ = s.ERROR.Output(s.depth, fmt.Sprintf(format, v...))
	}
}

// Warn implement ILogger
func (s *EmsLogger) Fatal(v ...interface{}) {
	if s.level <= LOG_FATAL {
		_ = s.FATAL.Output(s.depth, fmt.Sprintln(v...))
	}
}

// Warnf implement ILogger
func (s *EmsLogger) Fatalf(format string, v ...interface{}) {
	if s.level <= LOG_FATAL {
		_ = s.FATAL.Output(s.depth, fmt.Sprintf(format, v...))
	}
}

// Level implement ILogger
func (s *EmsLogger) Level() LogLevel {
	return s.level
}

// Level implement ILogger
func (s *EmsLogger) LevelString() string {
	return s.levelString[s.level]
}

// SetLevel implement ILogger
func (s *EmsLogger) SetLevel(l LogLevel) {
	s.level = l
}

var Elogger Logger

func InitLogger(logFile string, period int, count int, prefix string, logLevel LogLevel) io.Writer {

	/*
		logFile, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)
		if err != nil {
			panic(err)
		}
	*/

	logWriter := getLogWriter(logFile, period, count)
	Elogger = NewEmsLogger3(logWriter, prefix, DEFAULT_LOG_FLAG, logLevel)
	fmt.Printf("logFile=%s, period=%d, count=%d, prefix=%s, logLevel=%s\n", logFile, period, count, prefix, GetLevelString())
	return logWriter
}

// Trace implement ILogger
func Trace(v ...interface{}) {
	Elogger.Trace(v...)
}

// Tracef implement ILogger
func Tracef(format string, v ...interface{}) {
	Elogger.Tracef(format, v...)
}

// Debug implement ILogger
func Debug(v ...interface{}) {
	Elogger.Debug(v...)
}

// Debugf implement ILogger
func Debugf(format string, v ...interface{}) {
	Elogger.Debugf(format, v...)
}

// Info implement ILogger
func Info(v ...interface{}) {
	Elogger.Info(v...)
}

// Infof implement ILogger
func Infof(format string, v ...interface{}) {
	Elogger.Infof(format, v...)
}

// Warn implement ILogger
func Warn(v ...interface{}) {
	Elogger.Warn(v...)
}

// Warnf implement ILogger
func Warnf(format string, v ...interface{}) {
	Elogger.Warnf(format, v...)
}

// Error implement ILogger
func Error(v ...interface{}) {
	Elogger.Error(v...)
}

// Errorf implement ILogger
func Errorf(format string, v ...interface{}) {
	Elogger.Errorf(format, v...)
}

// Warn implement ILogger
func Fatal(v ...interface{}) {
	Elogger.Fatal(v...)
}

// Warnf implement ILogger
func Fatalf(format string, v ...interface{}) {
	Elogger.Fatalf(format, v...)
}

// Level implement ILogger
func GetLevel() LogLevel {
	return Elogger.Level()
}

// Level implement ILogger
func GetLevelString() string {
	return Elogger.LevelString()
}

// SetLevel implement ILogger
func SetLevel(l LogLevel) {
	Elogger.SetLevel(l)
}
