package log

import (
	"fmt"
	"reflect"
	"strings"
	"sync"
	"time"

	"go_dreamfactory/lego/utils/pools"
)

var (
	_cePool = sync.Pool{New: func() interface{} {
		// Pre-allocate some space for cores.
		return &Entry{
			Data: make([]Field, 6),
		}
	}}
)

func getEntry() *Entry {
	return _cePool.Get().(*Entry)
}
func putEntry(e *Entry) {
	e.reset()
	_cePool.Put(e)
}

//日志堆栈信息
type EntryCaller struct {
	Defined  bool
	PC       uintptr
	File     string
	Line     int
	Function string
	Stack    string
}

func (ec EntryCaller) String() string {
	return ec.FullPath()
}
func (ec EntryCaller) FullPath() string {
	if !ec.Defined {
		return "undefined"
	}
	buf := pools.BufferPoolGet()
	buf.AppendString(ec.File)
	buf.AppendByte(':')
	buf.AppendInt(int64(ec.Line))
	caller := buf.String()
	buf.Free()
	return caller
}
func (ec EntryCaller) TrimmedPath() string {
	if !ec.Defined {
		return "undefined"
	}
	idx := strings.LastIndexByte(ec.File, '/')
	if idx == -1 {
		return ec.FullPath()
	}
	idx = strings.LastIndexByte(ec.File[:idx], '/')
	if idx == -1 {
		return ec.FullPath()
	}
	buf := pools.BufferPoolGet()
	buf.AppendString(ec.File[idx+1:])
	buf.AppendByte(':')
	buf.AppendInt(int64(ec.Line))
	caller := buf.String()
	buf.Free()
	return caller
}

type Entry struct {
	Name    string
	Level   Loglevel
	Caller  EntryCaller
	Time    time.Time
	Message string
	Data    []Field
	Err     string
}

func (entry *Entry) reset() {
	entry.Message = ""
	entry.Caller.Defined = false
	entry.Caller.File = ""
	entry.Caller.Line = 0
	entry.Caller.Function = ""
	entry.Caller.Stack = ""
	entry.Err = ""
	entry.Data = make([]Field, 6)
}

func (entry *Entry) WithFields(fields ...Field) {

	fieldErr := entry.Err
	if len(fields) > len(entry.Data) {
		entry.Data = append(entry.Data, make([]Field, len(fields)-len(entry.Data))...)
	}
	for k, v := range fields {
		isErrField := false
		if t := reflect.TypeOf(v); t != nil {
			switch {
			case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
				isErrField = true
			}
		}
		if isErrField {
			tmp := fmt.Sprintf("can not add field %q", k)
			if fieldErr != "" {
				fieldErr = entry.Err + ", " + tmp
			} else {
				fieldErr = tmp
			}
		} else {
			entry.Data[k] = v
		}
	}
}
