package starlog

import (
	"fmt"
	"io"
	"math/rand"
	"sync"
	"time"

	"b612.me/starlog/colorable"
	"b612.me/starmap"
)

const (
	LvDebug = iota
	LvInfo
	LvNotice
	LvWarning
	LvError
	LvCritical
	LvPanic
	LvFatal
)

var (
	levels = map[int]string{
		LvDebug:    "DEBUG",
		LvInfo:     "INFO",
		LvNotice:   "NOTICE",
		LvWarning:  "WARNING",
		LvError:    "ERROR",
		LvCritical: "CRITICAL",
		LvPanic:    "PANIC",
		LvFatal:    "FATAL",
	}
	stacks        *starmap.StarStack
	stackStarted  bool = false
	stackStopChan chan int
	stackMu       sync.Mutex
	stdScreen     io.Writer = colorable.NewColorableStdout()
)

type starlog struct {
	mu             *sync.Mutex
	output         io.Writer
	showFuncName   bool
	showThread     bool
	showLevel      bool
	showDeatilFile bool
	showColor      bool
	switching      bool
	showStd        bool
	onlyColorLevel bool
	stopWriter     bool
	id             string

	colorList map[int][]Attr
	colorMe   map[int]*Color
}

type StarLogger struct {
	thread      string
	handlerFunc func([]Attr, string)
	logcore     *starlog
	isStd       bool
}

type logTransfer struct {
	handlerFunc func([]Attr, string)
	colors      []Attr
	logStr      string
}

func newLogCore(out io.Writer) *starlog {
	return &starlog{
		mu:             &sync.Mutex{},
		output:         out,
		showFuncName:   true,
		showThread:     true,
		showLevel:      true,
		showStd:        true,
		showDeatilFile: true,
		switching:      false,
		stopWriter:     false,
		showColor:      true,
		id:             generateId(),
		colorList: map[int][]Attr{
			LvDebug:    []Attr{FgWhite},
			LvInfo:     []Attr{FgGreen},
			LvNotice:   []Attr{FgBlue},
			LvWarning:  []Attr{FgYellow},
			LvError:    []Attr{FgMagenta},
			LvCritical: []Attr{FgRed, Bold},
			LvPanic:    []Attr{FgRed, Bold},
			LvFatal:    []Attr{FgRed},
		},
		colorMe: map[int]*Color{
			LvDebug:    NewColor([]Attr{FgWhite}...),
			LvInfo:     NewColor([]Attr{FgGreen}...),
			LvNotice:   NewColor([]Attr{FgBlue}...),
			LvWarning:  NewColor([]Attr{FgYellow}...),
			LvError:    NewColor([]Attr{FgMagenta}...),
			LvCritical: NewColor([]Attr{FgRed, Bold}...),
			LvPanic:    NewColor([]Attr{FgRed, Bold}...),
			LvFatal:    NewColor([]Attr{FgRed}...),
		},
	}
}

func NewStarlog(out io.Writer) *StarLogger {
	return &StarLogger{
		handlerFunc: nil,
		thread:      "MAN",
		logcore:     newLogCore(out),
		isStd:       false,
	}
}

func (logger *StarLogger) NewFlag() *StarLogger {
	return &StarLogger{
		thread:      getRandomFlag(false),
		handlerFunc: logger.handlerFunc,
		logcore:     logger.logcore,
		isStd:       false,
	}
}
func (logger *StarLogger) SetNewRandomFlag() {
	logger.thread = getRandomFlag(false)
}

func getRandomFlag(isMain bool) string {
	rand.Seed(time.Now().UnixNano())
	if isMain {
		return "MAN"
	}
	flag := "MAN"
	for flag == "MAN" {
		flag = string([]byte{uint8(rand.Intn(26) + 65), uint8(rand.Intn(26) + 65), uint8(rand.Intn(26) + 65)})
	}
	return flag
}

func generateId() string {
	rand.Seed(time.Now().UnixNano())
	return fmt.Sprintf("%dstar%db612%d", time.Now().UnixNano(), rand.Intn(1000000), rand.Intn(1000000))
}

func StartStacks() {
	stackMu.Lock()
	if stackStarted {
		stackMu.Unlock()
		return
	}
	go func() {
		stackStarted = true
		stackMu.Unlock()
		defer func() {
			stackStarted = false
		}()
		for {
			select {
			case <-stackStopChan:
				return
			default:
			}
			poped := stacks.MustPop()
			if poped == nil {
				time.Sleep(time.Microsecond * 500)
				continue
			}
			val := poped.(logTransfer)
			if val.handlerFunc != nil {
				val.handlerFunc(val.colors, val.logStr)
			}
		}
	}()
}

func StopStacks() {
	if !stackStarted {
		return
	}
	stackStopChan <- 1
}

func Stop() {
	StopStacks()
}
