<%doc>

-- Operations Worksheet -- 

Interface (similar to device worksheet) for viewing/updating
NOC relevant infomation.  This includes customer contacts, 
devices, sites, circuits.

</%doc>
%
%
<%args>
$id     => undef
$table  => undef
$search => undef
$_action => undef
$status => undef
$view   => "Basics"
$edit   => undef
$user   => $ui->get_current_user($r)
$only_show_available_contacts => 1
</%args>
%
%
<%attr>
title   => 'Operations' 
section => 'Operations'
</%attr>
%
%
<%init>
use Netdot::Operations;

my $DEBUG     = 0;
my $obj       = undef;
my $list;
my @tables;
my %results;
my %stables;
my $caller_args = $m->caller_args(-1);

@{ $list } = ();
if( exists( $caller_args->{page} ) ) {
    $user->setAttribute($r, "OPERATIONS_PAGE", $caller_args->{page});
}
my $page = $user->getAttribute("OPERATIONS_PAGE");

print '%ARGS is  <pre>', Dumper(%ARGS), '</pre><br>' if $DEBUG;


if( $search ) {

    @tables = qw( Connection Device Entity Person Site RR );
    if( defined( $search ) && $search =~ /\w+/ ) {

        # walk through a set of tables and search each column
        foreach my $tbl ( @tables ) {
            my @columns = $tbl->_essential();
            if( $tbl ne "Connection" && $tbl ne "RR" ) {
                push @columns, "aliases";
            }
            my %linksto = $ui->meta->get_links_to($tbl);
            foreach my $col ( @columns ) {
                if( $col eq "id" || $linksto{$col} ) {
                    next;
                } else {
		    $stables{$tbl}{$col} = 1;
                }
            }
        }
        foreach my $tbl ( keys %stables ) {
            foreach my $col ( keys %{ $stables{$tbl} } ) {
                my $q = "%" . $search . "%";
                map { 
                    $results{$tbl}{$_->id} = $_ 
                } $tbl->search_like( $col => $q );
            }
        }

    } elsif( defined( $search ) && $search !~ /\w+/ ) {

        print "<p><b>No search criteria</b>";
        $m->abort;

    } elsif( defined( $id ) && defined( $table ) ) {

        $obj = $table->retrieve($id);
        if( ! defined( $obj) ) {
            $m->comp( 'error.mhtml', error => "Could not retrieve $table with id $id" );
        }

    }

} elsif( $status ) {

    @tables = qw( BGPPeering Device Interface IpService );

} elsif( defined( $id ) && defined( $table ) ) {

     $obj = $table->retrieve($id);
     if( ! defined( $obj) ) {
         $m->comp( 'error.mhtml', error => "Could not retrieve $table with id $id" );
     }
}

if( %results ) {

    # Tables with entity column: Connection Device Person
    # Site must use EntitySite to look up the corresponding Entity
    foreach my $tbl ( keys %results ) {
        foreach my $id ( keys %{ $results{$tbl} } ) {
            if( $tbl eq "RR" ) {
                my $s = ( Device->search( name => $results{$tbl}{$id} ) )[0];
                if( $s ) {
		    $results{"Device"}{$s->id()} = $s;
                }
                delete( $results{$tbl}{$id} );
            }
        }
    }
    delete( $results{"RR"} );
    if( %results && scalar( keys %results ) == 1 ) {
        my $tbl = ( keys %results )[0];
        if( scalar( keys %{ $results{$tbl} } ) == 1 ) {
            undef( $search );
            $obj = ( values %{ $results{$tbl} } )[0];
	    $table = $tbl;
            $id = $obj->id();
        }
    }
    if( defined( $search ) ) {
        foreach my $tbl ( keys %results ) {
	    map {
                push @{ $list }, $results{$tbl}{$_}
            } keys %{ $results{$tbl} };
        }
    }

} elsif( $obj ) {

    ; # do nothing

} else {

    print  "<p><b>No results</b>";
    $m->abort;

}
</%init>
%
<%once>
my $names = 0;
my $dispatch = sub {
  # Each $table type will have specific ``call chains''
  # (e.g. Device->interfaces->nearcircuits->cid is the chain to circuit
  # ids for a device)
  my $key = shift;
  my %table = (
	       Device      => '.DeviceHandler',
#	       Interface   => '.InterfaceHandler',
	       Circuit     => '.CircuitHandler',
	       Entity      => '.EntityHandler',
#	       ContactList => '.ContactListHandler',
	       Site        => '.SiteHandler',
	       Person      => '.PersonHandler',
	      );
  die "Don't know how to dispatch \"$key\"s" unless exists $table{$key};
  return $table{$key};
};
</%once>

