package fsutil

import (
	"bytes"
	"io"
	"os"
	"path"
	"path/filepath"
)

// perm for create dir or file
var (
	DefaultDirPerm   os.FileMode = 0775
	DefaultFilePerm  os.FileMode = 0665
	OnlyReadFilePerm os.FileMode = 0444
)

var (
	// DefaultFileFlags for create and write
	DefaultFileFlags = os.O_CREATE | os.O_WRONLY | os.O_APPEND
	// OnlyReadFileFlags open file for read
	OnlyReadFileFlags = os.O_RDONLY
)

// alias methods
var (
	DirExist  = IsDir
	FileExist = IsFile
	PathExist = PathExists
)

// PathExists reports whether the named file or directory exists.
func PathExists(path string) bool {
	if path == "" {
		return false
	}

	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// IsDir reports whether the named directory exists.
func IsDir(path string) bool {
	if path == "" || len(path) > 468 {
		return false
	}

	if fi, err := os.Stat(path); err == nil {
		return fi.IsDir()
	}
	return false
}

// FileExists reports whether the named file or directory exists.
func FileExists(path string) bool {
	return IsFile(path)
}

// IsFile reports whether the named file or directory exists.
//
// - NOTE: not support symlink file
func IsFile(path string) bool {
	if path == "" || len(path) > 468 {
		return false
	}

	if fi, err := os.Stat(path); err == nil {
		return !fi.IsDir()
	}
	return false
}

// IsSymlink reports whether the named file is a symlink.
func IsSymlink(path string) bool {
	fi, err := os.Lstat(path)
	if err != nil {
		return false
	}
	return fi.Mode()&os.ModeSymlink != 0
}

// IsAbsPath is abs path check
func IsAbsPath(aPath string) bool {
	if len(aPath) > 0 {
		if aPath[0] == '/' {
			return true
		}
		return filepath.IsAbs(aPath)
	}
	return false
}

// IsAbsPath2 is abs path check. if start withs / OR ~, will direct return true.
func IsAbsPath2(aPath string) bool {
	if len(aPath) > 0 {
		if aPath[0] == '/' || aPath[0] == '~' {
			return true
		}
		return filepath.IsAbs(aPath)
	}
	return false
}

// IsEmptyDir reports whether the named directory is empty.
func IsEmptyDir(dirPath string) bool {
	f, err := os.Open(dirPath)
	if err != nil {
		return false
	}
	defer f.Close()

	_, err = f.Readdirnames(1)
	return err == io.EOF
}

// ImageMimeTypes refer net/http package
var ImageMimeTypes = map[string]string{
	"bmp": "image/bmp",
	"gif": "image/gif",
	"ief": "image/ief",
	"jpg": "image/jpeg",
	// "jpe":  "image/jpeg",
	"jpeg": "image/jpeg",
	"png":  "image/png",
	"svg":  "image/svg+xml",
	"ico":  "image/x-icon",
	"webp": "image/webp",
}

// IsImageFile check file is image file.
func IsImageFile(path string) bool {
	mime := MimeType(path)
	if mime == "" {
		return false
	}

	for _, imgMime := range ImageMimeTypes {
		if imgMime == mime {
			return true
		}
	}
	return false
}

// IsZipFile check is zip file.
// from https://blog.csdn.net/wangshubo1989/article/details/71743374
func IsZipFile(filepath string) bool {
	f, err := os.Open(filepath)
	if err != nil {
		return false
	}
	defer f.Close()

	buf := make([]byte, 4)
	if n, err := f.Read(buf); err != nil || n < 4 {
		return false
	}

	return bytes.Equal(buf, []byte("PK\x03\x04"))
}

// PathMatch check for a string. alias of path.Match()
func PathMatch(pattern, s string) bool {
	ok, err := path.Match(pattern, s)
	if err != nil {
		ok = false
	}
	return ok
}
