#
#  Splat.pm
#  --make queries to the Splat database

package Mchange::Splat;

use strict;

use lib '/afs/.cs.wisc.edu/s/mchange-2.2/common/perl5';

use CSL::DBs;
use CSL::Pg;
use CSL::PlRPC_Krb5;
use DBI;
use Class::Phrasebook::SQL;
use Carp;
use IO::File;
use Mchange;

sub new {
  my ( $class, $error_handle ) = @_;
  
  print "Connecting to Splat Database...\n";
  my $dbh = DBI->connect( $CSL::DBs::csdb )
    or die $DBI::errstr;

  $dbh->{ChopBlanks} = 1;
  
  $dbh->begin_work;
  $dbh->do( "set search_path to splat, public" );
    
  my $pb = new Class::Phrasebook::SQL( undef, "./Mchange/splat.pbk" );
  $pb->place_holders_conatain_dollars( 1 );
  $pb->load( "EN" );
  return unless $pb;

  my $ref = { dbh => $dbh,
              eh => $error_handle,
              pb => $pb };
  
  my $ret = bless $ref, $class; 
  return $ret;
}

sub DESTROY {

  my $self = shift;
  $self->{dbh}->disconnect;

  foreach ( keys %{$self->{temp_files}} ) {
    unlink( $_ ) if -e $_;
  }
}

#
#  query_machine_attrib
#  --return the splat information about a machine
#

sub query_machine_attrib {

  my ( $self, $mname ) = @_;

  return $self->_run_query( "GET_ATTACHED_HOST_INFO", {cpuname => $mname} )->fetchrow_hashref;


  #use Data::Dumper;
  #print Dumper( \@_ );
}

#
#  attach_host
#  --attach a host to the specified jack
#

sub attach_host {
  
  my ( $self, $host, $jack, $switch_command ) = @_;

  return $self->{eh}->( "$host does not exist--cannot attach to $jack" )
    unless $self->check_exists( 'hostname', $host );

  return $self->{eh}->( "$host is not an fqdn--cannot attach to $jack" )
    unless $host =~ /\.wisc\.edu$/;

  return $self->{eh}->( "$host is not a valid machine name-- cannot attach to $jack" )
      if !Mchange->is_valid_mname( $host );
#  return $self->{eh}->( "$host contains invalid characters--cannot attach to $jack" )
#      if $host =~ /[\!\@\#\$\% ]/;

  $jack =~ /^(.*)-(.*)$/;

  return $self->{eh}->( "$jack does not exist--aborting attach" )
    unless $self->check_exists( 'jacknum', $1, $2 );
  
  system( '/s/lab/bin/splat', '-a', $host, $jack );
            
}

#
#  detach_host
#  --detach a host from the jack
#

sub detach_host {

  my ( $self, $host ) = @_;

  return $self->{eh}->( "$host does not exist--aborting detach" )
    unless $self->_run_query( "GET_ATTACHED_HOST_INFO", {cpuname => $host} )->fetchrow_hashref;

  print "Detaching host: $host\n";
  system( '/s/lab/bin/splat', '-d', $host );
  
}

#
#  check_exists
#  --see if the given 'foo' exists
#

sub check_exists {

  my ( $self, $foo, @values ) = @_;

  my $query;

  if ( $foo =~ /hostname/ ) {
    $query = $self->_run_query( 'CHECK_HOST', {hostname => $values[0]} );
  }
  elsif ( $foo =~ /jacknum/ ) {
    $query = $self->_run_query( 'CHECK_JACK', {idf => $values[0], jacknum => $values[1]} );
  }
  elsif ( $foo =~ /part/ ) {
    $query = $self->_run_query( 'CHECK_PART', {part => $values[0], interface => $values[1]} );
  }

  return 1 if $query->fetchrow_hashref;
  return undef;
  
}

#
#  update_jack
#  --update the jack information for a host
#

sub update_host {

  my ( $self, $host, $idf, $jack ) = @_;

  $self->detach_host( $host );
  $self->attach_host( $host, $idf, $jack );
  
}

