#
#  Script.pm
#  --write out mchange scripts
#

package Mchange::Script;

use strict;


sub new {
  my ( $class, %params )  = @_;

  my $ref = { data => $params{data},
            };

  return bless $ref, $class;
}

#
#  write
#  --write out the script for later execution
#

sub write {

  my ( $self ) = @_;

  print "Creating Script....\n";
  
  return unless $self->_fix_data;
  $self->_open_file;
  $self->_write_header;
  $self->_write_name;
  $self->_write_parts;
  $self->_write_validos;
  $self->_write_attrib;
  $self->_write_office;
  $self->_write_splat;
  $self->_write_rt;
  $self->_write_footer;
  $self->_close_file;
  
}


#
#  write_and_run
#  --write a temporary script and execute immediately
#

sub write_and_run {

  my ( $self ) = @_;

  $self->write;

  print "Running script: $self->{file}\n";
  
  #now execute the script
  system( "$ENV{HOME}/$self->{file}" );
  
}

###################################################

#
#  _fix_host_data
#  --fix the data for a single host
#

sub _fix_host_data {

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

  if ( $self->{checked}{$curname} ) {
    print "Error--cycle detected in script...exiting...\n";
    exit( 0 );
  }

  $self->{checked}{$curname} = 1;

  #recurse down to get the last possible name change
  $self->_fix_host_data( $newname, $self->{data}{names}{$newname} )
    if $self->{data}{names}{$newname};

  #
  # do the actual fixing
  #

  if ( $curname eq $newname ) {
    delete $self->{data}{names}{$curname};
    return;
  }
    
  #update where a part is going based on a name change
  while( my ($key, $value) = each( %{$self->{data}{parts}} ) ) {
    if ( $curname eq $value->{machine} ) {
      $value->{machine} = $newname;
    }
  }

  #update the attribute list to reflect the name change
  while ( my ($mname, $attrib) = each( %{$self->{data}{attrib}} ) ) {
    next unless $mname eq $curname;
    
    $self->{data}{attrib}{$newname} = $attrib;
    delete $self->{data}{attrib}{$curname};

  }

  #update any validos based on name changes
  while ( my ($mname, $os_list) = each( %{$self->{data}{validos}} ) ) {
    next unless $mname eq $curname;

    $self->{data}{validos}{$newname} = $os_list;
    delete $self->{data}{validos}{$curname};
  }

  #check to see if we have any redundant detaches
  while ( my ($mname, $val) = each( %{$self->{data}{splat_detach}} ) ) {
    while ( my ($a_name, $jackinfo) = each( %{$self->{data}{splat_attach}} ) ) {
      delete $self->{data}{splat_detach}{$mname} if $mname eq $a_name;
      
    }
  }
  
  #update splat attaches/detaches based on name changes
  while ( my ($mname, $jackinfo) = each( %{$self->{data}{splat_attach}} ) ) {
    next unless $mname eq $curname;

    $self->{data}{splat_attach}{$newname} = $jackinfo;
    delete $self->{data}{splat_attach}{$curname};
  }
  
  while ( my ($mname, $dns_info) = each( %{$self->{data}{dns_add}} ) ) {
    next unless $mname eq $curname;

    my $attrib = $self->{data}{attrib}{$newname};

    #update the dns info if changed
    $dns_info->{room} = $attrib->{room} || $dns_info->{room};

    $self->{data}{dns_add}{$newname} = $dns_info;
    delete $self->{data}{dns_add}{$mname};

  }
  
  

}

#
#  _fix_data
#  --fix the inconsistencies with the data
#

sub _fix_data {

  my ( $self ) = @_;

  #we need to go through these in a certain order if there is more
  #than a single machine involved in an mchange
  while ( my ( $curname, $newname ) = each( %{$self->{data}{names}} ) ) {
    next if $self->{checked}{$curname};
    $self->_fix_host_data( $curname, $newname );
  }

  #
  #  update non-name critical things
  #
    
  #update all the descriptions of things to escape single quotes
  while ( my ($invnum, $info) = each( %{$self->{data}{parts}} ) ) {
    my $desc = $info->mdesc;
    $desc =~ s/\'/\\\'/g;
    $info->mdesc( $desc );
  }
  
  #check and see if there is something to write about
  unless ( keys %{$self->{data}{attrib}} or keys %{$self->{data}{parts}} or
           keys %{$self->{data}{names}} or keys %{$self->{data}{validos}} or
           keys %{$self->{data}{splat_names}} or keys %{$self->{data}{splat_add}} or
           keys %{$self->{data}{splat_attach}} ) {
    print "NO SCRIPT WRITTEN--no information needs changing\n";
    return;
  }

  return 1;
}

