// Copyright (C) 2019 Marius Schellenberger

package fs

import (
	"errors"
	"git.giftfish.de/ston1th/docstore/pkg/core"
	"git.giftfish.de/ston1th/docstore/pkg/log"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

const (
	DirMode  = 0750
	FileMode = 0640
)

// Directory represents a directory and its contents
type Directory struct {
	Dirs  core.Paths
	Files core.Paths
}

type Filesystem struct {
	Base string
}

func NewFilesystem(base string) (fs *Filesystem, err error) {
	if !filepath.IsAbs(base) {
		err = errors.New("base dir '" + base + "' is not an absolute path")
		return
	}
	fi, err := os.Stat(base)
	if err != nil {
		err = os.Mkdir(base, DirMode)
		if err != nil {
			return
		}
		log.Printf("fs: created data directory '%s'", base)
		fi, err = os.Stat(base)
		if err != nil {
			return
		}
	}
	if !fi.IsDir() {
		err = errors.New("base dir '" + base + "' is not a directory")
		return
	}
	fs = &Filesystem{base}
	return
}

func (fs *Filesystem) ReadDir(path string) (d *Directory) {
	path = Clean(path)
	combined := filepath.Join(fs.Base, path)
	f, err := os.Open(combined)
	if err != nil {
		return
	}
	names, err := f.Readdirnames(0)
	if err != nil {
		return
	}
	d = new(Directory)
	for _, n := range names {
		fi, err := os.Stat(filepath.Join(combined, n))
		if err != nil {
			continue
		}
		m := fi.Mode()
		if m.IsDir() {
			d.Dirs = append(d.Dirs, core.Path{Name: n, Abs: filepath.Join(path, n)})
			continue
		}
		if m.IsRegular() {
			d.Files = append(d.Files, core.Path{Name: n, Abs: filepath.Join(path, n)})
		}
	}
	sort.Sort(d.Dirs)
	sort.Sort(d.Files)
	return
}

func (fs *Filesystem) Recursive(p string) (paths core.Paths) {
	p, _ = filepath.Split(Clean(p))
	if len(p) >= 2 {
		p = p[:len(p)-1]
	}
	filepath.Walk(fs.Base, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil
		}
		if info.IsDir() {
			a := strings.TrimPrefix(path, fs.Base)
			if a == "" {
				a = "/"
			}
			paths = append(paths, core.Path{Abs: a, Flag: a == p})
		}
		return nil
	})
	sort.Sort(paths)
	return
}

func (fs *Filesystem) RecursiveFiles(p string) (files []string) {
	filepath.Walk(filepath.Join(fs.Base, Clean(p)), func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil
		}
		if info.Mode().IsRegular() {
			files = append(files, strings.TrimPrefix(path, fs.Base))
		}
		return nil
	})
	return
}

func Paths(path string) (p core.Paths) {
	s := strings.Split(Clean(path), string(filepath.Separator))
	for i, v := range s[1:] {
		if v == "" {
			continue
		}
		if i == 0 {
			p = append(p, core.Path{Name: v, Abs: "/" + v})
			continue
		}
		p = append(p, core.Path{Name: v, Abs: filepath.Join(p[i-1].Abs, v)})
	}
	if len(p) != 0 {
		p[len(p)-1].Flag = true
	}
	return
}
