package Spoon::Hub;
use Spoon::Base -Base;

const class_id => 'hub';
field action => '_default_';

field main => -weak;
field config_files => [];
field all_hooks => [];
field 'config';

sub new {
    $self = $self->SUPER::new(@_);
    $self->init;
    die "Hub already created"
      if defined $Spoon::Base::HUB;
    $Spoon::Base::HUB = $self;
}

our $AUTOLOAD;
sub AUTOLOAD {
    $AUTOLOAD =~ /.*::(.*)/
      or die "Can't AUTOLOAD '$AUTOLOAD'";
    my $class_id = $1;
    return if $class_id eq 'DESTROY';
    field $class_id => 
          -init => "\$self->load_class('$class_id')";
    $self->$class_id(@_);
}

sub pre_process {}
sub post_process {}

sub process {
    $self->preload;
    my $action = $self->action;
    return $self->unknown_action($action)
      unless defined $self->registry->lookup->action->{$action};
    my ($class_id, $method) = 
      @{$self->registry->lookup->action->{$action}};
    $method ||= $action;
    return $self->$class_id->$method;
}

sub unknown_action {
    my $action = shift;
    die "No plugin for action '$action'";
}

sub preload {
    my $preload = $self->registry->lookup->preload;
    map {
        $self->load_class($_->[0])
    } sort {
        $b->[1] <=> $a->[1]
    } map {
        my %hash = @{$preload->{$_}}[1..$#{$preload->{$_}}];
        [$_, $hash{priority} || 0];
    } keys %$preload;
    return $self;
}

sub load_class {
    my $class_id = shift;
    return $self if $class_id eq 'hub';
    return $self->$class_id 
      if $self->can($class_id) and defined $self->{$class_id};

    my $class_class = $class_id . '_class';

    my $class_name = $self->config->can($class_class)
        ? $self->config->$class_class
        : $self->registry_loaded
          ? $self->registry->lookup->classes->{$class_id}
          : Carp::confess "Can't find a class for class_id '$class_id'";

    Carp::confess "No class defined for class_id '$class_id'"
      unless $class_name;
    unless ($class_name->can('new')) {
        eval "require $class_name";
        die $@ if $@;
    }
    $self->add_hooks
      unless $class_id eq 'hooks';
    my $object = $class_name->new
      or die "Can't create new '$class_name' object";
    $class_id ||= $object->class_id;
    die "No class_id defined for class: '$class_name'\n"
      unless $class_id;
    field $class_id => 
          -init => "\$self->load_class('$class_id')";
    $self->$class_id($object);
    $object->init;
    return $object;
}

sub add_hooks {
    return unless $self->registry_loaded;
    my $hooks = $self->registry->lookup->{hook}
      or return;
    for my $class_name (keys %$hooks) {
        next unless $class_name->can('new');
        $self->add_hook(@$_) for @{$hooks->{$class_name} || []};
        delete $hooks->{$class_name};
    }
    delete $self->registry->lookup->{hook}
      if not keys %$hooks;
}

sub add_hook {
    my $hooks = $self->all_hooks;
    push @$hooks, $self->hooks->add(@_);
    return $hooks->[-1];
}

sub remove_hooks {
    my $hooks = $self->all_hooks;
    while (@$hooks) {
        pop(@$hooks)->unhook;
    }
}

sub registry_loaded {
    defined $self->{registry} &&
    defined $self->{registry}{lookup};
}

sub DESTROY {
    $self->remove_hooks;
}