%
<div id="sectiondetail">
%
% if( $search ) {
%
<&| .MakeContainerOutside, 
    lhs => scalar( @{$list} ) . " results for <i>$search</i>" &>
%   foreach my $tbl ( sort keys %results ) {
%       my @list = values %{ $results{$tbl} };
            <&| .MakeContainer, lhs => $tbl, &>
                <& /generic/sortresults.mhtml, table => $tbl, object => \@list, view => "row", page => "operations.html", withedit => 1 &>
	    </&>
%   }
</&>
%
% } elsif( $obj ) { # Displaying a single thing
%
% my $lbl = $ui->getobjlabel($obj, ", ");
<div class="containeroutside">
   <div class="containerheadleftoutside">
       <% $table %>: <b><% $obj->view_link %></b>&nbsp;<br/>
       View:
%       if( $view eq "Basics" ) { # The default view
       [Basics]
%       } else {
       <a href="operations.html?id=<% $id %>&view=Basics">[Basics]</a>
%       }
    </div>
    <div class="containerheadrightoutside">
%       if( $table eq "Device" ) {
            <a href="device.html?id=<% $id %>&view=<% $view %>"> [view details]</a>&nbsp;
       	    <a href="deldevice.html?id=<% $id %>"> [delete]</a>&nbsp;<br/>
%       } else {
            <br/>
%       }
            <br/>
    </div>
    <div class="containerbodyoutside">
 
%        $m->comp( $dispatch->($table) , obj => $obj, only_show_available_contacts => $only_show_available_contacts); #, dispatch => $dispatch );

    </div>
</div>
%
% } elsif( $status ) {

    print  "<p><b>Not yet implemented.</b>";

% }
%
</div>


<%def .PersonHandler >
  <%args>
    $obj => 0
    $accumulate_contact_lists => 0
    $only_show_available_contacts
  </%args>
  <%init>
    my $contact_lists = [map {$_->contactlist} $obj->roles];
  </%init>
    <div><b><% $obj->table %>:</b><% $obj->view_link %></div>
    <div><b>Contact List:</b> <% map {$_->name} @$contact_lists %></div>

<&| .MakeContainer, lhs => "Devices @{[ $obj->label ]} is responsible for:" &>
% foreach my $device ($obj->devices) {
    <& .DeviceHandler, obj => $device,
       only_show_available_contacts => $only_show_available_contacts, 
       accumulate_contact_lists => $contact_lists &>
% }
</&>
<&| .MakeContainer, lhs => $obj->location->table &>
    <& .SiteHandler, obj => $obj->location, 
       only_show_available_contacts => $only_show_available_contacts, 
       accumulate_contact_lists => $contact_lists &>
</&>
    <&| .MakeContainer, lhs => $obj->entity->table &>
    <& .EntityHandler, obj => $obj->entity,
       only_show_available_contacts => $only_show_available_contacts, 
       accumulate_contact_lists => $contact_lists &>
    </&>
<& .DisplayAvailableContacts, contact_lists => $contact_lists,
   only_show_available_contacts => $only_show_available_contacts, obj => $obj &>
</%def>