#
#  update_name
#  --update the hostname in the splat database
#

sub update_name {

  my ( $self, $curname, $newname ) = @_;  

  #return ({err => "Detach host before updating $curname"} )
  #  if _run_query->( 'GET_ATTACHED_HOST_INFO', {cpuname => $curname } );

  #don't do anything if the name is not in splat
  return unless $self->check_exists( 'hostname', $curname );

  return $self->{eh}->( "$newname is not a valid machine name-- cannot change name." )
      if !Mchange->is_valid_mname( $newname );
  #return $self->{eh}->( "Invalid characters in $newname" )
  #    if $newname =~ /[\!\@\#\$\% ]/;

  return $self->{eh}->( "Cannot update $curname: $newname already exists in Splat" )
    if $self->check_exists( 'hostname', $newname );

  print 'Check if host needs detaching:\n';
  my $ret = $self->detach_host( $curname );
  
  $self->_run_query( 'UPDATE_HOSTNAME', {curname => $curname, newname => $newname} );
  
  return 0;
}

#
#  insert_part 
#  --insert a part into the splat database
#

sub insert_part {

  my ( $self, $part, $mac, $interface, $name ) = @_;

  $self->_run_query( 'INSERT_PART', {part => $part, mac => $mac, interface => $interface} );

  #need to add the hostname if it isn't in splat
  
  
  $self->_run_query( 'ADD_HOST', {name => $name, part => $part, interface => $interface} )
    unless $self->check_exists( 'hostname', $name );

  
  
}

#
#  check_port
#  --make sure the idf/jacknum combo exists
#

sub check_port {

  my ( $self, $port ) = @_;
  
  my ( $idf, $jack ) = split( '-', $port );

  return unless $idf && $jack;
  
  #print "$idf $jack $port\n";
  my $query = $self->_run_query( 'CHECK_PORT', {idf => $idf, jack => $jack} );
  my $ref = $query->fetchrow_hashref;
  
  return $ref->{room} if $ref;
  return undef;
}

#
#  get_room_data
#  --return a file to be used with Mchange::Menu
#

sub get_room_data {

  my ( $self, $type, $room, $idf ) = @_;

  my ($key, $query, $file);
  
  if ( $type =~ /^idf$/ ) {
    $key = 'idf';
    $query = $self->_run_query( 'GET_ROOM_IDF', {room => $room} );
  }
  elsif ( $type =~ /^jack$/ ) {
    $key = 'jacknum';

    $query = $self->_run_query( 'GET_ROOM_JACK', {room => $room} );

  }
  elsif ( $type =~ /^jack-idf$/ ) {
    $key = 'jacknum';
    $query = $self->_run_query( 'GET_JACK_FROM_ROOM_IDF', {room => $room, idf => $idf} );
  }

  my @strings;
  
  while ( my $ref = $query->fetchrow_hashref ) {

    my $descstr;
    
    if ( $key eq 'jacknum' && $idf ) {
      my $tquery = $self->_run_query( 'GET_ATTACHED_HOST', {idf => $idf, jacknum => $ref->{$key}} );
      my $hostref = $tquery->fetchrow_hashref;

      if ( $hostref ) {
        $descstr = "$ref->{$key} ($hostref->{hostname})";
      }
      else {
        $descstr = "$ref->{$key} (Available)";
      }
      
    }
    else {
      $descstr = "$ref->{$key}";
    }

    push @strings, $descstr . "=$ref->{$key}\n";
    
  }

  return \@strings;
}

#
#  commit
#  --commit to the database
#

sub commit_changes {

  my ( $self ) = @_;

  $self->{dbh}->commit;
}


#
#  _run_query
#  --run the given query from the phrasebook
#

sub _run_query {

  my ( $self, @pb_args ) = @_;

  my $query;
  eval {
    $query = $self->{dbh}->prepare( $self->{pb}->get( @pb_args ) );
    $query->execute;
  };
  confess $@ if $@;

  return $query;
}


1;
