package starlog

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"
)

func generateCoreLogStr(skip int, logstr string) string {
	var line int = 0
	var funcname, fileName string
	now := time.Now()

	pc, fName, codeln, ok := runtime.Caller(skip)
	if !ok {
		return ""
	}
	line = codeln
	funcname = runtime.FuncForPC(pc).Name()
	funcname = filepath.Ext(funcname)
	funcname = strings.TrimPrefix(funcname, ".")
	fileName = filepath.Base(fName)

	y, m, d := now.Date()
	h, i, s := now.Clock()
	micro := now.Nanosecond() / 1e3
	logStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d", y, m, d, h, i, s, micro)
	logStr += " " + fileName + ":" + strconv.Itoa(line)
	logStr += " <" + funcname + ">"
	logStr += " " + logstr
	return logStr
}

func (logger *starlog) build(thread string, isStd bool, isShow bool, handler func([]Attr, string), level int, logDetail string) {
	logger.mu.Lock()
	defer logger.mu.Unlock()
	var skip, line int = 3, 0
	var funcname, fileName string
	now := time.Now()
	if isStd {
		skip++
	}
	if logger.showDeatilFile || logger.showFuncName {
		pc, fName, codeln, ok := runtime.Caller(skip)
		if !ok {
			return
		}
		line = codeln
		funcname = runtime.FuncForPC(pc).Name()
		funcname = filepath.Ext(funcname)
		funcname = strings.TrimPrefix(funcname, ".")
		fileName = filepath.Base(fName)
	}

	y, m, d := now.Date()
	h, i, s := now.Clock()
	micro := now.Nanosecond() / 1e3
	logStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d", y, m, d, h, i, s, micro)
	var cenStr string
	if logger.showDeatilFile {
		cenStr += " " + fileName + ":" + strconv.Itoa(line)
	}
	if logger.showFuncName {
		cenStr += " <" + funcname + ">"
	}
	if logger.showThread {
		cenStr += " |" + thread + "|"
	}
	if logger.showLevel {
		cenStr += " " + `[` + levels[level] + `]`
	}
	if !logger.showColor || !logger.onlyColorLevel {
		logStr += cenStr + " " + logDetail
	} else {
		logStr += logger.colorMe[level].Sprint(cenStr) + " " + logDetail
	}
	if isShow {
		if !logger.showColor {
			fmt.Print(logStr)
		} else if !logger.onlyColorLevel {
			//logcolor := NewColor(logger.colorList[level]...)
			logger.colorMe[level].Fprint(stdScreen, logStr)
		} else {
			fmt.Fprint(stdScreen, logStr)
		}
	}
	if handler != nil {
		stacks.Push(logTransfer{
			handlerFunc: handler,
			colors:      logger.colorList[level],
			logStr:      logStr,
		})
	}
	if !logger.stopWriter {
		logger.write(logStr)
	}
}

func (logger *starlog) write(logStr string) {
	if logger.output == nil || logger.stopWriter {
		return
	}
	var count int = 0
	for logger.switching {
		time.Sleep(time.Millisecond * 100)
		count++
		if count > 50 {
			return
		}
	}
	if logger.output == nil {
		return
	}
	logger.output.Write([]byte(logStr))
}

func (logger *starlog) print(str ...interface{}) string {
	return fmt.Sprint(str...)
}

func (logger *starlog) printf(format string, str ...interface{}) string {
	return fmt.Sprintf(format, str...)
}

func (logger *starlog) println(str ...interface{}) string {
	return fmt.Sprintln(str...)
}

func (logger *starlog) Debug(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvDebug, strs)
}

func (logger *starlog) Debugf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	logger.build(thread, isStd, logger.showStd, handler, LvDebug, strs)
}

func (logger *starlog) Debugln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvDebug, strs)
}

func (logger *starlog) Info(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvInfo, strs)
}

func (logger *starlog) Infof(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	logger.build(thread, isStd, logger.showStd, handler, LvInfo, strs)
}

func (logger *starlog) Infoln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvInfo, strs)
}

func (logger *starlog) Notice(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvNotice, strs)
}

func (logger *starlog) Noticef(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	logger.build(thread, isStd, logger.showStd, handler, LvNotice, strs)
}

func (logger *starlog) Noticeln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvNotice, strs)
}

func (logger *starlog) Warning(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvWarning, strs)
}

func (logger *starlog) Warningf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	logger.build(thread, isStd, logger.showStd, handler, LvWarning, strs)
}

func (logger *starlog) Warningln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvWarning, strs)
}

func (logger *starlog) Error(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvError, strs)
}

func (logger *starlog) Errorf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	logger.build(thread, isStd, logger.showStd, handler, LvError, strs)
}

func (logger *starlog) Errorln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvError, strs)
}

func (logger *starlog) Critical(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvCritical, strs)
}

func (logger *starlog) Criticalf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	logger.build(thread, isStd, logger.showStd, handler, LvCritical, strs)
}

func (logger *starlog) Criticalln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvCritical, strs)
}

func (logger *starlog) Fatal(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvFatal, strs)
	os.Exit(9)
}

func (logger *starlog) Fatalf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	logger.build(thread, isStd, logger.showStd, handler, LvFatal, strs)
	os.Exit(9)
}

func (logger *starlog) Fatalln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvFatal, strs)
	os.Exit(9)
}

func (logger *starlog) Panic(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvPanic, strs)
	panic(str)
}

func (logger *starlog) Panicf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	logger.build(thread, isStd, logger.showStd, handler, LvPanic, strs)
	panic(fmt.Sprintf(format, str...))
}

func (logger *starlog) Panicln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	logger.build(thread, isStd, logger.showStd, handler, LvPanic, strs)
	panic(fmt.Sprintln(str...))
}

func (logger *starlog) Print(thread string, isStd bool, isShow bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	if isShow {
		fmt.Print(strs)
	}
	logger.write(strs)
}

func (logger *starlog) Printf(thread string, isStd bool, isShow bool, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	if isShow {
		fmt.Print(strs)
	}
	logger.write(strs)
}

func (logger *starlog) Println(thread string, isStd bool, isShow bool, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	if isShow {
		fmt.Print(strs)
	}
	logger.write(strs)
}

func (logger *starlog) Log(thread string, isStd bool, isShow bool, level int, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprint(str...)
	logger.build(thread, isStd, isShow, handler, level, strs)
}

func (logger *starlog) Logf(thread string, isStd bool, isShow bool, level int, handler func([]Attr, string), format string, str ...interface{}) {
	strs := fmt.Sprintf(format, str...)
	logger.build(thread, isStd, isShow, handler, level, strs)
}

func (logger *starlog) Logln(thread string, isStd bool, isShow bool, level int, handler func([]Attr, string), str ...interface{}) {
	strs := fmt.Sprintln(str...)
	logger.build(thread, isStd, isShow, handler, level, strs)
}
