package log

import (
	"fmt"
	"os"
	"strconv"
	"sync"
	"time"
)

type File struct {
	Filename    string `json:"filename"`
	MaxSize     int64  `json:"max_size"`
	MaxLogFiles int    `json:"max_log_files"`
	Format      string `json:"format"`
	Level       int    `json:"level"`
	mutex       sync.RWMutex
	buf         []byte
	fp          *os.File
	prefix      string
	size        int64
}

func itoa(buf *[]byte, i int, wid int) {
	// Assemble decimal in reverse order.
	var b [20]byte
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	// i < 10
	b[bp] = byte('0' + i)
	*buf = append(*buf, b[bp:]...)
}

func (lg *File) SetLevel(lv int) {
	lg.Level = lv
}

func (lg *File) Prefix(s string) {
	lg.prefix = s
}

func (lg *File) Print(i ...interface{}) {
	lg.write(TraceLevel, fmt.Sprint(i...))
}

func (lg *File) Printf(format string, args ...interface{}) {
	lg.write(TraceLevel, fmt.Sprintf(format, args...))
}

func (lg *File) Debug(i ...interface{}) {
	lg.write(DebugLevel, fmt.Sprint(i...))
}

func (lg *File) Debugf(format string, args ...interface{}) {
	lg.write(DebugLevel, fmt.Sprintf(format, args...))
}

func (lg *File) Info(i ...interface{}) {
	lg.write(InfoLevel, fmt.Sprint(i...))
}

func (lg *File) Infof(format string, args ...interface{}) {
	lg.write(InfoLevel, fmt.Sprintf(format, args...))
}

func (lg *File) Warn(i ...interface{}) {
	lg.write(WarnLevel, fmt.Sprint(i...))
}

func (lg *File) Warnf(format string, args ...interface{}) {
	lg.write(WarnLevel, fmt.Sprintf(format, args...))
}

func (lg *File) Error(i ...interface{}) {
	lg.write(ErrorLevel, fmt.Sprint(i...))
}

func (lg *File) Errorf(format string, args ...interface{}) {
	lg.write(ErrorLevel, fmt.Sprintf(format, args...))
}

func (lg *File) Fatal(i ...interface{}) {
	lg.write(FatalLevel, fmt.Sprint(i...))
}

func (lg *File) Fatalf(format string, args ...interface{}) {
	lg.write(FatalLevel, fmt.Sprintf(format, args...))
}

func (lg *File) Panic(i ...interface{}) {
	lg.write(PanicLevel, fmt.Sprint(i...))
}

func (lg *File) Panicf(format string, args ...interface{}) {
	lg.write(PanicLevel, fmt.Sprintf(format, args...))
}

func (lg *File) format(buf *[]byte, level int, s string) (err error) {
	t := time.Now()
	year, month, day := t.Date()
	itoa(buf, year, 4)
	*buf = append(*buf, '-')
	itoa(buf, int(month), 2)
	*buf = append(*buf, '-')
	itoa(buf, day, 2)
	*buf = append(*buf, ' ')
	hour, min, sec := t.Clock()
	itoa(buf, hour, 2)
	*buf = append(*buf, ':')
	itoa(buf, min, 2)
	*buf = append(*buf, ':')
	itoa(buf, sec, 2)
	*buf = append(*buf, ' ')
	*buf = append(*buf, '[')
	*buf = append(*buf, getLevelText(level)...)
	*buf = append(*buf, ']')
	*buf = append(*buf, ' ')
	*buf = append(*buf, s...)
	return
}

//Write 实现标准的写入行数
func (lg *File) Write(p []byte) (n int, err error) {
	lg.mutex.Lock()
	if n, err = lg.fp.Write(p); err == nil {
		lg.mutex.Unlock()
		lg.size += int64(n)
		if lg.MaxSize > 0 && lg.size >= lg.MaxSize {
			if err = lg.rotate(); err != nil {
				return
			}
			lg.size = 0
		}
	} else {
		lg.mutex.Unlock()
	}
	return
}

func (lg *File) write(level int, s string) {
	var (
		n   int
		err error
	)
	if lg.Level > level {
		return
	}
	lg.mutex.Lock()
	lg.buf = lg.buf[:0]
	if err = lg.format(&lg.buf, level, s); err != nil {
		lg.mutex.Unlock()
		return
	}
	lg.buf = append(lg.buf, '\n')
	if n, err = lg.fp.Write(lg.buf); err != nil {
		lg.mutex.Unlock()
		return
	}
	lg.mutex.Unlock()
	//write br
	lg.size += int64(n)
	if lg.MaxSize > 0 && lg.size >= lg.MaxSize {
		if err = lg.rotate(); err != nil {
			return
		}
		lg.size = 0
	}
}

func (lg *File) isExists(filename string) bool {
	if _, err := os.Stat(filename); err == nil {
		return true
	} else {
		return false
	}
}

//rotate 实现日志滚动处理
func (lg *File) rotate() (err error) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	if err = lg.close(); err != nil {
		return
	}
	for i := lg.MaxLogFiles; i >= 0; i-- {
		filename := lg.Filename
		if i > 0 {
			filename += "." + strconv.Itoa(i)
		}
		if i == lg.MaxLogFiles {
			if lg.isExists(filename) {
				if err = os.Remove(filename); err != nil {
					return
				}
			}
		} else {
			if lg.isExists(filename) {
				if err = os.Rename(filename, lg.Filename+"."+strconv.Itoa(i+1)); err != nil {
					return
				}
			}
		}
	}
	err = lg.open()
	return
}

func (lg *File) Reload() (err error) {
	lg.mutex.Lock()
	_ = lg.close()
	err = lg.open()
	lg.mutex.Unlock()
	return
}

func (lg *File) open() (err error) {
	if lg.fp, err = os.OpenFile(lg.Filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600); err != nil {
		return
	}
	return
}

func (lg *File) Open() (err error) {
	var (
		info os.FileInfo
	)
	if err = lg.open(); err != nil {
		return
	}
	if info, err = os.Stat(lg.Filename); err == nil {
		lg.size = info.Size()
	}
	return
}

func (lg *File) close() (err error) {
	if lg.fp != nil {
		err = lg.fp.Close()
	}
	return
}

func (lg *File) Close() (err error) {
	err = lg.close()
	return
}

func NewFileLogger(filename string) *File {
	lg := &File{Filename: filename, buf: make([]byte, 1024)}
	return lg
}
