package filetree

import (
	"archive/tar"
	"bytes"
	"crypto/md5"
	"fmt"
	"io"
)

// enum to show whether a file has changed
const (
	Unchanged DiffType = iota
	Changed
	Added
	Removed
)

type NodeData struct {
	ViewInfo  ViewInfo
	FileInfo  FileInfo
	DiffType  DiffType
}

type ViewInfo struct {
	Collapsed bool
	Hidden    bool
}

type FileInfo struct {
	Path      string
	Typeflag  byte
	MD5sum    [16]byte
	TarHeader tar.Header
}

type DiffType int

func NewNodeData() (*NodeData) {
	return &NodeData{
		ViewInfo: *NewViewInfo(),
		FileInfo: FileInfo{},
		DiffType: Unchanged,
	}
}

func (data *NodeData) Copy() (*NodeData) {
	return &NodeData{
		ViewInfo: *data.ViewInfo.Copy(),
		FileInfo: *data.FileInfo.Copy(),
		DiffType: data.DiffType,
	}
}


func NewViewInfo() (view *ViewInfo) {
	return &ViewInfo{
		Collapsed: false,
		Hidden: false,
	}
}

func (view *ViewInfo) Copy() (newView *ViewInfo) {
	newView = NewViewInfo()
	*newView = *view
	return newView
}

func NewFileInfo(reader *tar.Reader, header *tar.Header, path string) FileInfo {
	if header.Typeflag == tar.TypeDir {
		return FileInfo{
			Path:     path,
			Typeflag: header.Typeflag,
			MD5sum:   [16]byte{},
			TarHeader: *header,
		}
	}
	fileBytes := make([]byte, header.Size)
	_, err := reader.Read(fileBytes)
	if err != nil && err != io.EOF {
		panic(err)
	}

	return FileInfo{
		Path:      path,
		Typeflag:  header.Typeflag,
		MD5sum:    md5.Sum(fileBytes),
		TarHeader: *header,
	}
}

func (d DiffType) String() string {
	switch d {
	case Unchanged:
		return "Unchanged"
	case Changed:
		return "Changed"
	case Added:
		return "Added"
	case Removed:
		return "Removed"
	default:
		return fmt.Sprintf("%d", int(d))
	}
}

func (a DiffType) merge(b DiffType) DiffType {
	if a == b {
		return a
	}
	return Changed
}

func (data *FileInfo) Copy() *FileInfo {
	if data == nil {
		return nil
	}
	return &FileInfo{
		Path:      data.Path,
		Typeflag:  data.Typeflag,
		MD5sum:    data.MD5sum,
		TarHeader: data.TarHeader,
	}
}

func (data *FileInfo) getDiffType(other FileInfo) DiffType {
	if data.Typeflag == other.Typeflag {
		if bytes.Compare(data.MD5sum[:], other.MD5sum[:]) == 0 {
			return Unchanged
		}
	}
	return Changed
}