package cmd

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"html"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"github.com/SAP/jenkins-library/pkg/command"
	piperhttp "github.com/SAP/jenkins-library/pkg/http"
	"github.com/SAP/jenkins-library/pkg/log"
	"github.com/SAP/jenkins-library/pkg/telemetry"
	"github.com/pkg/errors"
)

var atcFailure, aUnitFailure bool

func gctsExecuteABAPQualityChecks(config gctsExecuteABAPQualityChecksOptions, telemetryData *telemetry.CustomData) {

	// for command execution use Command
	c := command.Command{}
	// reroute command output to logging framework
	c.Stdout(log.Writer())
	c.Stderr(log.Writer())

	httpClient := &piperhttp.Client{}
	// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
	err := rungctsExecuteABAPQualityChecks(&config, httpClient)
	if err != nil {
		log.Entry().WithError(err).Fatal("step execution failed")
	}

	if aUnitFailure || atcFailure {

		log.Entry().Fatal("step execution failed")

	}

}

func rungctsExecuteABAPQualityChecks(config *gctsExecuteABAPQualityChecksOptions, httpClient piperhttp.Sender) error {

	const localChangedObjects = "localchangedobjects"
	const remoteChangedObjects = "remotechangedobjects"
	const localChangedPackages = "localchangedpackages"
	const remoteChangedPackages = "remotechangedpackages"
	const repository = "repository"
	const packages = "packages"

	cookieJar, cookieErr := cookiejar.New(nil)
	if cookieErr != nil {
		return errors.Wrap(cookieErr, "creating a cookie jar failed")
	}

	maxRetries := -1
	clientOptions := piperhttp.ClientOptions{
		CookieJar:  cookieJar,
		Username:   config.Username,
		Password:   config.Password,
		MaxRetries: maxRetries,
	}

	httpClient.SetOptions(clientOptions)

	log.Entry().Infof("start of gctsExecuteABAPQualityChecks step with configuration values: %v", config)

	var objects []repoObject
	var err error

	log.Entry().Info("scope:", config.Scope)

	switch strings.ToLower(config.Scope) {
	case localChangedObjects:
		objects, err = getLocalObjects(config, httpClient)
	case remoteChangedObjects:
		objects, err = getRemoteObjects(config, httpClient)
	case localChangedPackages:
		objects, err = getLocalPackages(config, httpClient)
	case remoteChangedPackages:
		objects, err = getRemotePackages(config, httpClient)
	case repository:
		objects, err = getRepositoryObjects(config, httpClient)
	case packages:
		objects, err = getPackages(config, httpClient)
	default:
		log.Entry().Info("the specified scope does not exists, the default one will be used:" + repository)
		objects, err = getRepositoryObjects(config, httpClient)
	}

	if err != nil {
		log.Entry().WithError(err).Fatal("failure in get objects")
	}

	if objects == nil {
		log.Entry().Warning("no object delta was found, therefore the step execution will stop")
		return nil

	}

	log.Entry().Infof("objects to be checked:")
	for _, object := range objects {
		log.Entry().Info(object.Type, " ", object.Object)
	}

	if config.AUnitTest {

		// wrapper for execution of AUnit Test
		err := executeAUnitTest(config, httpClient, objects)

		if err != nil {
			log.Entry().WithError(err)

		}

		if aUnitFailure {

			log.Entry().Error("unit test(s) has/have failed! Check " + config.AUnitResultsFileName + " for more information! If you have enabled Warnings-Next-Generation Plugin, you can see the issues there!")

		} else {

			log.Entry().Info("AUnit test run completed successfully. If there are any results from the run, the results are saved in " + config.AUnitResultsFileName)

		}
	}

	if config.AtcCheck {

		// wrapper for execution of ATCChecks
		err = executeATCCheck(config, httpClient, objects)

		if err != nil {
			log.Entry().WithError(err).Fatal("execute ATC Check failed")
		}

		if atcFailure {

			log.Entry().Error(" ATC issue(s) found! Check " + config.AtcResultsFileName + " for more information! If you have enabled Warnings-Next-Generation Plugin, you can see the issues there!")

		} else {

			log.Entry().Info("ATCCheck test run completed successfully. If there are any results from the run, the results are saved in " + config.AtcResultsFileName)

		}

	}

	return nil

}