<%def .EntityHandler >
  <%args>
    $obj => 0
    $accumulate_contact_lists => 0
    $only_show_available_contacts
  </%args>
  <%init>
    my $contact_lists = [$obj->contactlist];
  </%init>
    <div><b><% $obj->table %>:</b><% $obj->view_link %></div>
    <div><b>Contact List:</b> <% map {$_->name} @$contact_lists %></div>
% if (scalar $obj->sites) {
<&| .MakeContainer, lhs => "Sites" &>
% foreach my $site (map {$_->site} $obj->sites) {
    <& .SiteHandler, obj => $site, 
       only_show_available_contacts => $only_show_available_contacts, 
       accumulate_contact_lists => $contact_lists &>
% }
</&>
% }
% if (scalar $obj->devices) {
<&| .MakeContainer, lhs => "Devices" &>
% foreach my $device ($obj->devices) {
    <& .DeviceHandler, obj => $device, 
       only_show_available_contacts => $only_show_available_contacts, 
       accumulate_contact_lists => $contact_lists &>
% }
</&>
% }
% if (scalar $obj->all_circuits) {
    <&| .MakeContainer, lhs => "@{[$obj->table]} Circuits" &>
    <& .DisplayCircuits, obj => $obj,
       only_show_available_contacts => $only_show_available_contacts, 
       accumulate_contact_lists => $contact_lists &>
    </&>
% }
% if (ref $accumulate_contact_lists eq "ARRAY") {
%   @$accumulate_contact_lists = @$contact_lists;
% } else {
<& .DisplayAvailableContacts, contact_lists => $contact_lists,
   only_show_available_contacts => $only_show_available_contacts, obj => $obj &>
% }
</%def>

<%def .SiteHandler>
  <%args>
    $obj => 0
    $accumulate_contact_lists => 0
    $only_show_available_contacts
  </%args>
  <%init>
    #$m->comp('info', table => $obj->contactlist->table, id => $obj->contactlist->id);

# *** Still need the city (and contact list).
#    my $lhs = $obj->view_link . ':' . $obj->city . " " . $obj->state;
    my $contact_lists = [$obj->contactlist];
  </%init>
    <div><b>Site:</b><% $obj->view_link %></div>
    <div><b>Contact List:</b> <% map {$_->name} @$contact_lists %></div>
<&| '.MakeContainer', lhs => "Devices",  &>
% foreach my $device ($obj->devices) {
    <& .DeviceHandler, obj => $device, 
       only_show_available_contacts => $only_show_available_contacts, 
       accumulate_contact_lists => $contact_lists &>
% }
</&>
% if (ref $accumulate_contact_lists eq "ARRAY") {
%   @$accumulate_contact_lists = @$contact_lists;
% } else {
<& .DisplayAvailableContacts, contact_lists => $contact_lists,
   only_show_available_contacts => $only_show_available_contacts, obj => $obj &>
% }
</%def>

%# When you read calling code the `.' indicates it is a mason
%# subroutine (as opposed to a module from a file).
<%def .DeviceHandler>
  <%doc>
  </%doc>
  <%args>
    $obj => 0
    # If a list ref is passed for this parameter then, instead of
    # passing this devices contact lists to .DisplayAvailableContacts,
    # this devices contact lists will be pushed onto
    # @$accumulate_contact_lists.  This is used to prevent the same
    # contact list from being printed multiple times when showing the
    # operations page for something like a site which may have
    # multiple devices.
    $accumulate_contact_lists => 0
    $only_show_available_contacts
  </%args>
  <%init>
     my @contact_lists = map { $_->contactlist } $obj->contacts;     

     my $lhs = $obj->view_link . ':' . $obj->productname->name . " " . $obj->productname->type->name;
  </%init>

<&| '.MakeContainer', lhs => $lhs, 
    rhs => "<b>Contact List(s):</b> @{[map {$_->name } @contact_lists]}" &>
