// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Aaron Meihm ameihm@mozilla.com

package scribe

import (
	"fmt"
	"regexp"
)

// Pkg is used to perform tests against packages that are installed on the
// system, for example package version tests.
//
// Normally when a Pkg object is prepared, the pkgInfo list will be filled with
// information related to any package installed which exactly matches Name. If
// the optional CollectMatch parameter is set, this regular expression will be
// used to match packages, but the package name will still be Name.
//
// For example, if Name is set to linux-image-generic, and CollectMatch is set
// to ^linux-image-.*-generic$, it will result in the object being populated
// with a set of version strings from all installed packages that match the
// regexp.
//
// This is intended to handle a case where we want to do a kernel package
// version comparison, but the kernel package name actually includes the a
// version string which makes a direct package name -> name comparison harder.
//
// If OnlyNewest is true, the object will only be populated with the newest
// instance of a given package if there are multiple versions of the same
// package installed.
type Pkg struct {
	Name         string `json:"name,omitempty" yaml:"name,omitempty"`
	CollectMatch string `json:"collectmatch,omitempty" yaml:"collectmatch,omitempty"`
	OnlyNewest   bool   `json:"onlynewest,omitempty" yaml:"onlynewest,omitempty"`
	pkgInfo      []packageInfo
}

type packageInfo struct {
	Name    string
	Version string
}

func (p *Pkg) isChain() bool {
	return false
}

func (p *Pkg) validate(d *Document) error {
	if len(p.Name) == 0 {
		return fmt.Errorf("package must specify name")
	}
	if len(p.CollectMatch) > 0 {
		_, err := regexp.Compile(p.CollectMatch)
		if err != nil {
			return err
		}
	}
	return nil
}

func (p *Pkg) fireChains(d *Document) ([]evaluationCriteria, error) {
	return nil, nil
}

func (p *Pkg) mergeCriteria(c []evaluationCriteria) {
}

func (p *Pkg) getCriteria() (ret []evaluationCriteria) {
	for _, x := range p.pkgInfo {
		n := evaluationCriteria{}
		n.identifier = x.Name
		n.testValue = x.Version
		ret = append(ret, n)
	}
	return ret
}

func newestPackage(r pkgmgrResult) (ret packageInfo, err error) {
	var pinfo *pkgmgrInfo
	for i := range r.results {
		if pinfo == nil {
			pinfo = &r.results[i]
			continue
		}
		f, err := evrCompare(EvropLessThan, pinfo.version, r.results[i].version)
		if err != nil {
			return ret, err
		}
		if f {
			pinfo = &r.results[i]
		}
	}
	ret.Name = pinfo.name
	ret.Version = pinfo.version
	return ret, nil
}

func (p *Pkg) prepare() error {
	debugPrint("prepare(): preparing information for package \"%v\"\n", p.Name)
	p.pkgInfo = make([]packageInfo, 0)
	ret := getPackage(p.Name, p.CollectMatch)
	if p.OnlyNewest && len(ret.results) > 0 {
		pir, err := newestPackage(ret)
		if err != nil {
			return err
		}
		p.pkgInfo = append(p.pkgInfo, pir)
		return nil
	}
	for _, x := range ret.results {
		n := packageInfo{}
		n.Name = x.name
		n.Version = x.version
		p.pkgInfo = append(p.pkgInfo, n)
	}
	return nil
}

func (p *Pkg) expandVariables(v []Variable) {
	p.Name = variableExpansion(v, p.Name)
}