func getLocalObjects(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {

	var localObjects []repoObject
	var localObject repoObject

	log.Entry().Info("get local changed objects started")

	if config.Commit == "" {

		return []repoObject{}, errors.Errorf("For scope: localChangedObjects you need to specify a commit")

	}

	history, err := getHistory(config, client)
	if err != nil {
		return []repoObject{}, errors.Wrap(err, "get local changed objects failed")
	}

	if len(history.Result) == 0 {

		return []repoObject{}, errors.Wrap(err, "no activities (from commit - to commit) were found")
	}

	fromCommit := history.Result[0].FromCommit
	log.Entry().Info("from Commit: ", fromCommit)
	toCommit := history.Result[0].ToCommit
	log.Entry().Info("to Commit: ", toCommit)

	// object delta between FromCommit and ToCommit retrieved from Activities Tab in gCTS
	resp, err := getObjectDifference(config, fromCommit, toCommit, client)
	if err != nil {
		return []repoObject{}, errors.Wrap(err, "get local changed objects failed")
	}

	for _, object := range resp.Objects {
		localObject.Object = object.Name
		localObject.Type = object.Type
		localObjects = append(localObjects, localObject)
	}

	log.Entry().Info("get local changed objects finished")

	return localObjects, nil
}

func getRemoteObjects(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {

	var remoteObjects []repoObject
	var remoteObject repoObject
	var currentRemoteCommit string

	log.Entry().Info("get remote changed objects started")

	if config.Commit == "" {

		return []repoObject{}, errors.Errorf("For scope: remoteChangedObjects you need to specify a commit")

	}

	commitList, err := getCommitList(config, client)

	if err != nil {
		return []repoObject{}, errors.Wrap(err, "get remote changed objects failed")
	}

	for i, commit := range commitList.Commits {
		if commit.ID == config.Commit {
			currentRemoteCommit = commitList.Commits[i+1].ID
			break
		}
	}
	if currentRemoteCommit == "" {
		return []repoObject{}, errors.New("current remote commit was not found")

	}
	log.Entry().Info("current commit in the remote repository: ", currentRemoteCommit)
	// object delta between the commit that triggered the pipeline and the current commit in the remote repository
	resp, err := getObjectDifference(config, currentRemoteCommit, config.Commit, client)

	if err != nil {
		return []repoObject{}, errors.Wrap(err, "get remote changed objects failed")
	}

	for _, object := range resp.Objects {
		remoteObject.Object = object.Name
		remoteObject.Type = object.Type
		remoteObjects = append(remoteObjects, remoteObject)
	}

	log.Entry().Info("get remote changed objects finished")

	return remoteObjects, nil
}

func getLocalPackages(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {

	var localPackages []repoObject
	var localPackage repoObject

	log.Entry().Info("get local changed packages started")

	if config.Commit == "" {

		return []repoObject{}, errors.Errorf("For scope: localChangedPackages you need to specify a commit")

	}

	history, err := getHistory(config, client)
	if err != nil {
		return []repoObject{}, errors.Wrap(err, "get local changed objects failed")
	}

	if len(history.Result) == 0 {

		return []repoObject{}, errors.Wrap(err, "no activities (from commit - to commit) were found")
	}

	fromCommit := history.Result[0].FromCommit
	log.Entry().Info("from Commit: ", fromCommit)
	toCommit := history.Result[0].ToCommit
	log.Entry().Info("to Commit: ", toCommit)

	// object delta between FromCommit and ToCommit retrieved from Activities Tab in gCTS
	resp, err := getObjectDifference(config, fromCommit, config.Commit, client)

	if err != nil {
		return []repoObject{}, errors.Wrap(err, "get local changed packages failed")

	}

	myPackages := map[string]bool{}

	// objects are resolved into packages(DEVC)
	for _, object := range resp.Objects {
		objInfo, err := getObjectInfo(config, client, object.Name, object.Type)
		if err != nil {
			return []repoObject{}, errors.Wrap(err, "get local changed packages failed")
		}
		if myPackages[objInfo.Devclass] {

		} else {
			myPackages[objInfo.Devclass] = true
			localPackage.Object = objInfo.Devclass
			localPackage.Type = "DEVC"
			localPackages = append(localPackages, localPackage)
		}

	}

	log.Entry().Info("get local changed packages finished")
	return localPackages, nil
}

func getRemotePackages(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {

	var remotePackages []repoObject
	var remotePackage repoObject
	var currentRemoteCommit string

	log.Entry().Info("get remote changed packages started")

	if config.Commit == "" {

		return []repoObject{}, errors.Errorf("For scope: remoteChangedPackages you need to specify a commit")

	}

	commitList, err := getCommitList(config, client)

	if err != nil {
		return []repoObject{}, errors.Wrap(err, "get remote changed packages failed")
	}

	for i, commit := range commitList.Commits {
		if commit.ID == config.Commit {
			currentRemoteCommit = commitList.Commits[i+1].ID
			break
		}
	}

	if currentRemoteCommit == "" {
		return []repoObject{}, errors.Wrap(err, "current remote commit was not found")

	}
	log.Entry().Info("current commit in the remote repository: ", currentRemoteCommit)
	//object delta between the commit that triggered the pipeline and the current commit in the remote repository
	resp, err := getObjectDifference(config, currentRemoteCommit, config.Commit, client)
	if err != nil {
		return []repoObject{}, errors.Wrap(err, "get remote changed packages failed")
	}

	myPackages := map[string]bool{}
	// objects are resolved into packages(DEVC)
	for _, object := range resp.Objects {
		objInfo, err := getObjectInfo(config, client, object.Name, object.Type)
		if err != nil {
			return []repoObject{}, errors.Wrap(err, "get remote changed packages failed")
		}
		if myPackages[objInfo.Devclass] {

		} else {
			myPackages[objInfo.Devclass] = true
			remotePackage.Object = objInfo.Devclass
			remotePackage.Type = "DEVC"
			remotePackages = append(remotePackages, remotePackage)
		}

	}
	log.Entry().Info("get remote changed packages finished")
	return remotePackages, nil
}

func getRepositoryObjects(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {

	log.Entry().Info("get repository objects started")

	var repoResp repoObjectResponse

	url := config.Host +
		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
		"/objects?sap-client=" + config.Client

	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)

	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()

	if httpErr != nil {
		return []repoObject{}, errors.Wrap(httpErr, "could not get repository objects")
	} else if resp == nil {
		return []repoObject{}, errors.New("could not get repository objects: did not retrieve a HTTP response")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &repoResp)
	if parsingErr != nil {
		return []repoObject{}, errors.Errorf("%v", parsingErr)
	}

	var repositoryObjects []repoObject

	// remove object type DEVC, because it is already included in scope packages
	// also if you run ATC Checks for DEVC together with other object types, ATC checks will run only for DEVC
	for _, object := range repoResp.Objects {

		if object.Type != "DEVC" {
			repositoryObjects = append(repositoryObjects, object)
		}

	}

	log.Entry().Info("get repository objects finished")

	// all objects that are part of the local repository
	return repositoryObjects, nil
}

func getPackages(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {

	var packages []repoObject

	log.Entry().Info("get packages started")

	var repoResp repoObjectResponse

	url := config.Host +
		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
		"/objects?sap-client=" + config.Client

	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)

	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()

	if httpErr != nil {
		return []repoObject{}, errors.Wrap(httpErr, "get packages failed: could not get repository objects")
	} else if resp == nil {
		return []repoObject{}, errors.New("get packages failed: could not get repository objects: did not retrieve a HTTP response")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &repoResp)
	if parsingErr != nil {
		return []repoObject{}, errors.Errorf("%v", parsingErr)
	}
	// chose only DEVC from repository objects
	for _, object := range repoResp.Objects {

		if object.Type == "DEVC" {
			packages = append(packages, object)
		}

	}

	log.Entry().Info("get packages finished")
	return packages, nil
}

func discoverServer(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (*http.Header, error) {

	url := config.Host +
		"/sap/bc/adt/core/discovery?sap-client=" + config.Client

	header := make(http.Header)
	header.Add("Accept", "application/atomsvc+xml")
	header.Add("x-csrf-token", "fetch")
	header.Add("saml2", "disabled")

	disc, httpErr := client.SendRequest("GET", url, nil, header, nil)

	defer func() {
		if disc != nil && disc.Body != nil {
			disc.Body.Close()
		}
	}()

	if httpErr != nil {
		return nil, errors.Wrap(httpErr, "discovery of the ABAP server failed")
	} else if disc == nil || disc.Header == nil {
		return nil, errors.New("discovery of the ABAP server failed: did not retrieve a HTTP response")
	}

	return &disc.Header, nil
}

func executeAUnitTest(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, objects []repoObject) error {

	log.Entry().Info("execute ABAP Unit Test started")

	var innerXml string
	var result runResult

	for _, object := range objects {

		switch object.Type {
		case "CLAS":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/oo/classes/` + object.Object + `"/>`
		case "DEVC":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/packages/` + object.Object + `"/>`

		}

	}

	var xmlBody = []byte(`<?xml version="1.0" encoding="UTF-8"?>
		<aunit:runConfiguration xmlns:aunit="http://www.sap.com/adt/aunit">
			<external>
				<coverage active="false"/>
			</external>
			<options>
				<uriType value="semantic"/>
				<testDeterminationStrategy appendAssignedTestsPreview="true" assignedTests="false" sameProgram="true"/>
				<testRiskLevels critical="true" dangerous="true" harmless="true"/>
				<testDurations long="true" medium="true" short="true"/>
				<withNavigationUri enabled="false"/>
			</options>
			<adtcore:objectSets xmlns:adtcore="http://www.sap.com/adt/core">
			<objectSet kind="inclusive">
		<adtcore:objectReferences>` +
		innerXml +
		`</adtcore:objectReferences>
			</objectSet>
			</adtcore:objectSets>
		</aunit:runConfiguration>`)

	resp, err := runAUnitTest(config, client, xmlBody)
	if err != nil {
		return errors.Wrap(err, "execute of Aunit test has failed")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyXML(resp, &result)
	if parsingErr != nil {
		log.Entry().Warning(parsingErr)
		return nil
	}

	parsedRes, err := parseUnitResult(config, client, &result)

	if err != nil {
		log.Entry().Warning(err)
		return nil
	}

	log.Entry().Info("execute ABAP Unit Test finished.", parsedRes.Text)

	return nil
}

func runAUnitTest(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, xml []byte) (response *http.Response, err error) {

	log.Entry().Info("run ABAP Unit Test started")
	url := config.Host +
		"/sap/bc/adt/abapunit/testruns?sap-client=" + config.Client

	discHeader, discError := discoverServer(config, client)

	if discError != nil {
		return response, errors.Wrap(discError, "run of unit tests failed")
	}

	if discHeader.Get("X-Csrf-Token") == "" {

		return response, errors.Errorf("could not retrieve x-csrf-token from server")
	}

	header := make(http.Header)
	header.Add("x-csrf-token", discHeader.Get("X-Csrf-Token"))
	header.Add("Accept", "application/xml")
	header.Add("Content-Type", "application/vnd.sap.adt.abapunit.testruns.result.v1+xml")

	response, httpErr := client.SendRequest("POST", url, bytes.NewBuffer(xml), header, nil)

	if httpErr != nil {
		return response, errors.Wrap(httpErr, "run of unit tests failed")
	} else if response == nil {
		return response, errors.New("run of unit tests failed: did not retrieve a HTTP response")
	}

	log.Entry().Info("run ABAP Unit Test finished")
	return response, nil
}

func parseUnitResult(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, aUnitRunResult *runResult) (parsedResult checkstyle, err error) {

	log.Entry().Info("parse ABAP Unit Result started")

	var fileName string
	var aUnitFile file
	var aUnitError checkstyleError

	parsedResult.Version = "1.0"

	for _, program := range aUnitRunResult.Program {

		objectType := program.Type[0:4]
		objectName := program.Name

		//syntax error in unit test or class
		if program.Alerts.Alert.HasSyntaxErrors == "true" {

			aUnitFailure = true
			aUnitError.Source = objectName
			aUnitError.Severity = "error"
			log.Entry().Info("severity: ", aUnitError.Severity)
			aUnitError.Message = html.UnescapeString(program.Alerts.Alert.Title + " " + program.Alerts.Alert.Details.Detail.AttrText)
			log.Entry().Info("message: ", aUnitError.Message)
			aUnitError.Line, err = findLine(config, client, program.Alerts.Alert.Stack.StackEntry.URI, objectName, objectType)
			log.Entry().Error("line: ", aUnitError.Line)
			if err != nil {
				return parsedResult, errors.Wrap(err, "parse AUnit Result failed")

			}
			fileName, err = getFileName(config, client, program.Alerts.Alert.Stack.StackEntry.URI, objectName)
			log.Entry().Error("file path: ", aUnitError.Line)
			if err != nil {
				return parsedResult, errors.Wrap(err, "parse AUnit Result failed")

			}

			aUnitFile.Error = append(aUnitFile.Error, aUnitError)
			aUnitError = checkstyleError{}
			log.Entry().Error("there is a syntax error", aUnitFile)
		}

		for _, testClass := range program.TestClasses.TestClass {

			for _, testMethod := range testClass.TestMethods.TestMethod {

				aUnitError.Source = testClass.Name + "/" + testMethod.Name

				// unit test failure
				if len(testMethod.Alerts.Alert) > 0 {

					for _, testalert := range testMethod.Alerts.Alert {

						switch testalert.Severity {
						case "fatal":
							log.Entry().Error("unit test " + aUnitError.Source + " has failed with severity fatal")
							aUnitFailure = true
							aUnitError.Severity = "error"
						case "critical":
							log.Entry().Error("unit test " + aUnitError.Source + " has failed with severity critical")
							aUnitFailure = true
							aUnitError.Severity = "error"
						case "tolerable":
							log.Entry().Warning("unit test " + aUnitError.Source + "  has failed with severity warning")
							aUnitError.Severity = "warning"
						default:
							aUnitError.Severity = "info"

						}

						//unit test message is spread in different elements
						for _, detail := range testalert.Details.Detail {
							aUnitError.Message = aUnitError.Message + " " + detail.AttrText
							for _, subdetail := range detail.Details.Detail {

								aUnitError.Message = html.UnescapeString(aUnitError.Message + " " + subdetail.AttrText)
								log.Entry().Info("message: ", aUnitError.Message)
							}

						}

						aUnitError.Line, err = findLine(config, client, testalert.Stack.StackEntry.URI, objectName, objectType)
						log.Entry().Info("line: ", aUnitError.Line)
						if err != nil {

							log.Entry().Warning(err)

						}

					}

					aUnitFile.Error = append(aUnitFile.Error, aUnitError)
					aUnitError = checkstyleError{}

				} else {

					log.Entry().Info("unit test:", aUnitError.Source, "- was successful")

				}

			}

			fileName, err = getFileName(config, client, testClass.URI, objectName)
			if err != nil {
				return parsedResult, errors.Wrap(err, "parse AUnit Result failed")

			}
		}

		aUnitFile.Name, err = constructPath(config, client, fileName, objectName, objectType)
		log.Entry().Error("file path: ", aUnitFile.Name)
		if err != nil {

			return parsedResult, errors.Wrap(err, "parse AUnit Result failed")
		}
		parsedResult.File = append(parsedResult.File, aUnitFile)
		aUnitFile = file{}

	}

	body, _ := xml.Marshal(parsedResult)

	writeErr := ioutil.WriteFile(config.AUnitResultsFileName, body, 0644)

	if writeErr != nil {
		log.Entry().Error("file AUnitResults.xml could not be created")
		return parsedResult, fmt.Errorf("handling unit test results failed: %w", writeErr)
	}

	log.Entry().Info("parse ABAP Unit Result finished")
	return parsedResult, nil

}

func executeATCCheck(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, objects []repoObject) (error error) {

	log.Entry().Info("execute ATC Check started")

	var innerXml string
	var result worklist

	for _, object := range objects {

		switch object.Type {

		case "CLAS":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/oo/classes/` + object.Object + `"/>`
		case "INTF":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/oo/interfaces/` + object.Object + `"/>`
		case "DEVC":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/packages/` + object.Object + `"/>`
		case "FUGR":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/functions/groups/` + object.Object + `/source/main"/>`
		case "TABL":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/ddic/tables/` + object.Object + `/source/main"/>`
		case "DTEL":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/ddic/dataelements/` + object.Object + `"/>`
		case "DOMA":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/ddic/domains/` + object.Object + `"/>`
		case "MSAG":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/messageclass/` + object.Object + `"/>`
		case "PROG":
			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/programs/programs/` + object.Object + `/source/main"/>`
		default:
			log.Entry().Warning("object Type " + object.Type + " is not supported!")

		}

	}

	var xmlBody = []byte(`<?xml version="1.0" encoding="UTF-8"?>
	<atc:run xmlns:atc="http://www.sap.com/adt/atc"
	maximumVerdicts="100">
			<objectSets xmlns:adtcore="http://www.sap.com/adt/core">
			<objectSet kind="inclusive">
 		<adtcore:objectReferences>` + innerXml +
		`</adtcore:objectReferences>
			</objectSet>
			</objectSets>
				</atc:run>`)

	worklist, err := getWorklist(config, client)
	if err != nil {
		return errors.Wrap(err, "execution of ATC Checks failed")
	}

	err = startATCRun(config, client, xmlBody, worklist)

	if err != nil {
		return errors.Wrap(err, "execution of ATC Checks failed")
	}

	resp, err := getATCRun(config, client, worklist)

	if err != nil {
		return errors.Wrap(err, "execution of ATC Checks failed")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyXML(resp, &result)
	if parsingErr != nil {
		log.Entry().Warning(parsingErr)
		return nil
	}

	atcRes, err := parseATCCheckResult(config, client, &result)

	if err != nil {
		log.Entry().Error(err)
		return errors.Wrap(err, "execution of ATC Checks failed")
	}

	log.Entry().Info("execute ATC Checks finished.", atcRes.Text)

	return nil

}
func startATCRun(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, xml []byte, worklistID string) (err error) {

	log.Entry().Info("ATC Run started")

	discHeader, discError := discoverServer(config, client)
	if discError != nil {
		return errors.Wrap(discError, "start of ATC run failed")
	}

	if discHeader.Get("X-Csrf-Token") == "" {
		return errors.Errorf("could not retrieve x-csrf-token from server")
	}

	header := make(http.Header)
	header.Add("x-csrf-token", discHeader.Get("X-Csrf-Token"))
	header.Add("Accept", "application/xml")

	url := config.Host +
		"/sap/bc/adt/atc/runs?worklistId=" + worklistID + "&sap-client=" + config.Client

	resp, httpErr := client.SendRequest("POST", url, bytes.NewBuffer(xml), header, nil)

	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()

	if httpErr != nil {
		return errors.Wrap(httpErr, "start of ATC run failed")
	} else if resp == nil {
		return errors.New("start of ATC run failed: did not retrieve a HTTP response")
	}

	log.Entry().Info("ATC Run finished")

	return nil

}

func getATCRun(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, worklistID string) (response *http.Response, err error) {

	log.Entry().Info("get ATC Run Results started")

	header := make(http.Header)

	url := config.Host +
		"/sap/bc/adt/atc/worklists/" + worklistID + "?sap-client=" + config.Client

	header.Add("Accept", "application/atc.worklist.v1+xml")

	resp, httpErr := client.SendRequest("GET", url, nil, header, nil)

	if httpErr != nil {
		return response, errors.Wrap(httpErr, "get ATC run failed")
	} else if resp == nil {
		return response, errors.New("get ATC run failed: did not retrieve a HTTP response")
	}
	log.Entry().Info("get ATC Run Results finished")
	return resp, nil

}

func getWorklist(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (worklistID string, error error) {

	url := config.Host +
		"/sap/bc/adt/atc/worklists?checkVariant=" + config.AtcVariant + "&sap-client=" + config.Client
	discHeader, discError := discoverServer(config, client)

	if discError != nil {
		return worklistID, errors.Wrap(discError, "get worklist failed")
	}

	if discHeader.Get("X-Csrf-Token") == "" {
		return worklistID, errors.Errorf("could not retrieve x-csrf-token from server")
	}

	header := make(http.Header)
	header.Add("x-csrf-token", discHeader.Get("X-Csrf-Token"))
	header.Add("Accept", "*/*")

	resp, httpErr := client.SendRequest("POST", url, nil, header, nil)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()

	if httpErr != nil {
		return worklistID, errors.Wrap(httpErr, "get worklist failed")
	} else if resp == nil {
		return worklistID, errors.New("get worklist failed: did not retrieve a HTTP response")
	}
	location := resp.Header["Location"][0]
	locationSlice := strings.Split(location, "/")
	worklistID = locationSlice[len(locationSlice)-1]
	log.Entry().Info("worklist id for ATC check: ", worklistID)

	return worklistID, nil
}

func parseATCCheckResult(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, response *worklist) (atcResults checkstyle, error error) {

	log.Entry().Info("parse ATC Check Result started")

	var atcFile file
	var subObject string
	var aTCUnitError checkstyleError

	atcResults.Version = "1.0"

	for _, object := range response.Objects.Object {

		objectType := object.Type
		objectName := object.Name

		for _, atcworklist := range object.Findings.Finding {

			log.Entry().Info("there is atc finding for object type: ", objectType+" object name: "+objectName)

			path, err := url.PathUnescape(atcworklist.Location)

			if err != nil {
				return atcResults, errors.Wrap(err, "conversion of ATC check results to CheckStyle has failed")

			}

			if len(atcworklist.Atcfinding) > 0 {

				priority, err := strconv.Atoi(atcworklist.Priority)

				if err != nil {
					return atcResults, errors.Wrap(err, "conversion of ATC check results to CheckStyle has failed")

				}

				switch priority {
				case 1:
					atcFailure = true
					aTCUnitError.Severity = "error"
					log.Entry().Error("atc issue with priority: 1 ")
				case 2:
					atcFailure = true
					aTCUnitError.Severity = "error"
					log.Entry().Error("atc issue with priority: 2 ")
				case 3:
					aTCUnitError.Severity = "warning"
					log.Entry().Warning("atc issue with priority: 3 ")
				default:
					aTCUnitError.Severity = "info"
					log.Entry().Info("atc issue with low priority ")
				}

				log.Entry().Error("severity: ", aTCUnitError.Severity)

				if aTCUnitError.Line == "" {

					aTCUnitError.Line, err = findLine(config, client, path, objectName, objectType)
					log.Entry().Info("line: ", aTCUnitError.Line)

					if err != nil {
						log.Entry().Info(path)
						log.Entry().Warning(err)

					}

				}

				if subObject != "" {
					aTCUnitError.Source = objectName + "/" + strings.ToUpper(subObject)
				} else {
					aTCUnitError.Source = objectName
				}

				aTCUnitError.Message = html.UnescapeString(atcworklist.CheckTitle + " " + atcworklist.MessageTitle)
				log.Entry().Info("message: ", aTCUnitError.Message)
				atcFile.Error = append(atcFile.Error, aTCUnitError)
				aTCUnitError = checkstyleError{}
			}

			if atcFile.Error[0].Message != "" {

				fileName, err := getFileName(config, client, path, objectName)

				if err != nil {
					return atcResults, errors.Wrap(err, "conversion of ATC check results to CheckStyle has failed")
				}

				atcFile.Name, err = constructPath(config, client, fileName, objectName, objectType)
				log.Entry().Info("file path: ", atcFile.Name)
				if err != nil {
					return atcResults, errors.Wrap(err, "conversion of ATC check results to CheckStyle has failed")
				}
				atcResults.File = append(atcResults.File, atcFile)
				atcFile = file{}

			}

		}
	}

	atcBody, _ := xml.Marshal(atcResults)

	writeErr := ioutil.WriteFile(config.AtcResultsFileName, atcBody, 0644)

	if writeErr != nil {
		log.Entry().Error("ATCResults.xml could not be created")
		return atcResults, fmt.Errorf("handling atc results failed: %w", writeErr)
	}
	log.Entry().Info("parsing ATC check results to CheckStyle has finished.")
	return atcResults, writeErr
}

func constructPath(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, fileName string, objectName string, objectType string) (filePath string, error error) {

	targetDir, err := getTargetDir(config, client)
	if err != nil {
		return filePath, errors.Wrap(err, "path could not be constructed")

	}

	filePath = config.Workspace + "/" + targetDir + "/objects/" + strings.ToUpper(objectType) + "/" + strings.ToUpper(objectName) + "/" + fileName
	return filePath, nil

}

func findLine(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, path string, objectName string, objectType string) (line string, error error) {

	regexLine := regexp.MustCompile(`.start=\d*`)
	regexMethod := regexp.MustCompile(`.name=[a-zA-Z0-9_-]*;`)

	readableSource, err := checkReadableSource(config, client)

	if err != nil {

		return line, errors.Wrap(err, "could not find line in source code")

	}

	fileName, err := getFileName(config, client, path, objectName)

	if err != nil {

		return line, err

	}

	filePath, err := constructPath(config, client, fileName, objectName, objectType)
	if err != nil {
		return line, errors.Wrap(err, objectType+"/"+objectName+"could not find line in source code")

	}

	var absLine int
	if readableSource {

		// the error line that we get from UnitTest Run or ATC Check is not aligned for the readable source, we need to calculated it
		rawfile, err := ioutil.ReadFile(filePath)

		if err != nil {

			return line, errors.Wrapf(err, "could not find object in the workspace of your CI/CD tool ")
		}

		file := string(rawfile)

		splittedfile := strings.Split(file, "\n")

		// CLAS/OSO - is unique identifier for protection section in CLAS
		if strings.Contains(path, "CLAS/OSO") {

			for l, line := range splittedfile {

				if strings.Contains(line, "protected section.") {
					absLine = l
					break
				}

			}

			// CLAS/OM - is unique identifier for method section in CLAS
		} else if strings.Contains(path, "CLAS/OM") {

			methodName := regexMethod.FindString(path)

			if methodName != "" {
				methodName = methodName[len(`.name=`) : len(methodName)-1]

			}

			for line, linecontent := range splittedfile {

				if strings.Contains(linecontent, "method"+" "+methodName) {
					absLine = line
					break
				}

			}

			// CLAS/OSI - is unique identifier for private section in CLAS
		} else if strings.Contains(path, "CLAS/OSI") {

			for line, linecontent := range splittedfile {

				if strings.Contains(linecontent, "private section.") {
					absLine = line
					break
				}

			}

		}

		errLine := regexLine.FindString(path)

		if errLine != "" {

			errLine, err := strconv.Atoi(errLine[len(`.start=`):])
			if err == nil {
				line = strconv.Itoa(absLine + errLine)

			}

		}

	} else {
		// classic format
		errLine := regexLine.FindString(path)
		if errLine != "" {
			line = errLine[len(`.start=`):]

		}

	}

	return line, nil
}
func getFileName(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, path string, objName string) (fileName string, error error) {

	readableSource, err := checkReadableSource(config, client)
	if err != nil {
		return fileName, errors.Wrap(err, "get file name has failed")

	}

	path, err = url.PathUnescape(path)

	var fileExtension string
	fileExtensionLength := 30 - len(objName)
	for i := 0; i < fileExtensionLength; i++ {
		fileExtension += "="
	}

	if err != nil {
		return fileName, errors.Wrap(err, "get file name has failed")

	}

	//  INTERFACES
	regexInterface := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/interfaces\/\w*`)
	intf := regexInterface.FindString(path)
	if intf != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".intf.abap"
		} else {
			fileName = "REPS " + strings.ToUpper(objName) + fileExtension + "IU.abap"
		}

	}
	// CLASSES DEFINITIONS
	regexClasDef := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/includes\/definitions\/`)
	clasDef := regexClasDef.FindString(path)
	if clasDef != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".clas.definitions.abap"
		} else {
			fileName = "CINC " + objName + fileExtension + "CCDEF.abap"
		}

	}

	// CLASSES IMPLEMENTATIONS
	regexClasImpl := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/includes\/implementations\/`)
	clasImpl := regexClasImpl.FindString(path)
	if clasImpl != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".clas.implementations.abap"
		} else {
			fileName = "CINC " + objName + fileExtension + "CCIMP.abap"
		}

	}

	// CLASSES MACROS
	regexClasMacro := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/includes\/macros\/`)
	clasMacro := regexClasMacro.FindString(path)
	if clasMacro != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".clas.macros.abap"
		} else {
			fileName = "CINC " + objName + fileExtension + "CCMAC.abap"
		}

	}

	// TEST CLASSES
	regexTestClass := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*#?\/?\w*\/?testclass`)
	testClass := regexTestClass.FindString(path)
	if testClass != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".clas.testclasses.abap"
		} else {
			fileName = "CINC " + objName + fileExtension + "CCAU.abap"
		}

	}

	// CLASS PROTECTED
	regexClasProtected := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/source\/main#type=CLAS\/OSO`)
	classProtected := regexClasProtected.FindString(path)
	if classProtected != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".clas.abap"
		} else {
			fileName = "CPRO " + objName + ".abap"
		}

	}

	// CLASS PRIVATE
	regexClasPrivate := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/source\/main#type=CLAS\/OSI`)
	classPrivate := regexClasPrivate.FindString(path)
	if classPrivate != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".clas.abap"
		} else {
			fileName = "CPRI " + objName + ".abap"
		}

	}

	// CLASS METHOD
	regexClasMethod := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/source\/main#type=CLAS\/OM`)
	classMethod := regexClasMethod.FindString(path)
	if classMethod != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".clas.abap"
		} else {

			regexmethodName := regexp.MustCompile(`name=\w*`)
			methodName := regexmethodName.FindString(path)

			fileName = "METH " + methodName[len(`name=`):] + ".abap"
		}

	}

	// CLASS PUBLIC
	regexClasPublic := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/source\/main#start`)
	classPublic := regexClasPublic.FindString(path)
	if classPublic != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".clas.abap"
		} else {
			fileName = "CPUB " + objName + ".abap"
		}

	}

	// FUNCTION INCLUDE
	regexFuncIncl := regexp.MustCompile(`\/sap\/bc\/adt\/functions\/groups\/\w*\/includes/\w*`)

	funcIncl := regexFuncIncl.FindString(path)
	if funcIncl != "" && fileName == "" {

		regexSubObj := regexp.MustCompile(`includes\/\w*`)
		subObject := regexSubObj.FindString(path)
		subObject = subObject[len(`includes/`):]

		if readableSource {

			fileName = strings.ToLower(objName) + ".fugr." + strings.ToLower(subObject) + ".reps.abap"
		} else {
			fileName = "REPS " + strings.ToUpper(subObject) + ".abap"
		}

	}

	// FUNCTION GROUP
	regexFuncGr := regexp.MustCompile(`\/sap\/bc\/adt\/functions\/groups\/\w*\/source\/main`)

	funcGr := regexFuncGr.FindString(path)
	if funcGr != "" && fileName == "" {

		if readableSource {

			fileName = strings.ToLower(objName) + ".fugr.sapl" + strings.ToLower(objName) + ".reps.abap"
		} else {
			fileName = "REPS SAPL" + objName + ".abap"
		}

	}

	// FUNCTION MODULE
	regexFuncMod := regexp.MustCompile(`\/sap\/bc\/adt\/functions\/groups\/\w*\/fmodules/\w*`)
	funcMod := regexFuncMod.FindString(path)
	if funcMod != "" && fileName == "" {

		regexSubObj := regexp.MustCompile(`includes\/\w*`)
		subObject := regexSubObj.FindString(path)
		subObject = subObject[len(`includes/`):]

		if readableSource {

			fileName = strings.ToLower(subObject) + ".func.abap"
		} else {
			fileName = "FUNC " + subObject + ".abap"
		}

	}
	// CLAS
	regexClas := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/` + strings.ToLower(objName))
	clas := regexClas.FindString(path)
	if clas != "" && fileName == "" {
		if readableSource {

			fileName = strings.ToLower(objName) + ".clas.abap"
		} else {

			fileName = "CPUB " + objName + ".abap"
		}

	}

	// PROGRAM
	regexProg := regexp.MustCompile(`\/sap\/bc\/adt\/programs\/programs\/` + strings.ToLower(objName))
	prog := regexProg.FindString(path)
	if prog != "" && fileName == "" {

		fileName = "REPS " + objName + ".abap"

	}

	// TABLES
	regexTab := regexp.MustCompile(`\/sap\/bc\/adt\/ddic\/tables\/` + strings.ToLower(objName))
	tab := regexTab.FindString(path)
	if tab != "" && fileName == "" {

		fileName = "TABL " + objName + ".asx.json"

	}

	return fileName, nil

}

func getTargetDir(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (string, error) {

	var targetDir string

	repository, err := getRepo(config, client)

	if err != nil {
		return targetDir, err
	}

	for _, config := range repository.Result.Config {
		if config.Key == "VCS_TARGET_DIR" {
			targetDir = config.Value
		}
	}

	return targetDir, nil

}

func checkReadableSource(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (readableSource bool, error error) {

	repoLayout, err := getRepositoryLayout(config, client)
	if err != nil {
		return readableSource, errors.Wrap(err, "could not check readable source format")
	}

	if repoLayout.Layout.ReadableSource == "true" || repoLayout.Layout.ReadableSource == "only" || repoLayout.Layout.ReadableSource == "all" {

		readableSource = true

	} else {

		readableSource = false

	}

	return readableSource, nil
}

func getRepo(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (repositoryResponse, error) {

	var repositoryResp repositoryResponse
	url := config.Host +
		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
		"?sap-client=" + config.Client
	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()

	if httpErr != nil {
		return repositoryResponse{}, errors.Wrap(httpErr, "could not get repository")
	} else if resp == nil {
		return repositoryResponse{}, errors.New("could not get repository: did not retrieve a HTTP response")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &repositoryResp)
	if parsingErr != nil {
		return repositoryResponse{}, errors.Errorf("%v", parsingErr)
	}

	return repositoryResp, nil

}

func getRepositoryLayout(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (layoutResponse, error) {

	var repoLayoutResponse layoutResponse
	url := config.Host +
		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
		"/layout?sap-client=" + config.Client

	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)

	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()

	if httpErr != nil {
		return layoutResponse{}, errors.Wrap(httpErr, "could not get repository layout")
	} else if resp == nil {
		return layoutResponse{}, errors.New("could not get repository layout: did not retrieve a HTTP response")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &repoLayoutResponse)
	if parsingErr != nil {
		return layoutResponse{}, errors.Errorf("%v", parsingErr)
	}

	return repoLayoutResponse, nil
}

func getCommitList(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (commitResponse, error) {

	var commitResp commitResponse
	url := config.Host +
		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
		"/getCommit?sap-client=" + config.Client

	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)

	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()

	if httpErr != nil {
		return commitResponse{}, errors.Wrap(httpErr, "get repository history failed")
	} else if resp == nil {
		return commitResponse{}, errors.New("get repository history failed: did not retrieve a HTTP response")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &commitResp)
	if parsingErr != nil {
		return commitResponse{}, errors.Errorf("%v", parsingErr)
	}

	return commitResp, nil
}

func getObjectDifference(config *gctsExecuteABAPQualityChecksOptions, fromCommit string, toCommit string, client piperhttp.Sender) (objectsResponse, error) {
	var objectResponse objectsResponse

	url := config.Host +
		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
		"/compareCommits?fromCommit=" + fromCommit + "&toCommit=" + toCommit + "&sap-client=" + config.Client

	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)

	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()

	if httpErr != nil {
		return objectsResponse{}, errors.Wrap(httpErr, "get object difference failed")
	} else if resp == nil {
		return objectsResponse{}, errors.New("get object difference failed: did not retrieve a HTTP response")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &objectResponse)
	if parsingErr != nil {
		return objectsResponse{}, errors.Errorf("%v", parsingErr)
	}
	log.Entry().Info("get object differences: ", objectResponse.Objects)
	return objectResponse, nil
}

func getObjectInfo(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, objectName string, objectType string) (objectInfo, error) {

	var objectMetInfoResponse objectInfo
	url := config.Host +
		"/sap/bc/cts_abapvcs/objects/" + objectType + "/" + objectName +
		"?sap-client=" + config.Client

	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)

	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()

	if httpErr != nil {
		return objectInfo{}, errors.Wrap(httpErr, "resolve package failed")
	} else if resp == nil {
		return objectInfo{}, errors.New("resolve package failed: did not retrieve a HTTP response")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &objectMetInfoResponse)
	if parsingErr != nil {
		return objectInfo{}, errors.Errorf("%v", parsingErr)
	}
	return objectMetInfoResponse, nil

}

func getHistory(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (historyResponse, error) {

	var historyResp historyResponse
	url := config.Host +
		"/sap/bc/cts_abapvcs/repository/" + config.Repository + "/getHistory?sap-client=" + config.Client

	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)

	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if httpErr != nil {
		return historyResponse{}, errors.Wrap(httpErr, "get history failed")
	} else if resp == nil {
		return historyResponse{}, errors.New("get history failed: did not retrieve a HTTP response")
	}

	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &historyResp)
	if parsingErr != nil {
		return historyResponse{}, errors.Errorf("%v", parsingErr)
	}

	return historyResp, nil
}

type worklist struct {
	XMLName             xml.Name `xml:"worklist"`
	Text                string   `xml:",chardata"`
	ID                  string   `xml:"id,attr"`
	Timestamp           string   `xml:"timestamp,attr"`
	UsedObjectSet       string   `xml:"usedObjectSet,attr"`
	ObjectSetIsComplete string   `xml:"objectSetIsComplete,attr"`
	Atcworklist         string   `xml:"atcworklist,attr"`
	ObjectSets          struct {
		Text      string `xml:",chardata"`
		ObjectSet []struct {
			Text  string `xml:",chardata"`
			Name  string `xml:"name,attr"`
			Title string `xml:"title,attr"`
			Kind  string `xml:"kind,attr"`
		} `xml:"objectSet"`
	} `xml:"objectSets"`
	Objects struct {
		Text   string `xml:",chardata"`
		Object []struct {
			Text        string `xml:",chardata"`
			URI         string `xml:"uri,attr"`
			Type        string `xml:"type,attr"`
			Name        string `xml:"name,attr"`
			PackageName string `xml:"packageName,attr"`
			Author      string `xml:"author,attr"`
			Atcobject   string `xml:"atcobject,attr"`
			Adtcore     string `xml:"adtcore,attr"`
			Findings    struct {
				Text    string `xml:",chardata"`
				Finding []struct {
					Text              string `xml:",chardata"`
					URI               string `xml:"uri,attr"`
					Location          string `xml:"location,attr"`
					Processor         string `xml:"processor,attr"`
					LastChangedBy     string `xml:"lastChangedBy,attr"`
					Priority          string `xml:"priority,attr"`
					CheckId           string `xml:"checkId,attr"`
					CheckTitle        string `xml:"checkTitle,attr"`
					MessageId         string `xml:"messageId,attr"`
					MessageTitle      string `xml:"messageTitle,attr"`
					ExemptionApproval string `xml:"exemptionApproval,attr"`
					ExemptionKind     string `xml:"exemptionKind,attr"`
					Checksum          string `xml:"checksum,attr"`
					QuickfixInfo      string `xml:"quickfixInfo,attr"`
					Atcfinding        string `xml:"atcfinding,attr"`
					Link              struct {
						Text string `xml:",chardata"`
						Href string `xml:"href,attr"`
						Rel  string `xml:"rel,attr"`
						Type string `xml:"type,attr"`
						Atom string `xml:"atom,attr"`
					} `xml:"link"`
					Quickfixes struct {
						Text      string `xml:",chardata"`
						Manual    string `xml:"manual,attr"`
						Automatic string `xml:"automatic,attr"`
						Pseudo    string `xml:"pseudo,attr"`
					} `xml:"quickfixes"`
				} `xml:"finding"`
			} `xml:"findings"`
		} `xml:"object"`
	} `xml:"objects"`
}

type runResult struct {
	XMLName xml.Name `xml:"runResult"`
	Text    string   `xml:",chardata"`
	Aunit   string   `xml:"aunit,attr"`
	Program []struct {
		Text    string `xml:",chardata"`
		URI     string `xml:"uri,attr"`
		Type    string `xml:"type,attr"`
		Name    string `xml:"name,attr"`
		URIType string `xml:"uriType,attr"`
		Adtcore string `xml:"adtcore,attr"`
		Alerts  struct {
			Text  string `xml:",chardata"`
			Alert struct {
				Text            string `xml:",chardata"`
				HasSyntaxErrors string `xml:"hasSyntaxErrors,attr"`
				Kind            string `xml:"kind,attr"`
				Severity        string `xml:"severity,attr"`
				Title           string `xml:"title"`
				Details         struct {
					Text   string `xml:",chardata"`
					Detail struct {
						Text     string `xml:",chardata"`
						AttrText string `xml:"text,attr"`
					} `xml:"detail"`
				} `xml:"details"`
				Stack struct {
					Text       string `xml:",chardata"`
					StackEntry struct {
						Text        string `xml:",chardata"`
						URI         string `xml:"uri,attr"`
						Description string `xml:"description,attr"`
					} `xml:"stackEntry"`
				} `xml:"stack"`
			} `xml:"alert"`
		} `xml:"alerts"`

		TestClasses struct {
			Text      string `xml:",chardata"`
			TestClass []struct {
				Text             string `xml:",chardata"`
				URI              string `xml:"uri,attr"`
				Type             string `xml:"type,attr"`
				Name             string `xml:"name,attr"`
				URIType          string `xml:"uriType,attr"`
				NavigationURI    string `xml:"navigationUri,attr"`
				DurationCategory string `xml:"durationCategory,attr"`
				RiskLevel        string `xml:"riskLevel,attr"`
				TestMethods      struct {
					Text       string `xml:",chardata"`
					TestMethod []struct {
						Text          string `xml:",chardata"`
						URI           string `xml:"uri,attr"`
						Type          string `xml:"type,attr"`
						Name          string `xml:"name,attr"`
						ExecutionTime string `xml:"executionTime,attr"`
						URIType       string `xml:"uriType,attr"`
						NavigationURI string `xml:"navigationUri,attr"`
						Unit          string `xml:"unit,attr"`
						Alerts        struct {
							Text  string `xml:",chardata"`
							Alert []struct {
								Text     string `xml:",chardata"`
								Kind     string `xml:"kind,attr"`
								Severity string `xml:"severity,attr"`
								Title    string `xml:"title"`
								Details  struct {
									Text   string `xml:",chardata"`
									Detail []struct {
										Text     string `xml:",chardata"`
										AttrText string `xml:"text,attr"`
										Details  struct {
											Text   string `xml:",chardata"`
											Detail []struct {
												Text     string `xml:",chardata"`
												AttrText string `xml:"text,attr"`
											} `xml:"detail"`
										} `xml:"details"`
									} `xml:"detail"`
								} `xml:"details"`
								Stack struct {
									Text       string `xml:",chardata"`
									StackEntry struct {
										Text        string `xml:",chardata"`
										URI         string `xml:"uri,attr"`
										Type        string `xml:"type,attr"`
										Name        string `xml:"name,attr"`
										Description string `xml:"description,attr"`
									} `xml:"stackEntry"`
								} `xml:"stack"`
							} `xml:"alert"`
						} `xml:"alerts"`
					} `xml:"testMethod"`
				} `xml:"testMethods"`
			} `xml:"testClass"`
		} `xml:"testClasses"`
	} `xml:"program"`
}

type gctsException struct {
	Message     string `json:"message"`
	Description string `json:"description"`
	Code        int    `json:"code"`
}

type gctsLogs struct {
	Time     int    `json:"time"`
	User     string `json:"user"`
	Section  string `json:"section"`
	Action   string `json:"action"`
	Severity string `json:"severity"`
	Message  string `json:"message"`
	Code     string `json:"code"`
}

type commit struct {
	ID string `json:"id"`
}

type commitResponse struct {
	Commits   []commit      `json:"commits"`
	ErrorLog  []gctsLogs    `json:"errorLog"`
	Log       []gctsLogs    `json:"log"`
	Exception gctsException `json:"exception"`
}

type objectInfo struct {
	Pgmid     string `json:"pgmid"`
	Object    string `json:"object"`
	ObjName   string `json:"objName"`
	Srcsystem string `json:"srcsystem"`
	Author    string `json:"author"`
	Devclass  string `json:"devclass"`
}

type repoConfig struct {
	Key        string  `json:"key"`
	Value      string  `json:"value"`
	Cprivate   string  `json:"cprivate"`
	Cprotected string  `json:"cprotected"`
	Cvisible   string  `json:"cvisible"`
	Category   string  `json:"category"`
	Scope      string  `json:"scope"`
	ChangedAt  float64 `json:"changeAt"`
	ChangedBy  string  `json:"changedBy"`
}

type repository struct {
	Rid           string       `json:"rid"`
	Name          string       `json:"name"`
	Role          string       `json:"role"`
	Type          string       `json:"type"`
	Vsid          string       `json:"vsid"`
	PrivateFlag   string       `json:"privateFlag"`
	Status        string       `json:"status"`
	Branch        string       `json:"branch"`
	Url           string       `json:"url"`
	CreatedBy     string       `json:"createdBy"`
	CreatedDate   string       `json:"createdDate"`
	Config        []repoConfig `json:"config"`
	Objects       int          `json:"objects"`
	CurrentCommit string       `json:"currentCommit"`
}

type repositoryResponse struct {
	Result    repository    `json:"result"`
	Exception gctsException `json:"exception"`
}

type objects struct {
	Name   string `json:"name"`
	Type   string `json:"type"`
	Action string `json:"action"`
}
type objectsResponse struct {
	Objects   []objects     `json:"objects"`
	Log       []gctsLogs    `json:"log"`
	Exception gctsException `json:"exception"`
	ErrorLogs []gctsLogs    `json:"errorLog"`
}

type repoObject struct {
	Pgmid       string `json:"pgmid"`
	Object      string `json:"object"`
	Type        string `json:"type"`
	Description string `json:"description"`
}

type repoObjectResponse struct {
	Objects   []repoObject  `json:"objects"`
	Log       []gctsLogs    `json:"log"`
	Exception gctsException `json:"exception"`
	ErrorLogs []gctsLogs    `json:"errorLog"`
}

type layout struct {
	FormatVersion   int    `json:"formatVersion"`
	Format          string `json:"format"`
	ObjectStorage   string `json:"objectStorage"`
	MetaInformation string `json:"metaInformation"`
	TableContent    string `json:"tableContent"`
	Subdirectory    string `json:"subdirectory"`
	ReadableSource  string `json:"readableSource"`
	KeepClient      string `json:"keepClient"`
}

type layoutResponse struct {
	Layout    layout     `json:"layout"`
	Log       []gctsLogs `json:"log"`
	Exception string     `json:"exception"`
	ErrorLogs []gctsLogs `json:"errorLog"`
}

type history struct {
	Rid          string `json:"rid"`
	CheckoutTime int    `json:"checkoutTime"`
	FromCommit   string `json:"fromCommit"`
	ToCommit     string `json:"toCommit"`
	Caller       string `json:"caller"`
	Type         string `json:"type"`
}

type historyResponse struct {
	Result    []history `xml:"result"`
	Exception string    `json:"exception"`
}

type checkstyleError struct {
	Text     string `xml:",chardata"`
	Message  string `xml:"message,attr"`
	Source   string `xml:"source,attr"`
	Line     string `xml:"line,attr"`
	Severity string `xml:"severity,attr"`
}

type file struct {
	Text  string            `xml:",chardata"`
	Name  string            `xml:"name,attr"`
	Error []checkstyleError `xml:"error"`
}

type checkstyle struct {
	XMLName xml.Name `xml:"checkstyle"`
	Text    string   `xml:",chardata"`
	Version string   `xml:"version,attr"`
	File    []file   `xml:"file"`
}
