package common

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"go_dreamfactory/lego/core"
	"io"
	"io/ioutil"
	"math"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cast"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
)

func MF(s1 core.M_Modules, s2 string) string {
	return fmt.Sprintf("%s.%s", s1, s2)
}

func FormatJson(data string) (string, error) {
	var out bytes.Buffer
	err := json.Indent(&out, []byte(data), "", "    ")
	return out.String(), err
}

func SubStr(str string, start int, length int) (result string) {
	s := []rune(str)
	total := len(s)
	if total == 0 {
		return
	}
	// 允许从尾部开始计算
	if start < 0 {
		start = total + start
		if start < 0 {
			return
		}
	}
	if start > total {
		return
	}
	// 到末尾
	if length < 0 {
		length = total
	}

	end := start + length
	if end > total {
		result = string(s[start:])
	} else {
		result = string(s[start:end])
	}

	return
}

// 是否升级
func IsUpgrade(newVersion, oldVersion string) bool {
	nvArr := strings.SplitN(newVersion, ".", 3)
	if len(nvArr) != 3 {
		logrus.Error("new version format err")
		return false
	}

	ovArr := strings.SplitN(oldVersion, ".", 3)
	if len(ovArr) != 3 {
		logrus.Error("old version format err")
		return false
	}

	nvNum := cast.ToInt(nvArr[0])*100 + cast.ToInt(nvArr[1])*10 + cast.ToInt(nvArr[2])
	ovNum := cast.ToInt(ovArr[0])*100 + cast.ToInt(ovArr[1])*10 + cast.ToInt(ovArr[2])

	if nvNum > ovNum {
		return true
	}
	return false
}

func Copy(src, dst string) (len int64, err error) {

	dstWriter, err := os.Create(dst)
	if err != nil {
		logrus.Error(err)
		return
	}
	srcReader, err := os.Open(src)
	if err != nil {
		logrus.Error(err)
		return
	}
	// Copy()函数其实是调用了
	// io包中私有函数copyBuffer() 默认缓冲区是32K
	// 与Copy()函数功能一致的是CopyBuffer()可以设置缓冲区
	len, err = io.Copy(dstWriter, srcReader)
	if err != nil {
		logrus.Error(err)
		return
	}
	return
}

func DeleteFile(filePath string) error {
	return os.Remove(filePath)
}

func RemoveContents(dir string) error {
	d, err := os.Open(dir)
	if err != nil {
		return err
	}
	defer d.Close()
	names, err := d.Readdirnames(-1)
	if err != nil {
		return err
	}
	for _, name := range names {
		err = os.RemoveAll(filepath.Join(dir, name))
		if err != nil {
			return err
		}
	}
	return nil
}

func ConvertFileSize(size int64) string {
	if size == 0 {
		return "0"
	}

	kb := float64(size) / float64(1024)

	if kb < 1024 {
		f := math.Ceil(kb)
		return strconv.Itoa(int(f)) + " KB"
	}

	mb := kb / float64(1024)
	if mb < 1024 {
		f := math.Ceil(mb)
		return strconv.Itoa(int(f)) + " MB"
	}

	gb := mb / float64(1024)
	if gb < 1024 {
		f := math.Ceil(mb)
		return strconv.Itoa(int(f)) + " G"
	}

	t := gb / float64(1024)
	if t < 1024 {
		f := math.Ceil(mb)
		return strconv.Itoa(int(f)) + " T"
	}

	if t >= 1024 {
		return "VeryBig"
	}

	return "0"
}

type Config struct {
	Path string
}

func (this *Config) SetPath(path string) {
	this.Path = path
}

// "F:\\projects\\workspace\\go_dreamfactory\\bin\\json"
func (this *Config) Loader(file string) ([]map[string]interface{}, error) {
	if this.Path == "" {
		return nil, errors.New("no setting config path")
	}
	path := filepath.Join(this.Path, file+".json")
	if bytes, err := ioutil.ReadFile(path); err != nil {
		return nil, err
	} else {
		jsonData := make([]map[string]interface{}, 0)
		if err = json.Unmarshal(bytes, &jsonData); err != nil {
			return nil, err
		}
		return jsonData, nil
	}
}

func Json2Pb(jsonString string, pb proto.Message) error {
	m := pb.ProtoReflect().Interface()
	if err := protojson.Unmarshal([]byte(jsonString), m); err != nil {
		return err
	}
	return nil
}

func Pb2Json(pb proto.Message) string {
	return protojson.Format(pb.ProtoReflect().Interface())
}

// 保留两位小数 通用
func FormatFloatCommon(num float64) float64 {
	value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", num), 64)
	return value
}

// 保留两位小数，舍弃尾数，无进位运算
// 主要逻辑就是先乘，trunc之后再除回去，就达到了保留N位小数的效果
func FormatFloat(num float64, decimal int) (float64, error) {
	// 默认乘1
	d := float64(1)
	if decimal > 0 {
		// 10的N次方
		d = math.Pow10(decimal)
	}
	// math.trunc作用就是返回浮点数的整数部分
	// 再除回去，小数点后无效的0也就不存在了
	res := strconv.FormatFloat(math.Trunc(num*d)/d, 'f', -1, 64)
	return strconv.ParseFloat(res, 64)
}

// 强制舍弃尾数
func FormatFloatFloor(num float64, decimal int) (float64, error) {
	// 默认乘1
	d := float64(1)
	if decimal > 0 {
		// 10的N次方
		d = math.Pow10(decimal)
	}
	// math.trunc作用就是返回浮点数的整数部分
	// 再除回去，小数点后无效的0也就不存在了
	res := strconv.FormatFloat(math.Floor(num*d)/d, 'f', -1, 64)
	return strconv.ParseFloat(res, 64)
}

// 舍弃的尾数不为0，强制进位
func FormatFloatCeil(num float64, decimal int) (float64, error) {
	// 默认乘1
	d := float64(1)
	if decimal > 0 {
		// 10的N次方
		d = math.Pow10(decimal)
	}
	// math.trunc作用就是返回浮点数的整数部分
	// 再除回去，小数点后无效的0也就不存在了
	res := strconv.FormatFloat(math.Ceil(num*d)/d, 'f', -1, 64)
	return strconv.ParseFloat(res, 64)
}

func LastChars(str string, count int) string {
	if len(str) <= count {
		return str
	}
	return str[len(str)-count:]
}
