
# the object allowing us to build complex structures
# this thing contains everything else, including itself

require 'puppet'
require 'puppet/type'
require 'puppet/transaction'
require 'puppet/pgraph'

Puppet::Type.newtype(:component) do
    include Enumerable
    attr_accessor :children

    newparam(:name) do
        desc "The name of the component.  Generally optional."
        isnamevar
    end

    newparam(:type) do
        desc "The type that this component maps to.  Generally some kind of
            class from the language."

        defaultto "component"
    end

    # Remove a child from the component.
    def delete(child)
        if @children.include?(child)
            @children.delete(child)
            return true
        else
            return super
        end
    end

    # Recurse deeply through the tree, but only yield types, not properties.
    def delve(&block)
        self.each do |obj|
            if obj.is_a?(self.class)
                obj.delve(&block)
            end
        end
        block.call(self)
    end

    # Return each child in turn.
    def each
        @children.each { |child| yield child }
    end

    # Do all of the polishing off, mostly doing autorequires and making
    # dependencies.  This will get run once on the top-level component,
    # and it will do everything necessary.
    def finalize
        started = {}
        finished = {}
        
        # First do all of the finish work, which mostly involves
        self.delve do |object|
            # Make sure we don't get into loops
            if started.has_key?(object)
                debug "Already finished %s" % object.title
                next
            else
                started[object] = true
            end
            unless finished.has_key?(object)
                object.finish
                finished[object] = true
            end
        end

        @finalized = true
    end

    def finalized?
        if defined? @finalized
            return @finalized
        else
            return false
        end
    end

    # Initialize a new component
    def initialize(*args)
        @children = []
        super

        @reference = Puppet::ResourceReference.new(:component, @title)

        unless self.class[@reference.to_s]
            self.class.alias(@reference.to_s, self)
        end
    end

    def initvars
        super
        @children = []
    end

    # Add a hook for testing for recursion.
    def parentof?(child)
        if super(child)
            return true
        elsif @children.include?(child)
            debug "child is already in children array"
            return true
        else
            return false
        end
    end
    
    # Component paths are special because they function as containers.
    def pathbuilder
        if @reference.type == "Class"
            # 'main' is the top class, so we want to see '//' instead of
            # its name.
            if @reference.title == "main"
                myname = ""
            else
                myname = @reference.title
            end
        else
            myname = @reference.to_s
        end
        if p = self.parent
            return [p.pathbuilder, myname]
        else
            return [myname]
        end
    end

    def ref
        @reference.to_s
    end

    # We want our title to just be the whole reference, rather than @title.
    def title
        @reference.to_s
    end

    def refresh
        catalog.adjacent(self).each do |child|
            if child.respond_to?(:refresh)
                child.refresh
                child.log "triggering %s" % :refresh
            end
        end
    end

    def to_s
        @reference.to_s
    end
end
