package common

import (
	"bytes"
	"encoding/binary"
	"errors"
	"hash/crc32"
	"os"
)

const (
	MDUMP_MAGIC_CODE      uint32 = 0xdeadfee1
	MDUMP_FILEHEAD_LEN           = 16
	MDUMP_VERSION_LASTEST        = 1
)

var (
	errMdumpEmpty      = errors.New(".mdump empty")
	errMdumpFormat     = errors.New(".mdump format error")
	errMdumpCheckSum   = errors.New(".mdump checksum error")
	errMdumpTruncation = errors.New(".mdump truncation error")
)

// 对象序列化接口
type Serializable interface {
	//序列化
	Marshal() ([]byte, error)
	//反序列化
	Unmarshal([]byte, interface{}) error
}

// 内存dump文件头
type MDumpFileHead struct {
	MagicCode uint32 //mdump文件标记
	Version   uint32 //版本号
	DataLen   uint32 //数据区长度
	CheckSum  uint32 //校验和
}

func ReadMDumpFile(fileName string) (head *MDumpFileHead, data []byte, err error) {
	buf, err := os.ReadFile(fileName)
	if err != nil {
		return
	}
	os.Remove(fileName)
	//解析文件头
	head = &MDumpFileHead{}
	err = binary.Read(bytes.NewReader(buf), binary.BigEndian, head)
	if err != nil {
		return
	}

	if head.MagicCode != MDUMP_MAGIC_CODE {
		err = errMdumpFormat
		return
	}

	data = buf[MDUMP_FILEHEAD_LEN:]
	if uint32(len(data)) != head.DataLen {
		err = errMdumpTruncation
		return
	}

	checkSum := crc32.ChecksumIEEE(data)
	if checkSum != head.CheckSum {
		err = errMdumpCheckSum
		return
	}

	return
}

func WriteMDumpFile(fileName string, data []byte) error {
	if len(data) == 0 {
		return errMdumpEmpty
	}

	checkSum := crc32.ChecksumIEEE(data)
	head := MDumpFileHead{
		MagicCode: MDUMP_MAGIC_CODE,
		Version:   MDUMP_VERSION_LASTEST,
		DataLen:   uint32(len(data)),
		CheckSum:  checkSum,
	}

	file, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer file.Close()
	//写文件头
	err = binary.Write(file, binary.BigEndian, &head)
	if err != nil {
		return err
	}
	//写数据
	_, err = file.WriteAt(data, int64(MDUMP_FILEHEAD_LEN))
	if err != nil {
		return err
	}

	return err
}