% if (scalar $obj->all_circuits != 0) {
   <&| '.MakeContainer', lhs => 'Device Circuits'  &>        
     <& .DisplayCircuits, obj => $obj &>
   </&>
% }
%# Display out of bound info if there is any.
% if ($obj->oobname ne "" or $obj->oobnumber ne "") {
    <&| '.MakeContainer', lhs => 'OOB Info' &>
	<& '/generic/attribute_table.mhtml', field_headers => ['oobname', 'oobnumber'],
	data => [$obj->oobname, $obj->oobnumber] &>
    </&>
% }
%# Display uplinks and downlinks.
<&| '.MakeContainer', lhs => "Interface Links",  &>
% foreach my $interface ($obj->interfaces) {
%# Are there any {up,down}links?
%   if (scalar ($interface->children, $interface->parents)) {
%#  allen-304-sw1 has multiple uplinks
    <& /generic/data_table.mhtml, field_headers => ['interface', 'uplinks', 'downlinks'],
    data => 
	[[
	 $interface->view_link,
	 $m->scomp('/generic/data_table.mhtml', 
		   data => [map {[$_->view_link]} $interface->parents]),
	 $m->scomp('/generic/data_table.mhtml', 
		   data => [map {[$_->view_link]} $interface->children]),
	 ]]
    &>
%   }
% }
</&>
% if (ref $accumulate_contact_lists eq "ARRAY") {
%	push @$accumulate_contact_lists, @contact_lists;
% } else {
    <& .DisplayAvailableContacts, contact_lists => \@contact_lists,
       obj => $obj, only_show_available_contacts=> $only_show_available_contacts &>
% } 
</&>
</%def>

<%def .DisplayCircuits>
  <%args>
    $obj
  </%args>
  <%init>
    # Added in Operations.pm
    my @circuits = $obj->all_circuits;
my (@field_headers, @data);
# This will fail if there aren't any circuits.
# Not any more.  Was getting table names from first object in @circuits.
@field_headers = ('Circuit', 'Connection', 'Entity');

foreach my $circuit (@circuits) {
    push @data, [$circuit->view_link, $circuit->connectionid->view_link, $circuit->vendor->view_link];
}

$m->comp('/generic/data_table.mhtml', field_headers => \@field_headers, data => \@data);
  </%init>
</%def>


%# Name should just be display contacts now that availability is an option
<%def .DisplayAvailableContacts>

  <%args>
    @contact_lists
    $obj
    # If true only available contacts are displayed
    $only_show_available_contacts=> 1
  </%args>
  <%init>
    # Need to ``uniqueify'' the contact lists since there might be
    # many repeats (most devices at the same site have the same
    # contact lists).  The below works because keys are unique.
    @contact_lists = values %{ { map {$_->name => $_} @contact_lists } };
    my @contacts = map { $_->contacts } @contact_lists;
    # Will fail if there aren't any contact_sets
    # Not any more.  Was getting table names from first object in @contacts.
    my @field_headers = ("Person",
			 "Contact List",
			 "Contact Info",);
    my @data;

    if ($only_show_available_contacts) {
	@contacts = Netdot::Operations::contactable_contacts(@contacts); 
	foreach my $contact_set (@contacts) {
	    my $contact = $contact_set->{contact};
	    push @data, [$contact->person->view_link,
			 $contact->contactlist->view_link,
			 $m->scomp('.ContactHandler', contact => $contact,
				   notification_modes => $contact_set->{availabilities})
			 ];
	}
    } else {
	foreach my $contact (@contacts) {
	    push @data, [$contact->person->view_link,
			 $contact->contactlist->view_link,
			 $m->scomp('.ContactHandler', contact => $contact)
			 ];
	}
    }

    my $current_set_of_contacts = $only_show_available_contacts? 'Available' : 'All';
    my $other_set_of_contacts = $only_show_available_contacts? 'All' : 'Available';
  </%init>
    <&| '.MakeContainer', lhs => "$current_set_of_contacts @{[ $obj->table ]} Contacts", 
        rhs => "<a href=\"operations.html?id=@{[ $obj->id ]}&table=@{[ $obj->table ]}&only_show_available_contacts=@{[ ! $only_show_available_contacts]}\">[$other_set_of_contacts @{[ $obj->table ]} Contacts]</a>" &>

      <& '/generic/data_table.mhtml', field_headers => \@field_headers, data => \@data &>
    </&>