#
#  _open_file
#  --figure out the filename if one is not given
#

sub _open_file {

  my ( $self ) = @_;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime(time);
  $year += 1900;
  $mon += 1;
  $mon = "0$mon" if $mon < 10;
  $mday = "0$mday" if $mday < 10;
  $sec = "0$sec" if $sec < 10;
  
  $self->{file} = "mc-$self->{data}{machine}-$year-$mon-$mday\_$hour:$min:$sec";
#  while ( -e "$ENV{HOME}/$self->{file}" ) {
#    $self->{file} =~ /,(\d+)/;
#    my $num = $1 + 1;
#    $self->{file} =~ s/,(\d+)/,$num/;
#  }
  
  open MC, "> $ENV{HOME}/$self->{file}" or die "Can't open temp file for mchange script\n"; 
}

#
#  _write_header
#  --set up the header
#

sub _write_header {

  my ( $self ) = @_;
  
  print MC <<__END__
#!/s/std/bin/perl
#
#  mchange v0.2.0 output script
#

use strict;
    
use lib '/s/mchange-2.2/common/perl5';
use Mchange::CSDB;
use Mchange::Splat;
use Mchange::RT;
use Mchange::Error;
use Tie::IxHash;
use Socket;

__END__
  ;
  
#see if we need to be on the lab network
$self->_write_check_network;

  print MC <<__END__
my \$err = new Mchange::Error;
my \$eh = sub { \$err->text_err( \@_ ) };
my \$csdb = new Mchange::CSDB( \$eh );
my \$splatdb = new Mchange::Splat( \$eh );
  
__END__
    ;

  my $ref = $self->{data}{names};
  while ( my ( $key, $value ) = each( %$ref ) ) {
    print MC "#$key is changing names to \'$value\'\n";
  }
  
  $ref = $self->{data}{parts};
  while ( my ( $key, $value ) = each( %$ref ) ) {
    print MC "#$key is moving to $value->{machine}\n";
  }

  print MC "\n";
  
  $ref = $self->{data}{attrib};
  while ( my ($key, $value) = each( %$ref ) ) {
    while ( my ($k, $v) = each( %$value ) ) {
      print MC "#$key\'s $k is being changed to \'$v\'\n";
    }
  }

  $ref = $self->{data}{validos};
  while ( my ($key, $value) = each( %$ref ) ) {
    foreach ( @$value ) {
      print MC "#$key is adding \'$_\' as a valid OS\n";
    }
  }

  $ref = $self->{data}{office};
  while ( my ($key, $value) = each( %$ref ) ) {
    print MC "#$key\'s office is begin changed to \'$value\'\n";
  }
  
  print MC "\n\n";
}


#
#  _write_check_dns
#  --if needed, check if a machine name is in dns
#

sub _write_check_dns {

  my ($self) = @_;

  my @k = keys %{$self->{data}{dns_add}};
  return unless @k;

  print MC "my \@checks = qw/@k/;\n"; 
  print MC <<__END__

my \$done;
foreach my \$name ( \@checks ) {
  unless ( (gethostbyname(\$name))[0] eq \$name ) {
    print "\$name is not in DNS...Please rerun script once it has been added.\\n";
    \$done = 1;
  }
}

exit( 1 ) unless \$done;

__END__
;
    
}

#
#  _write_check_network
#  --see if we need to exec ourself on the 162 net for splat
#

sub _write_check_network {

  my ($self) = @_;

  return unless keys %{$self->{data}{splat_attach}} or keys %{$self->{data}{splat_detach}};
  
  print MC <<__END__
my \$myhost = `uname -n`;
chomp \$myhost;
\$myhost = gethostbyname( \$myhost );
\$myhost = inet_ntoa( \$myhost );
unless ( \$myhost =~ /^128\.105\.162\./ ) {
  print \"Not on the 162 net...connecting to cookie\\n\";
  print `ssh cookie $ENV{HOME}/$self->{file}`;
  exit;
}

__END__
;
    
}

#
#  _write_name
#  --setup any name changes
#

