package main

import (
	"embed"
	"errors"
	"io/fs"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"gitea.twomorecents.org/Rockingcool/ccat/stack"
	"gitea.twomorecents.org/Rockingcool/kleingrep/regex"

	"gopkg.in/yaml.v2"
)

//go:embed config
var storedConfigs embed.FS // Embed the folder containing config files

// runningOnWindows: At the moment this function isn't used. When Window support is added,
// it will be used to determine if the program is being run on Windows.
func runningOnWindows() bool {
	return runtime.GOOS == "windows"
}

// generateDefaultConfigs is used to generate a folder of default config files
// for common languages. These default config files are embedded into the program, and will
// be outputted into the given directory.
//
// If there is an error encountered, the error is returned.
func generateDefaultConfigs(configOutputPath string) error {
	err := os.MkdirAll(configOutputPath, 0755)
	if err != nil {
		if os.IsExist(err) {
			return errors.New("Directory already exists.")
		} else {
			return errors.New("Unable to create directory.")
		}
	}

	// Copy each folder from the embedded filesystem, into the destination path
	err = fs.WalkDir(storedConfigs, "config", func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if d.IsDir() { // Skip directories
			return nil
		}
		relPath, _ := filepath.Rel("config", path)
		dstPath := filepath.Join(configOutputPath, relPath) // Destination path

		data, err := storedConfigs.ReadFile(path)
		if err != nil {
			return err
		}

		if err := os.WriteFile(dstPath, data, 0644); err != nil {
			return err
		}
		return nil
	})
	return nil
}

// loadConfig takes in the filename of a config file. It reads the file,
// and returns a stack of RegColors, with the item at the bottom being the one that
// was read first. This ensures that, _when accessing the RegColors in the stack, the last
// one (ie. the one that was read first) has highest precedence_.
// If there is an error compiling the regular expressions, the error is returned.
func loadConfig(configFilename string) (stack.Stack[regColor], error) {
	configFile, err := os.ReadFile(configFilename)
	if err != nil {
		return *stack.NewStack[regColor](0), err
	}

	// Here, I create a MapSlice. This is a slice of key-value pairs, and will
	// store the results of unmarshalling the YAML file.
	tempMapSlice := yaml.MapSlice{}
	if err := yaml.Unmarshal(configFile, &tempMapSlice); err != nil {
		return *stack.NewStack[regColor](0), err
	}

	// Here, I create the stack which will eventually be returned.
	// Each element of the MapSlice (created above) stores the key and value of a line
	// in the file.
	// Each regex string is compiled, and if there is an error, that error is
	//  returned.
	regColorStack := stack.NewStack[regColor](len(strings.Split(string(configFile), "\n"))) // The stack will have the same size as the number of lines in the file
	for _, item := range tempMapSlice {
		re := regex.MustCompile(item.Key.(string), regex.RE_MULTILINE, regex.RE_SINGLE_LINE)
		clr, err := newColor(item.Value.(string))
		if err != nil {
			return *stack.NewStack[regColor](0), err
		}
		// If we got past the errors, then the color _must_ be valid.
		regColorStack.Push(regColor{&re, clr})
	}

	return *regColorStack, nil
}