package systemlib

import (
	"os"
	"strings"

	"gitlab.com/olanguage/olang/olpfile"
)

var DEFAULT_PATH_NAMES = map[string]string{
	"OLIB_PATH":  "lib",
	"OBASE_PATH": "base",
	"OBIN_PATH":  "bin",
	"OTEMP_PATH": "tmp",
}

type System struct {
	Env  []Path
	User string
	OS   string
}

type Path struct {
	Key  string
	Path string
}

type Library struct {
	Name string
	File string
}
type Libraries []Library

func New() *System {
	system := new(System)
	for key, value := range DEFAULT_PATH_NAMES {
		if Env(key, "") == "" {
			Env(key, Env("HOME", "")+"/.olang/"+value)
			system.Env = append(system.Env, Path{key, Env(key, "")})
		}
	}
	system.User = os.Getenv("USER")
	system.OS = os.Getenv("OSTYPE")
	return system
}

func (system *System) GetEnv(key string) string {
	for _, env := range system.Env {
		if env.Key == key {
			return env.Path
		}
	}

	return ""
}

var libraries = make(Libraries, 0)

func (system *System) GetLibraries() Libraries {
	var libraries = make(Libraries, 0)

	for _, env := range system.Env {
		if env.Key == "OLIB_PATH" {
			var dir, _ = os.Open(env.Path)
			defer dir.Close()

			var folders, _ = dir.Readdir(-1)

			for _, folder := range folders {
				if folder.IsDir() {
					check, _ := os.Stat(env.Path + "/" + folder.Name() + "/library.olp")
					if check != nil {
						olp := olpfile.New("library.olp", env.Path+"/"+folder.Name()).Parse()
						if olp.Error == "" {

							var dir, _ = os.Open(env.Path + "/" + folder.Name() + "/" + olp.Parsed.Sources + "/")
							defer dir.Close()

							mainFile := Library{olp.Parsed.Main, env.Path + "/" + folder.Name() + "/" + olp.Parsed.Main}

							libraries = ScanFolder(folder.Name()+"/"+olp.Parsed.Sources, env, mainFile)
						} else {
							libraries = ScanFolder(folder.Name(), env, Library{Name: "", File: ""})
						}

					} else {
						libraries = ScanFolder(folder.Name(), env, Library{Name: "", File: ""})
					}

				}

			}
		}
	}

	return libraries
}

func ScanFolder(path string, env Path, mainFile Library) Libraries {
	scanFolder := env.Path + "/" + path

	var dir, _ = os.Open(scanFolder)
	defer dir.Close()

	var sources, _ = dir.Readdir(-1)

	for _, lib := range sources {
		if lib.IsDir() {
			if lib.Name() == "test" {
				continue
			}
			ScanFolder(path+"/"+lib.Name(), env, mainFile)
		}
		if strings.HasSuffix(lib.Name(), ".ola") {
			libraryFile := Library{lib.Name(), scanFolder + "/" + lib.Name()}
			if mainFile.Name != "" {
				if libraryFile.File == mainFile.File {
					continue
				}
			}

			if lib.Name() == "main.ola" {
				continue
			}

			libraries = append(libraries, libraryFile)
		}
	}

	return libraries
}

func CreateFile(path string) {
	// detect if file exists
	var _, err = os.Stat(path)

	// create file if not exists
	if os.IsNotExist(err) {
		var file, err = os.Create(path)
		if isError(err) {
			return
		}
		defer file.Close()
	}
}

func WriteFile(path string, content string) bool {
	// open file using READ & WRITE permission
	var file, err = os.OpenFile(path, os.O_RDWR, 0644)
	if isError(err) {
		return false
	}
	defer file.Close()

	content = strings.Replace(content, "\\n", "\n", -1)
	content = strings.Replace(content, "\\r", "\r", -1)
	content = strings.Replace(content, "\\t", "\t", -1)
	content = strings.Replace(content, "\\b", "\b", -1)
	content = strings.Replace(content, "\\\"", "\"", -1)

	cn := []byte(content)

	// write some text line-by-line to file
	_, err = file.Write(cn)

	if isError(err) {
		return false
	}

	// save changes
	err = file.Sync()

	return isError(err)
}

func ReadFile(path string) string {
	// re-open file
	content, err := os.ReadFile(path)
	if isError(err) {
		return ""
	}

	return string(content)
}

func DeleteFile(path string) bool {
	// delete file
	var err = os.Remove(path)
	return isError(err)
}

func CreateDir(path string) bool {
	var err = os.MkdirAll(path, 644)

	return isError(err)
}

func DeleteDir(path string) bool {
	var err = os.RemoveAll(path)

	return isError(err)
}

func Link(path1 string, path2 string) bool {
	var err = os.Symlink(path1, path2)

	return isError(err)
}

func Env(key string, value string) string {
	result := ""
	if key != "" {
		result = os.Getenv(key)

		if value != "" {
			os.Setenv(key, value)
			result = os.Getenv(key)
		}
	}

	return result
}

func isError(err error) bool {
	if err != nil {
		return false
	}

	return (err != nil)
}