sub _write_name {

  my ( $self ) = @_;

  return unless keys %{$self->{data}{names}};

  #clear up our flags
  undef $self->{checked} if exists $self->{checked};
  $self->{first} = 1;
  
  #only dealing with non-collision names
  print MC "my \@names = ( \n";

  
  while ( my ( $curname, $newname ) = each( %{$self->{data}{names}} ) ) {
    next if $self->{checked}{$curname};
    $self->_write_host_name( $curname );
  }
  
#   my ($key, $value) = each( %{$self->{data}{names}} );
#   print MC "{ machine => \'$key\', attrib => 'cpuname', value => \'$value\' }";

#   while ( ($key, $value) = each( %{$self->{data}{names}} ) ) {
#     print MC ",\n{ machine => \'$key\', attrib => 'cpuname', value => \'$value\' }";
#   }
          
  print MC " );\n\n";

  #setup the query loop
  print MC "foreach my \$trans ( \@names ) {\n";

  if ($self->{data}{transfer} == 1) {
    print MC <<EOF;
  print "Transferring prime user from \$trans->{machine} to \$trans->{value}\\n";
  my \$pret = \$csdb->trans_prime( \$trans->{machine}, \$trans->{value} );

  die if \$pret eq "error";

EOF
  }

  print MC <<EOF;
  print "Changing names: \$trans->{machine} is becoming \$trans->{value}\\n";
  my \$ret = \$csdb->update_attrib( 'cpuname', \$trans->{machine}, \$trans->{value} );

  die if \$ret eq "error";

}

EOF
}

#
#  _write_host_name
#  --recursive helper of _write_name
#

sub _write_host_name {

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

  #see if we have made it to a leaf condition
  my $newname = $self->{data}{names}{$host};

  return unless $newname;
  
  $self->_write_host_name( $newname );

  #write out the name change
  if ( $self->{first} ) {
    undef $self->{first};
  }
  else {
    print MC ",\n"
  }

  print MC "{ machine => \'$host\', attrib => 'cpuname', value => \'$newname\' }";

  $self->{checked}{$host} = 1;
  
}


#
#  _write_parts
#  --set up part changes
#

sub _write_parts {

  my ( $self ) = @_;

  return unless keys %{$self->{data}{parts}};

  #make the array of part moves
  print MC "my \@parts = ( \n";
  #the first interation has no comma before it
  my ($key, $value) = each( %{$self->{data}{parts}} );
  print MC "{ machine => \'$value->{machine}\', invnum => \'$key\', mdesc => \'" . $value->mdesc . "\' }";
  
  while ( ($key, $value) = each( %{$self->{data}{parts}} ) ) {
    print MC ",\n{ machine => \'$value->{machine}\', invnum => \'$key\', mdesc => \'" . $value->mdesc . "\' }";
  }

  print MC " );\n";
  
  print MC <<__END__

foreach my \$trans ( \@parts ) {
  print "Moving \$trans->{invnum} ( \$trans->{mdesc} ) to \$trans->{machine}\\n";
  my \$ret = \$csdb->update_part( \$trans->{machine}, \$trans->{invnum} );

  die if \$ret eq "error";
}

__END__
    ;
  
  
}

#
#  _write_validos
#  --set up validos updates
#

sub _write_validos {

  my ( $self ) = @_;

  return unless keys %{$self->{data}{validos}};

  print MC "my \@validos = ( \n";
  my ($mname, $os_list) = each( %{$self->{data}{validos}} );
  my $first = shift @$os_list;
  print MC "{ machine => \'$mname\', attrib => \'validos\', value => \'$first\' }";
  foreach ( @$os_list ) {
    print MC ",\n{ machine => \'$mname\', attrib => \'validos\', value => \'$_\' }";
  }

  #add the rest of the valdios updates
  while ( ($mname, $os_list) = each( %{$self->{data}{validos}} ) ) {
    foreach ( @$os_list ) {
      print MC ",\n{ machine => \'$mname\', attrib => \'validos\', value => \'$_\' }";
    }
  }
  
  print MC " );\n";

  #write the query loop
   print MC <<__END__

foreach my \$trans ( \@validos ) {
  print "Adding \$trans->{value} as a valid OS for \$trans->{machine}\\n";
  my \$ret = \$csdb->update_attrib( \$trans->{attrib}, \$trans->{machine}, \$trans->{value} );

  die if \$ret eq "error";
}


__END__
    ;
  
  
}

#
#  _write_attrib
#  --set up attrib changes
#

