package storage

import "unicode/utf8"

// Entries is for sorting by Position
type Entries []Entry

func (e Entries) Len() int           { return len(e) }
func (e Entries) Swap(i, j int)      { e[i], e[j] = e[j], e[i] }
func (e Entries) Less(i, j int) bool { return e[i].Position < e[j].Position }

// Type of Entry
type Type int

const (
	// FileType represents a file payload from the tar stream.
	//
	// This will be used to map to relative paths on disk. Only Size > 0 will get
	// read into a resulting output stream (due to hardlinks).
	FileType Type = 1 + iota
	// SegmentType represents a raw bytes segment from the archive stream. These raw
	// byte segments consist of the raw headers and various padding.
	//
	// Its payload is to be marshalled base64 encoded.
	SegmentType
)

// Entry is the structure for packing and unpacking the information read from
// the Tar archive.
//
// FileType Payload checksum is using `hash/crc64` for basic file integrity,
// _not_ for cryptography.
// From http://www.backplane.com/matt/crc64.html, CRC32 has almost 40,000
// collisions in a sample of 18.2 million, CRC64 had none.
type Entry struct {
	Type     Type   `json:"type"`
	Name     string `json:"name,omitempty"`
	NameRaw  []byte `json:"name_raw,omitempty"`
	Size     int64  `json:"size,omitempty"`
	Payload  []byte `json:"payload"` // SegmentType stores payload here; FileType stores crc64 checksum here;
	Position int    `json:"position"`
}

// SetName will check name for valid UTF-8 string, and set the appropriate
// field. See https://github.com/vbatts/tar-split/issues/17
func (e *Entry) SetName(name string) {
	if utf8.ValidString(name) {
		e.Name = name
	} else {
		e.NameRaw = []byte(name)
	}
}

// SetNameBytes will check name for valid UTF-8 string, and set the appropriate
// field
func (e *Entry) SetNameBytes(name []byte) {
	if utf8.Valid(name) {
		e.Name = string(name)
	} else {
		e.NameRaw = name
	}
}

// GetName returns the string for the entry's name, regardless of the field stored in
func (e *Entry) GetName() string {
	if len(e.NameRaw) > 0 {
		return string(e.NameRaw)
	}
	return e.Name
}

// GetNameBytes returns the bytes for the entry's name, regardless of the field stored in
func (e *Entry) GetNameBytes() []byte {
	if len(e.NameRaw) > 0 {
		return e.NameRaw
	}
	return []byte(e.Name)
}