</%def>

<%def .ContactHandler>
  <%args>
    $contact
    # Optionally specify which notification modes to display.  If none
    # are specified then all are shown.
    @notification_modes => ()
  </%args>
  <%init>

@notification_modes = qw/notify_voice notify_pager notify_email/
    if (scalar @notification_modes == 0); 
my $person = $contact->person;
    
# For generically processing voice, pager, and email data.
my $template = {
    notify_voice => {
        title => "Phone",
	fields => [qw/office cell home/],
    },
    notify_pager => {
        title => "Pager",
	fields => [qw/pager emailpager/],
    },
    notify_email => {
        title => "Email",
	fields => [qw/email/],
    },
};

my (@field_headers, @data);
foreach my $mode (@notification_modes) {
    my $values = $template->{$mode};

    push @field_headers, "<b>" . $values->{title} . "</b>";
    push @data, $contact->$mode->view_link;

    foreach my $type (@{ $values->{fields} }) {
	my %pair = $ui->form_field(object => $contact->person, column => $type);
	if ($pair{value} ne "") {
	    push @field_headers, $pair{label};
	    push @data, $pair{value};
	}
    }

}
$m->comp('/generic/attribute_table.mhtml', field_headers => \@field_headers, 
	 data => \@data);

  </%init>
</%def>

%# Could make a meta make container method as these methods are almost
%# identical.  Also, there is a containerhead(outside)? class which is
%# more appropriate for the case where no right side is specified.

<%def .MakeContainerOutside >
    <%args>
    $lhs
    $rhs => "&nbsp;"
    </%args>
%
  <div class="containeroutside">
    <div class="containerheadleftoutside">  <% $lhs %> </div>
    <div class="containerheadrightoutside"> <% $rhs %> </div>
    <div class="containerbodyoutside">
      <% $m->content %>
    </div>
  </div>
</%def>

<%def .MakeContainer >
  <%args>
    $lhs
    $rhs => "&nbsp;"
  </%args>
%
  <div class="container">
    <div class="containerheadleft">  <% $lhs %> </div>
    <div class="containerheadright"> <% $rhs %> </div>
    <div class="containerbody">
      <% $m->content %>
    </div>
  </div>
</%def>

%# Debugging

<%def info>
  <%args>
  $table
  $id
  </%args>
  <%init>
  sub pp {
    my $obj = shift;
    my $columns = shift || 'Essential';
    eval {
      my $s = $obj->pp($columns);
      $m->print("<pre>$s</pre>");
    };
  }

  use Data::Dumper;
my $obj = $table->retrieve($id);
my $indent = " "x4;

$m->print("<h3> ID $id, Table $table </h3>");
pp($obj, "All");

$m->print("<h3> Has A </h3>");
foreach my $key (keys %{ $obj->meta_info->{has_a} }) {
  eval{pp($obj->$key);};
  $m->print("no $key in the obj<br/>")
    if ($@);

}

$m->print("<h3> Has Many </h3>");
foreach my $key (keys %{ $obj->meta_info->{has_many} }) {
  eval {
    # Same as $obj->$key :)
    #my $accessor = $obj->meta_info->{has_many}->{$key}->{accessor};
    #$m->print($key . ": " . $obj->$accessor . "<br/>");
    $m->print("Has " . $obj->$key . " $key: <br/>");
    $m->print("<pre>");
    foreach my $foreign ($obj->$key) {
      $m->print("$indent" . $foreign->pp . "\n");
    }
    $m->print("</pre>");
  };
}


</%init>
  <h3> Meta Info Dump </h3>
  <pre><% Dumper($obj->meta_info()) %></pre>
</%def>
