// This file is part of Arduino Lint.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License, either
// version 3 of the License, or (at your option) any later version.
// This license covers the main part of Arduino Lint.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package projectdata

import (
	"io"
	"net/http"
	"os"

	"github.com/arduino/arduino-cli/arduino/libraries"
	"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
	"github.com/arduino/arduino-lint/internal/configuration"
	"github.com/arduino/arduino-lint/internal/configuration/rulemode"
	"github.com/arduino/arduino-lint/internal/project"
	"github.com/arduino/arduino-lint/internal/project/library/libraryproperties"
	"github.com/arduino/arduino-lint/internal/result/feedback"
	"github.com/arduino/arduino-lint/internal/rule/schema"
	"github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel"
	"github.com/arduino/go-paths-helper"
	"github.com/arduino/go-properties-orderedmap"
	"github.com/client9/misspell"
	"github.com/sirupsen/logrus"
)

// InitializeForLibrary gathers the library rule data for the specified project.
func InitializeForLibrary(project project.Type) {
	var err error

	libraryProperties, libraryPropertiesLoadError = libraryproperties.Properties(project.Path)
	if libraryPropertiesLoadError != nil {
		logrus.Errorf("Error loading library.properties from %s: %s", project.Path, libraryPropertiesLoadError)
		libraryPropertiesSchemaValidationResult = nil
	} else {
		libraryPropertiesSchemaValidationResult = libraryproperties.Validate(libraryProperties)
	}

	loadedLibrary, err = libraries.Load(project.Path, libraries.User)
	if err != nil {
		logrus.Errorf("Error loading library from %s: %s", project.Path, err)
		loadedLibrary = nil
		sourceHeaders = nil
	} else {
		sourceHeaders, err = loadedLibrary.SourceHeaders()
		if err != nil {
			panic(err)
		}
	}

	// Download the Library Manager index if needed.
	if !configuration.RuleModes(project.SuperprojectType)[rulemode.LibraryManagerIndexing] && libraryManagerIndex == nil {
		// Set up the temporary folder for the index
		libraryIndexFolderPath, err := paths.TempDir().MkTempDir("arduino-lint-library-index-folder")
		defer libraryIndexFolderPath.RemoveAll()
		if err != nil {
			panic(err)
		}
		libraryIndexPath := libraryIndexFolderPath.Join("library_index.json")

		// Download the index data
		httpResponse, err := http.Get(librariesmanager.LibraryIndexURL.String())
		if err != nil {
			feedback.Errorf("Unable to download Library Manager index from %s: %s", err, librariesmanager.LibraryIndexURL)
			os.Exit(1)
		}
		defer httpResponse.Body.Close()

		// Write the index data to file
		libraryIndexFile, err := libraryIndexPath.Create()
		if err != nil {
			panic(err)
		}
		defer libraryIndexFile.Close()
		if _, err := io.Copy(libraryIndexFile, httpResponse.Body); err != nil {
			panic(err)
		}

		libraryManagerIndex = librariesmanager.NewLibraryManager(libraryIndexFolderPath, nil)
		libraryManagerIndex.LoadIndex()
	}

	if misspelledWordsReplacer == nil { // The replacer only needs to be compiled once per run.
		misspelledWordsReplacer = misspell.New()
		misspelledWordsReplacer.Compile()
	}
}

var libraryPropertiesLoadError error

// LibraryPropertiesLoadError returns the error output from loading the library.properties metadata file.
func LibraryPropertiesLoadError() error {
	return libraryPropertiesLoadError
}

var libraryProperties *properties.Map

// LibraryProperties returns the data from the library.properties metadata file.
func LibraryProperties() *properties.Map {
	return libraryProperties
}

var libraryPropertiesSchemaValidationResult map[compliancelevel.Type]schema.ValidationResult

// LibraryPropertiesSchemaValidationResult returns the result of validating library.properties against the JSON schema.
func LibraryPropertiesSchemaValidationResult() map[compliancelevel.Type]schema.ValidationResult {
	return libraryPropertiesSchemaValidationResult
}

var loadedLibrary *libraries.Library

// LoadedLibrary returns the library object generated by Arduino CLI.
func LoadedLibrary() *libraries.Library {
	return loadedLibrary
}

var sourceHeaders []string

// SourceHeaders returns the list of library source header filenames discovered by Arduino CLI.
func SourceHeaders() []string {
	return sourceHeaders
}

var libraryManagerIndex *librariesmanager.LibrariesManager

// LibraryManagerIndex returns the Library Manager index data.
func LibraryManagerIndex() *librariesmanager.LibrariesManager {
	return libraryManagerIndex
}

var misspelledWordsReplacer *misspell.Replacer

// MisspelledWordsReplacer returns the misspelled words replacer used for spell check.
func MisspelledWordsReplacer() *misspell.Replacer {
	return misspelledWordsReplacer
}
