#! /usr/bin/env ruby
#
# Copyright (c) 2024 Morgane "Sardem FF7" Glidic
#
# SPDX-License-Identifier: GPL-3.0

require 'optparse'
require 'set'
require 'Paludis'

$envspec = ""
OptionParser.new do |opts|
    opts.banner = "Usage: #{$0} [options] <package spec>"

    opts.on("-E", "--environment=ENV", "Paludis environment") do |s|
        $envspec = s
    end
end.parse!

target = ARGV.shift

$env = Paludis::EnvironmentFactory.instance.create($envspec)
matches = $env[Paludis::Selection::AllVersionsSorted.new(Paludis::Generator::Matches.new(Paludis::parse_user_package_dep_spec(target, $env, []), nil, []) | Paludis::Filter::SupportsAction.new(Paludis::InstallAction) | Paludis::Filter::NotMasked.new)]
case ( matches.size )
    when 0
        $stderr.puts "Could not find the target '#{target}'"
        exit(1)
    when 1
        # All is good
    else
        $stderr.puts "Could not find only one target '#{target}'"
        exit(2)
end

class PaludisNode
    protected
    @@recursive_sets = Set.new

    @@packages = {}
    def get_dep(spec)
        return @@packages[spec.to_s] if ( @@packages.has_key?(spec.to_s) )
        $env[Paludis::Selection::AllVersionsSorted.new(Paludis::Generator::Matches.new(spec, @pkg, []) | Paludis::Filter::InstalledAtRoot.new($env.preferred_root_key.parse_value))].map do |pkg|
            id = pkg.name.to_s
            id += ":#{pkg.slot_key.parse_value}" unless ( pkg.slot_key.nil? )
            @@packages[id] = PaludisPackage.new(pkg) unless ( @@packages.has_key?(id) )
            @@packages[id]
        end
    end

    def initialize()
        @checked = false
    end

    def check
        return true if ( @checked )
        @checked = true
        return false
    end

    RUN_DEPS = [ "run", "post" ]
    BUILD_DEPS = [ "build", "test", "test-expensive", "run" ]
end

class PaludisSet < PaludisNode
    def initialize(name)
        super()
        @name = name
        @set = $env.set(@name)
        raise "Unknown set #{@name}" if @set.nil?
    end

    def check
        return nil if ( super() || @@recursive_sets.include?(@name) )
        @@recursive_sets.add(@name)
        @pull_dep = true
        r = self.read_dep(@set).flatten
        @@recursive_sets.delete(@name)
        r
    end

    def files
        []
    end
end

class PaludisPackage < PaludisNode
    def initialize(pkg, all_deps: false)
        @pkg = pkg
        @checked = false
        @all_deps = all_deps
    end

    def check
        return nil if ( super() )
        # Default label is build+run, so we pull the dep
        @pull_dep = true
        deps = @pkg.dependencies_key
        deps.nil? ? [] : deps.parse_value.map(&self.method(:read_dep)).flatten
    end

    def files
        c = @pkg.contents
        return [] if ( c.nil? )
        c.reject do |entry|
            Paludis::ContentsDirEntry === entry
        end.map(&:location_key.to_proc >> :parse_value.to_proc)
    end
end

class PaludisNode
    protected

    def read_dep(node)
        case ( node )
        when Paludis::AllDepSpec, Paludis::AnyDepSpec
            node.map(&self.method(:read_dep)).compact.flatten
        when Paludis::DependenciesLabelsDepSpec
            @pull_dep = node.labels.map(&:text).intersect?(@all_deps ? BUILD_DEPS : RUN_DEPS)
            nil
        when Paludis::ConditionalDepSpec
            node.map(&self.method(:read_dep)).compact.flatten if ( node.condition_met?($env, @pkg) )
        when Paludis::PackageDepSpec
            @pull_dep ? node.package ? self.get_dep(node) : nil : nil
        when Paludis::NamedSetDepSpec
            [PaludisSet.new(node.name)]
        when Paludis::BlockDepSpec
        end
    end
end

packages = [
    PaludisPackage.new(matches[0], all_deps: true),
    PaludisSet.new("system")
]
begin
    new_stuff = packages.map(&:check).compact
    packages.concat(*new_stuff)
end until ( new_stuff.empty? )

puts(packages.map(&:files).flatten.map do |f|
    "allow/stat+#{f}"
end)
