//
// dir_render.go
// Copyright (C) 2024 veypi <i@veypi.com>
// 2024-10-18 19:35
// Distributed under terms of the GPL license.
//

package webdav

import (
	"embed"
	"fmt"
	"html/template"
	"io/fs"
	"net/http"
	"net/url"
	"strings"

	"github.com/veypi/vigo/logv"
)

type anyDirs interface {
	len() int
	name(i int) string
	isDir(i int) bool
}

type fileInfoDirs []fs.FileInfo

func (d fileInfoDirs) len() int          { return len(d) }
func (d fileInfoDirs) isDir(i int) bool  { return d[i].IsDir() }
func (d fileInfoDirs) name(i int) string { return d[i].Name() }

type dirEntryDirs []fs.DirEntry

func (d dirEntryDirs) len() int          { return len(d) }
func (d dirEntryDirs) isDir(i int) bool  { return d[i].IsDir() }
func (d dirEntryDirs) name(i int) string { return d[i].Name() }

//go:embed dir.html
var dirTemplate embed.FS // 嵌入文件系统

func size2Label(s int64) string {
	if s < 1024 {
		return fmt.Sprintf("%d B", s)
	} else if s < 1048576 {
		return fmt.Sprintf("%d KB", s/1024)
	} else if s < 1073741824 {
		return fmt.Sprintf("%d MB", s/1024/1024)
	} else {
		return fmt.Sprintf("%d MB", s/1024/1024/1024)
	}
}

func dirList(w http.ResponseWriter, r *http.Request, f File, rootPath string, rootIndex int) {

	// Prefer to use ReadDir instead of Readdir,
	// because the former doesn't require calling
	// Stat on every entry of a directory on Unix.
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
		if err != nil {
			logv.Warn().Msgf("http: error reading directory: %v", err)
			http.Error(w, "Error reading directory", http.StatusInternalServerError)
		}
	}()
	dir_count := 0
	f_count := 0
	var file_bytes int64
	files := make([][4]any, 0, 5)
	dirs := make([][4]any, 0, 5)
	if d, ok := f.(fs.ReadDirFile); ok {
		for _, item := range logv.AssertFuncErr(d.ReadDir(-1)) {
			if item.IsDir() {
				name := item.Name() + "/"
				dir_count += 1
				fstat, _ := item.Info()
				furl := url.URL{Path: name}
				dirs = append(dirs, [4]any{name, furl.String(), "-----", fstat.ModTime().UTC()})
			} else {
				name := item.Name()
				f_count += 1
				fstat, _ := item.Info()
				file_bytes += fstat.Size()
				furl := url.URL{Path: name}
				files = append(files, [4]any{name, furl.String(), size2Label(fstat.Size()), fstat.ModTime().UTC()})
			}
		}
	} else {
		for _, item := range logv.AssertFuncErr(d.ReadDir(-1)) {
			name := item.Name()
			f_count += 1
			fstat, _ := item.Info()
			file_bytes += fstat.Size()
			furl := url.URL{Path: name}
			files = append(files, [4]any{name, furl.String(), size2Label(fstat.Size()), fstat.ModTime().UTC()})
		}
	}

	dirBody := logv.AssertFuncErr(dirTemplate.ReadFile("dir.html"))

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	tpl := logv.AssertFuncErr(template.New("").Parse(string(dirBody)))
	pathTag := strings.Split(rootPath, "/")
	logv.AssertError(tpl.Execute(w, map[string]any{"files": files, "dirs": dirs, "path": pathTag[rootIndex:], "root": strings.Join(pathTag[:rootIndex], "/") + "/", "cdir": dir_count, "cfile": f_count, "size": size2Label(file_bytes)}))
}
