// logger for omc/ems

package logmml

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

// LogLevel defines a log level
type LogLevel int

// enum all LogLevels
const (
	// following level also match syslog.Priority value
	LOG_RET LogLevel = iota
	LOG_CMD
	LOG_OFF
	LOG_NODEF
)

// default log options
const (
	DEFAULT_LOG_PREFIX = "omc:mml"
	DEFAULT_LOG_FLAG   = log.Ldate | log.Ltime | log.Lmsgprefix
	DEFAULT_LOG_LEVEL  = LOG_CMD
	DEFAULT_CALL_DEPTH = 0
)

// Logger is a logger interface
type Logger interface {
	Ret(v ...interface{})
	Retf(format string, v ...interface{})
	Cmd(v ...interface{})
	Cmdf(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) Ret(v ...interface{}) {}

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

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

// Debugf empty implementation
func (DiscardLogger) Cmdf(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 MMLLogger struct {
	RET         *log.Logger
	CMD         *log.Logger
	level       LogLevel
	levelString []string
	//depth       int
}

var _ Logger = &MMLLogger{}

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

// NewEmsLogger3 let you customrize your logger prefix and flag and logLevel
func NewMmlLogger3(out io.Writer, prefix string, flag int, l LogLevel) *MMLLogger {
	return &MMLLogger{
		RET:         log.New(out, fmt.Sprintf("[%s] [ret]: ", prefix), flag),
		CMD:         log.New(out, fmt.Sprintf("[%s] [cmd]: ", prefix), flag),
		level:       l,
		levelString: []string{"ret", "cmd"},
		//depth:       DEFAULT_CALL_DEPTH,
	}
}

// Trace implement ILogger
func (s *MMLLogger) Ret(v ...interface{}) {
	if s.level <= LOG_RET {
		//_ = s.RET.Output(s.depth, fmt.Sprintln(v...))
		_ = s.RET.Output(0, fmt.Sprintln(v...))
	}
}

// Tracef implement ILogger
func (s *MMLLogger) Retf(format string, v ...interface{}) {
	if s.level <= LOG_RET {
		_ = s.RET.Output(0, fmt.Sprintf(format, v...))
	}
}

// Debug implement ILogger
func (s *MMLLogger) Cmd(v ...interface{}) {
	if s.level <= LOG_CMD {
		_ = s.CMD.Output(0, fmt.Sprintln(v...))
	}
}

// Debugf implement ILogger
func (s *MMLLogger) Cmdf(format string, v ...interface{}) {
	if s.level <= LOG_CMD {
		_ = s.CMD.Output(0, fmt.Sprintf(format, v...))
	}
}

// Info implement ILogger

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

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

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

var Elogger Logger

func InitMmlLogger(logFile string, period int, count int, prefix string, logLevel LogLevel) {
	logWriter := getLogWriter(logFile, period, count)
	Elogger = NewMmlLogger3(logWriter, prefix, DEFAULT_LOG_FLAG, logLevel)
	fmt.Printf("logFile=%s, period=%d, count=%d, prefix=%s, logLevel=%s\n", logFile, period, count, prefix, GetLevelString())
}

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

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

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

// Debugf implement ILogger
func Cmdf(format string, v ...interface{}) {
	Elogger.Cmdf(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)
}
