// FralaParser gets the directory contents of tests/design (if it exists) and parse the files with Frala.
func FralaParser(rootDirectory string, files []string) {
	if len(rootDirectory) != 0 && rootDirectory != "/" { // If the rootDirectory has content but NOT /
		rootDirectory += "/" // Append / to end of rootDirectory

		var fileNamesWithoutDir []string                             // Define filesWithoutDir as an empty []string that'll contain names of files without the directory string
		languagesToRecurse := []string{frala.Config.DefaultLanguage} // Set languagesToRecurse to a string slicen with Default Language by default

		if len(frala.Config.Languages) != 0 { // If pre-defined languages have been set
			languagesToRecurse = frala.Config.Languages // Set languagesToRecurse to the languages defined
		}

		for _, fileName := range files { // For each file in files
			fileNamesWithoutDir = append(fileNamesWithoutDir, filepath.Base(fileName)) // Append only the file name
		}

		for _, language := range languagesToRecurse { // For each language
			frala.Config.DefaultLanguage = language                                   // Change DefaultLanguage in Frala to language
			frala.Config.Direction = frala.GetDirection(frala.Config.DefaultLanguage) // Ensure we have an accurate Direction against the updated DefaultLanguage

			listOfParseResponses := frala.MultiParse(files) // Parse all the HTML files in tests/design, return the ParseResponses

			for fileName, parseResponse := range listOfParseResponses { // For each file and parseResponse in listOfParseResponses
				if len(parseResponse.Content) != 0 && parseResponse.Error == nil { // If there is content returned from the parsing and there is no error
					codeutilsShared.WriteOrUpdateFile(rootDirectory+language+"/"+filepath.Base(fileName), []byte(parseResponse.Content), codeutilsShared.UniversalFileMode) // Update the file in lang specific design dir
				}
			}

			CompressHTML(rootDirectory+language, fileNamesWithoutDir) // Compress all the HTML files provided in this language directory
		}

		for _, fileName := range fileNamesWithoutDir { // For each file in fileNamesWithoutDir
			os.Remove(rootDirectory + fileName) // Remove the file in tests/design now that we have compressed forms in each individual language folder in tests/design
		}
	}
}
// MinifyJavaScript minifies the JavaScript using Google Closure Compiler and then proceed to attempt to provide a zopfli compressed version.
func MinifyJavaScript() error {
	var minifyError error

	if codeutilsShared.ExecutableExists("ccjs") { // If the ccjs executable exists
		fmt.Println("Minifying " + projectConfig.Name + " compiled JavaScript.")

		closureArgs := []string{ // Define closureArgs as
			"build/" + lowercaseProjectName + ".js",    // Yes, I like to compress things.
			"--compilation_level=SIMPLE_OPTIMIZATIONS", // Simple optimizations
			"--warning_level=QUIET",                    // Shush you...
		}

		closureOutput := codeutilsShared.ExecCommand("ccjs", closureArgs, false) // Run Google Closure Compiler and store the output in closureOutput

		codeutilsShared.WriteOrUpdateFile("build/"+lowercaseProjectName+".min.js", []byte(closureOutput), codeutilsShared.UniversalFileMode) // Write or update the minified JS file content to build/lowercaseProjectName.min.js

		if codeutilsShared.ExecutableExists("zopfli") { // If zopfli exists
			codeutilsShared.ExecCommand("zopfli", []string{"build/" + lowercaseProjectName + ".min.js"}, false) // Have zopfli run and gzip the contents
		}
	} else { // If ccjs does not exist
		minifyError = errors.New("ccjs" + executableNotInstalled)
	}

	return minifyError
}
// BootstrapProject assists in the bootstrap of a project.
func BootstrapProject(projectName string) {
	var projectConfig codeutilsShared.ProjectConfig // Define projectConfig as a ProjectConfig struct from codeutilsShared
	projectConfig.Name = projectName                // Set the Name to projectName

	contentTypesString := codeutilsShared.InputMessage("Please input the content types of this project, separated by commas")

	if contentTypesString != "" { // If the cocontentTypesStringntentTypes is not an empty string
		contentTypesString = strings.Replace(contentTypesString, " ", "", -1) // Remove any whitespace from contentTypesString
		contentTypesString = strings.ToLower(contentTypesString)              // Lowercase all content
		contentTypes := strings.Split(contentTypesString, ",")                // Split contentTypesString by , into contentTypes
		sort.Strings(contentTypes)                                            // Sort the contentTypes
		projectConfig.ContentTypes = contentTypes                             // Set ContentTypes to sorted contentTypes

		for _, contentType := range contentTypes { // For each contentType in contentTypes
			fmt.Println("----- Configuring " + contentType + " -----")

			switch contentType {
			case "go":
				projectConfig.Go = GoCompilerOptionsPrompt() // Define Go's GoCompilerOptions as the GoCompilerOptions provided by GoCompilerOptionsPrompt
			case "html": // If this is HTML contenttype
				projectConfig.HTML = HTMLCompilerOptionsPrompt() // Define HTML's HTMLCompilerOptions as the HTMLCompilerOptions provided by HTMLCompilerOptionsPrompt
				projectConfig.UsesTests = true
			case "less": // If this is LESS contenttype
				projectConfig.LESS = LESSCompilerOptionsPrompt() // Define LESS's LESSCompilerOptions as the LESSCompilerOptions
			case "typescript": // If this is TypeScript contenttype
				projectConfig.TypeScript = TypeScriptCompilerOptionsPrompt() // Define TypeScript's TypeScriptCompilerOptions as one provided by TypeScriptCompilerOptionsPrompt
			}

			codeutilsShared.OutputStatus(true, "Configured "+contentType+".")
			fmt.Println("") // Force new-line
		}

		CreateFolderStructure(projectConfig) // Create the folder structure if necessary
		codeutilsShared.OutputStatus(true, "Created folder structure for project.")

		// Save config
		projectConfigBytes, _ := json.MarshalIndent(projectConfig, "", "\t")                                          // Convert projectConfig to projectConfigBytes
		codeutilsShared.WriteOrUpdateFile("build/config.json", projectConfigBytes, codeutilsShared.UniversalFileMode) // Write the config to build/config
		codeutilsShared.OutputStatus(true, "Saved config.")
	} else { // If the contentTypes is an empty string
		fmt.Println("No content types provided for project creation.")
	}
}
// CompressHTML compresses the HTML files provided
func CompressHTML(directory string, files []string) error {
	var compressError error
	htmlMinifierFlags := []string{ // Oh god I am so sorry for all the flags
		"--case-sensitive",                    // Ensure case sensitivity for custom HTML tags
		"--collapse-whitespace",               // Collapse any whitespace
		"--remove-comments",                   // Remove comments
		"--remove-comments-from-cdata",        // Same as above but from CDATA
		"--remove-redundant-attributes",       // Fix your damn HTML, no need for redundancies
		"--remove-script-type-attributes",     // Remove script type attribute
		"--remove-style-link-type-attributes", // Remove style and link tag type attribute
	}

	if codeutilsShared.ExecutableExists("html-minifier") { // If the minifier exists
		if codeutilsShared.IsDir(directory) { // If the directory provided indeed exists
			if len(files) != 0 { // If there was files passed
				replacer := regexp.MustCompile(`"{{?\s?(type=")?\s(\w+)?\s(src="[^>]+)`) // Search for instances of messed up quoting due to html-minifier
				for _, file := range files {                                             // For each file in files
					fmt.Println("Compressing " + file + " in " + directory)

					file = directory + "/" + file                                    // Prepend the directory
					fileSpecificFlags := htmlMinifierFlags                           // Assign fileSpecificFlags flags as initially the same as htmlMinifierFlags
					fileSpecificFlags = append(fileSpecificFlags, []string{file}...) // Specify file to minify

					compressedFileContent := codeutilsShared.ExecCommand("html-minifier", fileSpecificFlags, false) // Run the html-minifier
					fixedContent := replacer.ReplaceAllString(compressedFileContent, `"{{ $1$2" $3 }}"`)            // $1 is type, $2 is the type value, $3 is src+lang

					codeutilsShared.WriteOrUpdateFile(file, []byte(fixedContent), 0755) // Update the file contents
				}
			} else { // If there was no files passed
				fmt.Println("No files provided.")
			}
		} else { // If the directory provided is not, in fact, a directory
			fmt.Println(directory + " is not a directory.")
		}
	} else { // If the minifier does not exist
		compressError = errors.New("html-minifier" + executableNotInstalled)
	}

	return compressError
}
// LESSBranchCompiler compiles individual branches of the Go source tree into selective CSS files
func LESSBranchCompiler(lessBranchOptions codeutilsShared.LESSBranchOptions) {
	lessCompileFlags := []string{
		"--clean-css", // Clean the CSS (such as minification, comment removal, so forth)
		"--no-color",
		"--no-ie-compat", // Disable IE Compatibility
		"--no-js",
		"--strict-math=on", // Do not process math
	}

	if lessBranchOptions.UseGlob { // If  we should use globbing
		lessCompileFlags = append(lessCompileFlags, []string{"--glob"}...) // Provide --glob
	}

	if len(lessBranchOptions.AdditionalCompileOptions) != 0 { // If there was Additional Compile Options to pass
		lessCompileFlags = append(lessCompileFlags, lessBranchOptions.AdditionalCompileOptions...)
	}

	writeFileName, _ := codeutilsShared.FindClosestFile("src/less/" + lowercaseProjectName + ".less") // Define writeFileName as the file name we'll be compiling

	lessCompileFlags = append(lessCompileFlags, []string{writeFileName}...)
	commandOutput := codeutilsShared.ExecCommand("lessc", lessCompileFlags, false) // Run the less compiler

	errorIndex := strings.Index(commandOutput, "Error")
	if (errorIndex > 12) || (errorIndex == -1) { // If there was no initial error (any error string beyond a certain point should be assumed to be a part of the program)
		uniqueFileName := strings.Replace(filepath.Base(writeFileName), ".less", "", -1)
		if lessBranchOptions.UniqueHash { // If we should be using a unique hash
			uniqueFileName += "-" + codeutilsShared.Sha512Sum(commandOutput, 1)[0:12] // Append first 12 characters of hash
		}

		uniqueFileName += ".css"                                                                                             // Append .css
		codeutilsShared.WriteOrUpdateFile("build/"+uniqueFileName, []byte(commandOutput), codeutilsShared.UniversalFileMode) // Write to build folder
		codeutilsShared.CopyFile("build/"+uniqueFileName, "tests/design/css/"+uniqueFileName)                                // Copy from build folder to css folder
	} else {
		fmt.Println(commandOutput)
	}
}