
package UR::Namespace::Command::Describe;

use strict;
use warnings;
use UR;

UR::Object::Type->define(
    class_name => __PACKAGE__,
    is => 'UR::Namespace::Command::RunsOnModulesInTree'
);


sub help_brief {
    "Outputs class description(s) to stdout.";
}


# The class metadata has lots of properties that we're not interested in
our @CLASS_PROPERTIES_NOT_TO_PRINT = qw(
    generated
    short_name
    is
    all_class_metas
);
    
    
our $view;
sub for_each_class_object {
    my $self = shift;
    my $class_meta = shift;

    $view ||= UR::Object::Type->create_view(
                    perspective => 'default',
                    toolkit => 'text',
                    aspects => [
                        'namespace', 'table_name', 'data_source_id', 'is_abstract', 'is_final',
                        'is_singleton', 'is_transactional', 'schema_name', 'meta_class_name',
                        'first_sub_classification_method_name', 'sub_classification_method_name',
                        {
                            label => 'Properties',
                            name => 'properties',
                            subject_class_name => 'UR::Object::Property',
                            perspective => 'description line item',
                            toolkit => 'text',
                            aspects => ['is_id', 'property_name', 'column_name', 'data_type', 'is_optional' ],
                        },
                        {
                            label => 'References',
                            name => 'all_reference_metas',
                            subject_class_name => 'UR::Object::Reference',
                            perspective => 'description line item',
                            toolkit => 'text',
                            aspects => [],
                        }
                    ],
                );
    unless ($view) {
        $self->error_message("Can't initialize view");
        return;
    }

    $view->subject($class_meta);
    $view->show();
    #print $view->content();
    #print "\n";
}

    


sub X_for_each_class_object {
    my $self = shift;
    my $class = shift;

$DB::single=1;
    print $class->class_name;

    #my @parent_class_names = grep { $_ !~ qr/^(UR::Object|UR::Entity)$/ } $class->parent_class_names;
    my @parent_class_names = $class->parent_class_names;
    if (@parent_class_names) {
        print " < ", join(' ', @parent_class_names);
    }

    my $class_meta = $class->__meta__();

    print "\n  Class:\n";
    
    my %all_class_properties = map { $_ => 1 } $class_meta->all_property_names;

    # Print these first
    my %printed;
    my @prop_list = qw(namespace table_name doc);
    foreach my $item ( @prop_list )  {
        my $val = eval { $class->$item };
        $printed{$item} = 1;
        next unless defined $val;
        printf("    %16s  %s\n", $item, $val);
    }

    

    my @data_sources = $UR::Context::current->resolve_data_sources_for_class_meta_and_rule($class);
    { no warnings 'uninitialized';
      printf("    %16s  %s\n", 'data_source', join(',',map { $_ and $_->id } @data_sources));
    }
    
    delete @all_class_properties{(@prop_list, @CLASS_PROPERTIES_NOT_TO_PRINT, 'id_by','is','class_name','source','data_source_id')};
    foreach my $item ( sort keys %all_class_properties ) {
        next if $printed{$item}++;

        my $prop_meta = $class_meta->property_meta_for_name($item);
        next if $prop_meta->is_delegated;  # Not interested in those delegated properties

        my $val = eval { $class->$item };
        if ($item =~ /^is_/) {
            $val = ($val ? "TRUE" : "FALSE");
        }
        next unless defined $val;
        printf("    %16s  %s\n", $item, $val);
    }

    
    my %id_properties = map { $_ => 1 } $class->all_id_property_names;
    my %printed_properties;
    my @properties = 
        sort { 
            defined($id_properties{$a}) cmp defined($id_properties{$b})
            ||
            $a->property_name cmp $b->property_name 
        } 
        grep { ! $printed_properties{$_->property_name}++ }
        $class->all_property_metas;

    print "\n  Properties:\n" if (@properties);
    foreach my $property ( @properties ) {
        my $nullable = $property->is_optional ? "NULLABLE" : "";
        my $column_name = $property->column_name ? $property->column_name : "(no column)";
        my $data_type_string;
        if (defined $property->data_type) {
            $data_type_string = $property->data_type . ( $property->data_length ? "(".$property->data_length.")" : "");
        } else {
            $data_type_string = "";
        }
        printf(" %2s %25s  %-25s %15s $nullable\n", 
               $id_properties{$property->property_name} ? "ID" : "  ",
               $property->property_name,
               $column_name,
               $data_type_string,
              );
    }


    my @relationships = UR::Object::Reference->get(class_name => $class->class_name);
    print "\n  Relationships:\n" if (@relationships);
    foreach my $rel ( @relationships ) {
        my $r_class_name = $rel->r_class_name;
        if (grep { $_ eq $r_class_name } @parent_class_names) {
            # inheritance is already shown
            next;
        }
        my @rel_detail;
        foreach my $rel_prop ( UR::Object::Reference::Property->get(tha_id => $rel->tha_id) ) {
            my $property_name = $rel_prop->property_name;
            my $r_property_name = $rel_prop->r_property_name;
            push @rel_detail, $rel->r_class_name . "->get($r_property_name => \$self->$property_name)";
        }

        printf("    %20s => %s\n", $rel->delegation_name, shift @rel_detail);
        while (@rel_detail) {
            print " "x28, shift @rel_detail,"\n";
        }
    }

    return 1;
}

1;