sub _write_attrib {

  my ( $self ) = @_;
  
  return unless keys %{$self->{data}{attrib}};

  #make the array of attribute changes
  print MC "my \@attrib = ( \n";
  my ($key, $value) = each ( %{$self->{data}{attrib}} );
  my ($k, $v) = each( %$value );
  print MC "{ machine => \'$key\', attrib => \'$k\', value => \'$v\' }";

  #finish out the first machine
  while ( ($k, $v) = each( %$value ) ) {
    print MC ",\n{ machine => \'$key\', attrib => \'$k\', value => \'$v\' }";
  }

  #add the rest of the machines into the array
  while ( ($key, $value) = each( %{$self->{data}{attrib}} ) ) {
    while ( ($k, $v) = each( %$value ) ) {
      print MC ",\n{ machine => \'$key\', attrib => \'$k\', value => \'$v\' }";
    }
  }

  print MC " );\n";

  #now write the query loop
  print MC <<__END__

foreach my \$trans ( \@attrib ) {
  print "Setting \$trans->{attrib} to \$trans->{value} for \$trans->{machine}\\n";
  my \$ret = \$csdb->update_attrib( \$trans->{attrib}, \$trans->{machine}, \$trans->{value} );

  die if \$ret eq "error";
}


__END__
    ;
}

#
#  _write_office
#  --setup office changes
#

sub _write_office {

  my ( $self ) = @_;
  
  return unless keys %{$self->{data}{office}};

  #make the array of attribute changes
  print MC "my \@office = ( \n";
  my ($user, $office) = each ( %{$self->{data}{office}} );
  print MC "{ user => \'$user\', office => \'$office\' }";

  #finish out the first machine
  while ( ($user, $office) = each( %{$self->{data}{office}} ) ) {
    print MC ",\n{ user => \'$user\', office => \'$office\' }"; 
  }

  print MC " );\n";

  #now write the query loop
  print MC <<__END__

foreach my \$trans ( \@office ) {
  print "Setting \$trans->{user}\\\'s office to \$trans->{office}\\n";
    my \$ret = \$csdb->update_office( \$trans->{user}, \$trans->{office} );

  die if \$ret eq "error";
}

__END__

}

#
#  _write_splat
#  --setup splat changes
#

sub _write_splat {

  my ( $self ) = @_;

  $self->_write_splat_add if keys %{$self->{data}{splat_add}};
  $self->_write_splat_detach if keys %{$self->{data}{splat_detach}};
  $self->_write_splat_names if keys %{$self->{data}{splat_names}};
  
  #commit the changes so attaches can work
  print MC <<__END__;
print "Commiting changes to splat database\\n";
\$splatdb->commit_changes;

__END__

# -u doesn't exist
#system("/s/lab/bin/splat -u");

  $self->_write_splat_attach if keys %{$self->{data}{splat_attach}};
  
}


#
#  _write_splat_add
#  --insert a new ethernet device into splatdb
#

sub _write_splat_add {

  my ( $self ) = @_;
  
  print MC "my \@splat_add = ( \n";
  my ( $key, $value ) = each( %{$self->{data}{splat_add}} );
  print MC "{ part => \'$key\', mac => \'$value->{mac}\', int => \'$value->{int}\', name => \'$value->{name}\' }"; 

  while ( ($key, $value) = each( %{$self->{data}{splat_add}} ) ) {
    print MC ",\n{ part => \'$key\', mac => \'$value->{mac}\', int => \'$value->{int}\', name => \'$value->{name}\' }"; 
  }

  print MC " );\n";

  #now write the query loop

  print MC <<__END__

foreach my \$trans ( \@splat_add ) {
  print "Adding new part to splat: \$trans->{part} MAC \$trans->{mac} Interface: \$trans->{int}\\n";
  my \$ret = \$splatdb->insert_part( \$trans->{part}, \$trans->{mac}, \$trans->{int}, \$trans->{name} );

  die if \$ret eq "error";
}

__END__
  ;
}

#
#  _write_splat_names
#  -update a hostname in splatdb
#

sub _write_splat_names {

  my ( $self ) = @_;

  return unless keys %{$self->{data}{splat_names}};
  
  #setup the checked hash
  undef $self->{checked} if exists $self->{checked};
  $self->{first} = 1;
  
  #write the data array
  print MC "my \@splat_names = ( \n";

  foreach my $curname ( keys %{$self->{data}{splat_names}} ) {
    next if $self->{checked}{$curname};
    $self->_write_splat_host_name( $curname );
  }
  
  print MC " );\n";

  #now for the query loop

  print MC <<__END__

foreach my \$trans ( \@splat_names ) {
  print "Updating name in splatdb: \$trans->{curname} is becoming \$trans->{newname}\\n";
  my \$ret = \$splatdb->update_name( \$trans->{curname}, \$trans->{newname} );

  die if \$ret eq "error";
}

__END__
  ;
  
}

#
#  _write_splat_host_name
#  --recurisive helper for _write_splat_names
#

