package main

import (
	"fmt"
	"log"
	"regexp"
	"strings"
)

const (
	// TODO: drop these in favor of strings.Fields()
	nonSpaceToken = "[^\t\n\f\r ]+" // equivalent to perl's \S
	spaceToken    = "[\t\n\f\r ]+"  // equivalent to perl's \s
)

// packageProvides returns a map from package name to a string slice of
// provided packages, for all of the specified packages (or all of the
// installed ones, if none are specified).
func packageProvides(ignoreMissing bool, pkgs []string) map[string]map[string]bool {
	lines, err := runCommand(append([]string{
		"/usr/bin/dpkg-query",
		"-Wf", "${binary:Package} ${Package};${Status};${Provides}\n",
		"--"},
		pkgs...))
	if err != nil {
		log.Fatal(err)
	}

	// sample input:
	// libparted-fs-resize0:amd64 libparted-fs-resize0;install ok installed;
	// xserver-xorg-core xserver-xorg-core;deinstall ok config-files;xorg-input-abi-24, xorg-video-abi-25, xserver-xorg-video-modesetting
	// ^(\S+).*;.*\s(\S+);(.*)$
	pkgStatusProvides := regexp.MustCompile("^(" + nonSpaceToken + ").*;.*" + spaceToken + "(" + nonSpaceToken + ");(.*)$")

	pkgProvides := make(map[string]map[string]bool)
	for _, line := range lines {
		m := pkgStatusProvides.FindStringSubmatch(line)
		if len(m) != 4 {
			log.Fatalf("Unexpected output from dpkg-query: %q\n", line)
			continue
		}
		pkg := string(m[1])
		status := string(m[2])
		providedPkgs := strings.Split(strings.Replace(string(m[3]), " ", "", -1), ",")

		if status == "installed" {
			provides := make(map[string]bool)
			for _, pr := range providedPkgs {
				provides[pr] = true
			}
			pkgProvides[pkg] = provides
		} else if len(pkgs) > 0 {
			fmt.Printf("Skipping %s because it's not installed", pkg)
		}

	}

	return pkgProvides
}

// packageFiles returns a map from deb (binary) package name to string slice of
// package files, and the total number of files.
func packageFiles(pkgs []string) (map[string][]string, int64, error) {
	lines, err := runCommand(append([]string{"/usr/bin/dpkg", "-L"}, pkgs...))
	if err != nil {
		log.Fatal(err)
	}

	pkg2files := make(map[string][]string)
	i := 0
	var totalFiles int64
	diverted := regexp.MustCompile("^(?:locally diverted|diverted by " + nonSpaceToken + ") to: (/.+)$")
	for _, line := range lines {
		if line == "" {
			i++
			continue
		}
		pkg := pkgs[i]
		file := line

		if m := diverted.FindStringSubmatch(line); len(m) == 2 {
			file = string(m[1])
		}
		if len(file) == 0 || file[0] != '/' {
			continue
		}

		pkg2files[pkg] = append(pkg2files[pkg], file)
		totalFiles += 1
	}

	return pkg2files, totalFiles, nil
}