sub _write_splat_host_name {

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

  my $newname = $self->{data}{splat_names}{$host};

  return unless $newname;

  $self->_write_splat_host_name( $newname );

  #we need to treat the first time a little special
  if ( $self->{first} ) {
    undef $self->{first};
  }
  else {
    print MC ",\n";
  }

  print MC "{ curname => \'$host\', newname => \'$newname\' }";

  #we might see this host again in the main loop, so mark it as checked
  $self->{checked}{$host} = 1;
}


#
#  _write_splat_detach
#  --detach the hosts
#

sub _write_splat_detach {

  my ( $self ) = @_;

  #write the data array 
  print MC "my \@splat_detach = ( \n";
  my ( $hostname, $jack ) = each( %{$self->{data}{splat_detach}} );
  print MC "{ hostname => \'$hostname\' }";

  while ( ( $hostname, $jack ) = each( %{$self->{data}{splat_detach}} ) ) {
    print MC ",\n{ hostname => \'$hostname\' }";
  }

  print MC " );\n\n";

  print MC <<__END__
foreach my \$trans ( \@splat_detach ) {
  print "Detaching \$trans->{hostname}\\n";
  my \$ret = \$splatdb->detach_host( \$trans->{hostname} );

  #die if \$ret eq "error";

}
  
__END__
  ;

}

#
#  _write_splat_attach
#  --attach the hosts
#

sub _write_splat_attach {

  my ( $self ) = @_;

  #write the data array
  print MC "my \@splat_attach = ( \n";
  my ( $hostname, $jack ) = each( %{$self->{data}{splat_attach}} );
  print MC "{ hostname => \'$hostname\', jack => \'$jack\' }";

  while ( ( $hostname, $jack ) = each( %{$self->{data}{splat_attach}} ) ) {
    print MC ",\n{ hostname => \'$hostname\', jack => \'$jack\' }";
  }

  print MC " );\n\n";

  print MC <<__END__
foreach my \$trans ( \@splat_attach ) {
  print "Attaching \$trans->{hostname} to jack \$trans->{jack}\\n";
  my \$ret = \$splatdb->attach_host( \$trans->{hostname}, \$trans->{jack} );

  #die if \$ret eq "error";

}
  
__END__
  ;
  
}

#
#  _write_rt
#  --setup the rt
#

sub _write_rt {

  my ( $self ) = @_;

  print MC "my \$data;\n";
  print MC "tie \%\$data, \"Tie::IxHash\";\n";
  print MC "\$data = { ";


  my @list = ( 'names', 'parts', 'validos', 'attrib', 'office', 'splat_add', 'splat_names', 'splat_attach' );

  #add the first name to the list
  while ( @list ) {
    my $name = shift @list;
    if ( keys %{$self->{data}{$name}} ) {
      print MC "$name => \\@" . $name;
      last;
    }
  }

  map { print MC ", $_ => \\@" . $_ if keys %{$self->{data}{$_}} } @list;
  
  print MC " };\n\n";
  my $munged = $self->{data}{rt};
  # heh
  $munged =~ s/\{/\\{/g;
  $munged =~ s/\}/\\}/g;
  print MC "my \$mcrt = new Mchange::RT( parent_rt => qq{$munged}, machine => \'$self->{data}{machine}\' );\n";
  #print MC "my \$mcrt = new Mchange::RT( parent_rt => \'$self->{data}{rt}\', machine => \'$self->{data}{machine}\' );\n";
  print MC "\$mcrt->send( \$data, 'invent' );\n";
  
}

#
#  _write_footer
#  --take care of any cleanup issues we have
#
 
sub _write_footer {

  my ( $self ) = @_;

  print MC <<__END__

print "Commiting changes to inventory database\\n";
\$csdb->commit_changes;
  
rename \'$ENV{HOME}/$self->{file}\', \'$ENV{HOME}/COMPLETED-$self->{file}\';
chmod( 0666, \'$ENV{HOME}/COMPLETED-$self->{file}\' );  
print \"Renamed $self->{file} to COMPLETED-$self->{file}\\n\";

#tell the user to look at WHAT
print \"\\n\\n\";
print \"================================================================================\\n";
print \"NOTICE: You may have to update WHAT b/c you updated the inventory database\\n";
print \"================================================================================\\n";
print \"\\n\\n\";
__END__
    ;
}

#
#  _close_file
#  --make the file ready to execute
#

sub _close_file {

  my ( $self ) = @_;

  close( MC );
  chmod( 0755, "$ENV{HOME}/$self->{file}" );

  print "Script is in your HOME directory: $self->{file}\n";

}

1;
