#!/usr/bin/perl
package sdevSeqCurationPage;

##################################################################
##### Author :	Shuai Weng
##### Date   :  Sept. 2002
##### Description : This package contains all necessary methods 
#####               for dictyBase curators to insert, delete or substitute 
#####               the nucleotide(s) in chromosome sequence.
#####              
##################################################################
use strict;
use DBI;
use CGI qw/:all :html3/;
# use CGI::Carp qw(fatalsToBrowser);
use lib "/usr/local/dicty/www_dictybase/db/lib/common";
use Login qw (ConnectToDatabase);
use TextUtil qw (DeleteUnwantedChar);
use lib "/usr/local/dicty/www_dictybase/db/lib/dictyBase";
use dictyBaseCentralMod qw(:formatPage :getInfo);

use lib "/usr/local/dicty/www_dictybase/db/lib/dictyBase/Objects";
use ConfigURLdictyBase;
use HtmlAlignment;
use Chromosome;
use Chromosome_archive;
use Chromosome_update;
use Feature;
use Feature_update;
use ClustalWlocal;
use Note;
use Note_link;
use GCG;

#######################################################################
#################### global variables #################################
#######################################################################

my $dbh;
my $dblink; 
my $configUrl;

#######################################################################
sub new {      ############ constructor ###############################
#######################################################################
       
	my ($self, %args) = @_;

	$self = {};
	bless $self;

      	$self->{'_database'} = $args{'database'};
	$self->{'_help'}     = $args{'help'};
	$self->{'_title'}    = defined($args{'title'}) ? 
	                       $args{'title'} : "Sequence Curation Page";
	$self->{'_user'}     = $args{'user'};

	$dbh = &ConnectToDatabase($self->database);
    	return $self;

}

#######################################################################
sub help { $_[0]->{_help} }
sub database { $_[0]->{_database} }
sub title { $_[0]->{_title} }
sub user { $_[0]->{_user} }

######################################################################
sub DESTROY {   ############ destructor ##############################
######################################################################
    	if (defined $dbh) {
		$dbh->disconnect;
    	}
}

######################################################################
sub start{
######################################################################

    	my ($self) = @_;
	$configUrl = ConfigURLdictyBase->new;	
	$dblink = $configUrl->dblink($self->database);
	if (!$self->user) {
	    print "location: ", $configUrl->dictyBaseCGIRoot, "$dblink/curatorLogin\n";
	    print "Content-type: text/html\n\n";
	    exit;
	}
	my ($dbuser, $dbpasswd) = &getUsernamePassword(uc($self->user), 
						       $self->database);
	if (!$dbuser || !$dbpasswd) {
	    print "location: ", $configUrl->dictyBaseCGIRoot, "$dblink/curatorLogin\n";
	    print "Content-type: text/html\n\n";
	    exit;
	}
       
	if (param('commit')) {
	    $dbh->disconnect;
	    $dbh = &ConnectToDatabase($self->database, $dbuser, $dbpasswd);
	    $self->commitInfo;
	}
	elsif (param('confirm')) {
	    $self->displayConfirmationPage;
	}
        else {
	    $self->printEntryForm(param('rowNum')+5);
        }
}

#######################################################################
sub printEntryForm {
#######################################################################
        my ($self, $rowNum) = @_;

	&printStartPage($self->database, $self->title, $self->help);

	print p, b(font({-color=>'red'}, "Talk to Kara or Anand before using this interface."));

	print p, b("This page allows curators to insert, delete or substitute nucleotide(s) in a given chromosome sequence."),p;

	my %chrLabel = ('0'=>'-chr-',
			'1'=>'I', '2'=>'II', '3'=>'III', 
			'4'=>'IV', '5'=>'V', '6'=>'VI', 
			'7'=>'VII', '8'=>'VIII', '9'=>'IX', 
			'10'=>'X', '11'=>'XI', '12'=>'XII',
			'13'=>'XIII', '14'=>'XIV', '15'=>'XV',
			'16'=>'XVI', '17'=>'Mito');

	my @chr;
	for (my $i = 0; $i <= 17; $i++) {
	    push(@chr, $i);
	}

	print table({-align=>'center'},
		    Tr(td(start_form)).
		    Tr(td({-bgcolor=>"#a4abc2",
			   -width=>150,
			   -align=>'CENTER',
			   -colspan=>'2'},
			  font({size=>'+1'}, 
			       b("Choose chromosome: "))).
		       td(popup_menu(-name=>'chr',
				     -values=>\@chr,
				     -labels=>\%chrLabel))).
		    Tr(td({-colspan=>'4'}, hr)).
		    $self->_rows($rowNum).
		    Tr(td(submit(-name=>'confirm',
				 -value=>'Submit')).
		       td(submit(-name=>'morerows',
				 -value=>'More Rows')).
		       td(reset(-name=>'Clear').
			  hidden(-name=>'user',
				 -value=>$self->user).
			  hidden(-name=>'rowNum',
				 -value=>$rowNum).
			  end_form)));

	&printEndPage;
}


########################################################################
sub displayConfirmationPage {
########################################################################
    my ($self) = @_;
    
    if (!param('chr')) {
	$self->_err_report("Please choose chromosome name before press 'Submit' button.");
	exit;
    }
    
    &printStartPage($self->database, $self->title, $self->help);

    $self->_initChrSeqInfo;

    print start_form,
          hidden(-name=>'chr', 
		 -value=>param('chr'));
    my %seq4feat;
    my %featStart; 
    my %featStop;
    my %insertAction;
    my %deleteAction;
    my %substituteAction;
    my @intergenicRegionAction;
    my $found;
    for (my $i = 1; $i <= 50; $i++) {

	if (!param("insertAfterCoord$i") && 
	    !param("deleteFromCoord$i") && 
	    !param("substituteFromCoord$i") ) { 
	    last; 
	}
	
	$found++;

	##### insertion
	my ($insertAfterCoord, $insertSeq) = 
	    $self->_processInsertVariables($i);
	if ($insertAfterCoord) {

	    print hidden(-name=>"insertAfterCoord$i",
			 -value=>$insertAfterCoord),
                  hidden(-name=>"insertSeq$i",
			 -value=>$insertSeq),
	          hidden(-name=>"insertNote$i",
			 -value=>param("insertNote$i"));
           			 
	    $self->checkForInsertion($insertAfterCoord, $insertSeq, 
				     \%featStart, \%featStop, \%seq4feat,
				     \%insertAction, 
				     \@intergenicRegionAction);
	    
	}

	##### deletion
	my ($deleteFromCoord, $deleteToCoord) = 
	    $self->_processDeleteVariables($i);
	
	if ($deleteFromCoord) {
	    print hidden(-name=>"deleteFromCoord$i",
			 -value=>$deleteFromCoord),
                  hidden(-name=>"deleteToCoord$i",
			 -value=>$deleteToCoord),
	          hidden(-name=>"deleteNote$i",
			 -value=>param("deleteNote$i"));
           			     
	    $self->checkForDeletion($deleteFromCoord, $deleteToCoord,
				    \%featStart, \%featStop, \%seq4feat,
				    \%deleteAction,
				    \@intergenicRegionAction);
	}

	##### substitution
	my ($substituteFromCoord, $substituteToCoord, $substituteSeq) = 
	    $self->_processSubstituteVariables($i);
	
	if ($substituteFromCoord) {
	    print hidden(-name=>"substituteFromCoord$i",
			 -value=>$substituteFromCoord),
                  hidden(-name=>"substituteToCoord$i",
			 -value=>$substituteToCoord),
	          hidden(-name=>"substituteSeq$i",
			 -value=>$substituteSeq),
	          hidden(-name=>"substituteNote$i",
			 -value=>param("substituteNote$i"));
           			     
	    $self->checkForSubstitution($substituteFromCoord, 
					$substituteToCoord,
					$substituteSeq, \%featStart, 
					\%featStop, \%seq4feat, 
					\%substituteAction,
					\@intergenicRegionAction);
	}
    }

    if (!$found) {

	print "Please enter what you want to update in the Insertion/Deletion/Substitution boxes before press the 'Submit' button.",p;

	&printEndPage;

	exit;

    }


    $self->printConfirmationInfo(\%featStart, \%featStop, \%seq4feat, 
				 \%insertAction, \%deleteAction,
				 \%substituteAction,
				 \@intergenicRegionAction);

    print hidden(-name=>'user',
		 -value=>$self->user).
	  submit(-name=>'commit',
		 -value=>'Commit').end_form;

    &printEndPage;

}

##################################################################
sub commitInfo {
##################################################################
    my ($self) = shift;
   
    &printStartPage($self->database, $self->title, $self->help);

    my $chrObj = $self->_initChrSeqInfo;

    my (%featStartCoordDB, %featStopCoordDB);

    $self->_setFeatCoordDB(\%featStartCoordDB, \%featStopCoordDB);

    my $featArrayRef = Feature->GetFeatureTableArrayRefByChrBegEnd(
				    dbh=>$dbh,
				    chr=>param('chr'),
				    beg=>1,
				    end=>$self->{'_chr_length'});


    my %actionCoord;
    for (my $i = 1; $i <= 50; $i++) {

	if (!param("insertAfterCoord$i") && 
	    !param("deleteFromCoord$i") && 
	    !param("substituteFromCoord$i") ) { 
	    last; 
	}
	
	##### insertion
	if (param("insertAfterCoord$i")) {
	    
	    my $key = "Insertion:".param("insertAfterCoord$i").":".param("insertSeq$i").":".param("insertNote$i");
	    $actionCoord{$key} = param("insertAfterCoord$i");
 
	}
	    
	##### deletion
	if (param("deleteFromCoord$i")) {
           			     
	    my $key = "Deletion:".param("deleteFromCoord$i").":".param("deleteToCoord$i").":".param("deleteNote$i");
	    $actionCoord{$key} = param("deleteFromCoord$i");

	}

	##### substitution
	if (param("substituteFromCoord$i")) {
           			     
	    my $key = "Substitution:".param("substituteFromCoord$i").":".
		param("substituteToCoord$i").":".param("substituteSeq$i").":".
		param("substituteNote$i");

	    $actionCoord{$key} = param("substituteFromCoord$i");

	}

    }

    $self->{'_curr_chr_seq'} = $self->{'_chr_seq'};

    ### set new chr sequence and down stream feature coords: 
    my (%featStartCoordNew, %featStopCoordNew); ## new coords for down 
                                                ## stream orfs
    foreach my $action (sort { $actionCoord{$b}<=>$actionCoord{$a} } keys %actionCoord) {  ### coords from bigger to smaller

	$self->_setNewChrSeq($action);

	$self->_setDownStreamFeatureCoord($action, 
					  \%featStartCoordNew, 
					  \%featStopCoordNew);

    }


    $self->{'_curr_chr_length'} = length($self->{'_curr_chr_seq'});
    $self->{'_curr_chr_version'} = Feature->GetSysDate(dbh=>$dbh);

    ##################### UPDATE DATABASE ##########################

    my $downStreamFeatureNo;
    my (%featStartCoordUpdate, %featStopCoordUpdate); 
                                       ## new coords for the 
                                       ## orfs with sequence 
                                       ## changed
    eval {
	######### update chromosome table
	print "Update chromosome table ...", p;
	$self->updateChromosomeTable($chrObj);

	######### update chromosome_archive table
	print "Update chromosome_archive table ...", p;
	my $chrArchiveNo = $self->insertChromosomeArchiveTable;

	print "chr_archive_no = $chrArchiveNo", p;

	######### update chromosome_update table
	######### set changed orf coords
	print "Update chromosome_update table ...", p;
	foreach my $action (sort { $actionCoord{$b}<=>$actionCoord{$a} } keys %actionCoord) {  

	    $self->_setChangedFeatureCoord(
				       $action, \%featStartCoordNew,
				       \%featStopCoordNew,
				       \%featStartCoordUpdate,
				       \%featStopCoordUpdate);

	    print "Insert info into chromosome_update table for $action", br;
	    $self->insertChromosomeUpdateTable($chrArchiveNo, $action);

	}

	########  update downstream feature coords
	print p, "Update feature and feature_update tables for downstream feature coords ...", p;
       
	foreach my $featNm (keys %featStartCoordNew) {
	    
	    if ($featStartCoordUpdate{$featNm}) { 
		### feature with sequence changed, 
		### will deal this separately
		next; 
	    }
	    
	    ### update feature table 
	    my ($featNo, $featVersion) = $self->updateFeatureTable($featNm, 
					        $featStartCoordNew{$featNm},
				                $featStopCoordNew{$featNm});
	    
	    if ($featNo) {
		### update feature_update table
		my $oldStart = $featStartCoordDB{$featNm};
		my $newStart = $featStartCoordNew{$featNm};
		my $oldStop = $featStopCoordDB{$featNm};
		my $newStop = $featStopCoordNew{$featNm};
		if ($oldStart == $newStart && $oldStop == $newStop) {
		    next;
		}
		if ($oldStart == $newStart) { undef $newStart; }
		if ($oldStop == $newStop) { undef $newStop; }
		$self->updateFeatureUpdateTable($featNo, $oldStart, $newStart, 
						$oldStop, $newStop, 
						$self->{'_chr_version'}, 
						$featVersion);
		$downStreamFeatureNo++;
	    }

	}

	######### update feature coords 
	print p, "Update feature and feature_update tables for affected features ...",p;

	
	foreach my $featNm (keys %featStartCoordUpdate) {
	    	    
	    ### update feature table 
	    my ($featNo, $featVersion) = $self->updateFeatureTable($featNm, 
					        $featStartCoordUpdate{$featNm},
				                $featStopCoordUpdate{$featNm});
	    
	    if ($featNo) {
		### update feature_update table
		my $oldStart = $featStartCoordDB{$featNm};
		my $newStart = $featStartCoordUpdate{$featNm};
		my $oldStop = $featStopCoordDB{$featNm};
		my $newStop = $featStopCoordUpdate{$featNm};
	#	if ($oldStart == $newStart && $oldStop == $newStop) {
	#	    next;
	#	}
		if ($oldStart == $newStart) { undef $newStart; }
		if ($oldStop == $newStop) { undef $newStop; }
		$self->updateFeatureUpdateTable($featNo, $oldStart, $newStart, 
						$oldStop, $newStop, 
						$self->{'_chr_version'}, 
						$featVersion);
	    }

	}

    };

    ######################### END UPDATE ################################

    if ($@) {

	$dbh->rollback;

	print "Error occurred when updating chromosome sequence for chr ".param('chr').". See following error message:".br.$@,p; 
	print "The database has been rollbacked.",p;

    }
    else {

	$dbh->commit;

#	print "The chr_seq and related info have been updated in chromosome table. The old chr_seq has been inserted into chromosome_archive table. The sequence update details have been inserted into chromosome_update table.",p;
#	print "The coordinates for $downStreamFeatureNo downstream features have been updated in feature table. $downStreamFeatureNo new feature_update entries have been inserted into database.", p;
  
    }
    
    ######################### Features with sequence changed ###########
    my @featNm = keys (%featStartCoordUpdate);
    if (@featNm) {
#	print hr, p, b("The chromosome sequence has been changed. ".font({-color=>"red"}, "You must")." now use the 'Update Feature Sequences and Coords' interface to update the coords (and individual sequence) for:"), p;
	print hr, p, b("The chromosome sequence has been changed. If it is necessary, you may use the 'Update Feature Sequences and Coords' interface to adjust the coords (and individual sequence) for:"), p;
    }	
    
    my $list;
    foreach my $featNm (keys %featStartCoordUpdate) {
	
	my $oldStart = $featStartCoordDB{$featNm};
	my $oldStop = $featStopCoordDB{$featNm};
	my $newStart = $featStartCoordUpdate{$featNm};
	my $newStop = $featStopCoordUpdate{$featNm};

	my $warning = $self->{'_warning4orf'}{$featNm};
	if ($warning) {
	    $warning = br.font({-color=>'red'}, "Note:")." $warning";
	}
	
	my $user = $self->user;

#	$list .= li(a({-href=>$configUrl->dictyBaseCGIRoot."$dblink/updateSeq.pl?user=$user&type=changeFeatureCoord",
#		       -target=>'infowin'}, 
#		      $featNm).br.
#		    "coordinates before the sequence change (still currently in the database):".br.
#		    "start_coord = $oldStart stop_coord = $oldStop".br.
#		    "coordinates with the sequence change (what you may want to input in the 'Update Feature Sequences and coords' interface):".br.
#		    "start_coord = $newStart stop_coord = $newStop".br.
#		    $warning);

	$list .= li(a({-href=>$configUrl->dictyBaseCGIRoot."$dblink/curation/updateSeq.pl?user=$user&type=changeFeatureCoord",
		       -target=>'infowin'}, 
		      $featNm).br.
		    "coordinates before the sequence change:".br.
		    "start_coord = $oldStart stop_coord = $oldStop".br.
		    "coordinates with the sequence change:".br.
		    "start_coord = $newStart stop_coord = $newStop".br.
		    $warning);
    }

    if ($list) {
	print ul($list),p;
    }

    my $dbname = $self->database;

    $dbname = "\U$dbname";

    print p, b("Return to ".a({-href=>$configUrl->dictyBaseCGIRoot."$dblink/curatorLogin?user=".$self->user}, "$dbname Curator Central")), br;
    ##################

    ##################
    &printEndPage;
    ##################
}


########################################################################
sub updateFeatureTable {
########################################################################
    my ($self, $featNm, $newStart, $newStop) = @_;
    
    my $featVersion = Feature->GetSysDate(dbh=>$dbh);

    my $featObj = Feature->new(dbh=>$dbh,
			       feature_name=>$featNm);
    $featObj->updateStart_coord($newStart);
    $featObj->updateStop_coord($newStop);
    $featObj->updateFeature_version($featVersion);
    $featObj->enterUpdates;
    
    return ($featObj->feature_no, $featVersion);

}

########################################################################
sub updateFeatureUpdateTable {
########################################################################
    my ($self, $featNo, $oldStart, $newStart, $oldStop, $newStop, 
	$chrVersion, $featVersion) = @_;

    Feature_update->Insert(dbh=>$dbh,
			   literals=>{feature_update_no=>'CGM_DDB.featupdno_seq.nextval'},
			   binds=>{feature_no=>$featNo,
			           old_start_coord=>$oldStart,
			           new_start_coord=>$newStart,
			           old_stop_coord=>$oldStop,
			           new_stop_coord=>$newStop,
			           chr_version=>$chrVersion,
			           feature_version=>$featVersion});

}

########################################################################
sub updateChromosomeTable {
########################################################################
    my ($self, $chrObj) = @_;
    
    $chrObj->updateChr_seq($self->{'_curr_chr_seq'});
    $chrObj->updateChr_version($self->{'_curr_chr_version'});
    $chrObj->updatePhysical_length($self->{'_curr_chr_length'});
    $chrObj->enterUpdates; 

}

########################################################################
sub insertChromosomeArchiveTable {
########################################################################
    my ($self) = @_;

    Chromosome_archive->Insert(dbh=>$dbh,
		  literals=>{chr_archive_no=>'CGM_DDB.chrarchiveno_seq.nextval'},
		  binds=>{chromosome=>param('chr'),
			  chr_seq_length=>$self->{'_chr_length'},
			  chr_version=>$self->{'_chr_version'},
			  chr_seq=>$self->{'_chr_seq'}});

    my $sth = $dbh->prepare("
        SELECT CGM_DDB.chrarchiveno_seq.currval
        FROM   dual
    ");
    $sth->execute;
    my $chrArchiveNo = $sth->fetchrow;
    return $chrArchiveNo;

}

########################################################################
sub insertChromosomeUpdateTable {
########################################################################
    my ($self, $chrArchiveNo, $action) = @_;
    
    my @action = split(/:/, $action);
    my $actionType = shift (@action);
    
    my ($start, $stop, $oldSeq, $newSeq, $note);
    if ($actionType =~ /^Insertion/i) {
	$start = shift(@action);
	$stop = $start;
	$newSeq = shift(@action);
	$note = join(':', @action); 
    }
    elsif ($actionType =~ /^Deletion/i) {
	$start = shift(@action);
	$stop = shift(@action);
	$oldSeq = substr($self->{'_chr_seq'}, $start-1, $stop-$start+1);
	$note = join(':', @action);
    }
    elsif ($actionType =~ /^Substitution/i) {
	$start = shift(@action);
	$stop = shift(@action);
	$newSeq = shift(@action);
	$oldSeq = substr($self->{'_chr_seq'}, $start-1, $stop-$start+1);
	$note = join(':', @action);
    }
    Chromosome_update->Insert(dbh=>$dbh,
		  literals=>{chr_update_no=>'CGM_DDB.chrupdateno_seq.nextval'},
		  binds=>{chr_archive_no=>$chrArchiveNo,
		          chr_update_type=>$actionType,
		          start_coord=>$start,
		          stop_coord=>$stop,
		          old_seq=>$oldSeq,
		          new_seq=>$newSeq});

    if ($note) {
	my $chrUpdateObj = Chromosome_update->new(dbh=>$dbh,
					      chr_archive_no=>$chrArchiveNo,
					      chr_update_type=>$actionType,
					      start_coord=>$start,
					      stop_coord=>$stop);

	if ($chrUpdateObj) {
	    my $chrUpdateNo = $chrUpdateObj->chr_update_no;
	    my $noteObj = Note->new(dbh=>$dbh,
				    note=>$note);
	    if (!$noteObj) {
		Note->Insert(dbh=>$dbh,
			     literals=>{note_no=>'CGM_DDB.noteno_seq.nextval'},
			     binds=>{note=>$note});
		$noteObj = Note->new(dbh=>$dbh,
				     note=>$note);
	    }
	    if ($noteObj) {
		Note_link->Insert(dbh=>$dbh,
				  literals=>{note_link_no=>'CGM_DDB.notelinkno_seq.nextval'},
				  binds=>{note_no=>$noteObj->note_no,
				          tab_name=>'CHROMOSOME_UPDATE',
				          primary_key=>$chrUpdateNo});
	    }
	}
    }
}

########################################################################
sub _initChrSeqInfo {
########################################################################
    my ($self) = @_;

    my $chrObj = Chromosome->new(dbh=>$dbh,
				 chromosome=>param('chr'));

    $self->{'_chr_seq'} = $chrObj->chr_seq;

    $self->{'_chr_length'} = $chrObj->physical_length;
    
    $self->{'_chr_version'} = $chrObj->chr_version;

    return $chrObj;
}

########################################################################
sub _setFeatCoordDB {
########################################################################
    my ($self, $featStartCoordDBHashRef, $featStopCoordDBHashRef) = @_;

    my $featArrayRef = Feature->GetFeatureTableArrayRefByChr(
				    dbh=>$dbh,
				    chromosome=>param('chr'));

    foreach my $rowRef (@$featArrayRef) {
	my ($featNm, $start, $stop, $strand, $others) = @$rowRef;
	$$featStartCoordDBHashRef{$featNm} = $start;
	$$featStopCoordDBHashRef{$featNm} = $stop;
    }

}

########################################################################
sub _setNewChrSeq {
########################################################################
    my ($self, $action) = @_;

    my @action = split(/:/, $action);
    my $actionType = shift (@action);

    my $newSeq;
    if ($actionType =~ /^Insertion/i) {
	my $coord = $action[0];
	my $seq = $action[1];
	$newSeq = substr($self->{'_curr_chr_seq'}, 0, $coord);
	$newSeq .= $seq;
	$newSeq .= substr($self->{'_curr_chr_seq'}, $coord);
    }
    elsif ($actionType =~ /^Deletion/i) {
	my $coord1 = $action[0];
	my $coord2 = $action[1];
	$newSeq = substr($self->{'_curr_chr_seq'}, 0, $coord1-1);
	$newSeq .= substr($self->{'_curr_chr_seq'}, $coord2);
    }
    elsif ($actionType =~ /^Substitution/i) {
	my $coord1 = $action[0];
	my $coord2 = $action[1];
	my $seq = $action[2];
	$newSeq = substr($self->{'_curr_chr_seq'}, 0, $coord1-1);
	$newSeq .= $seq;
	$newSeq .= substr($self->{'_curr_chr_seq'}, $coord2);
    }
    else {
	return; 
    }
    $self->{'_curr_chr_seq'} = $newSeq;

}

########################################################################
sub _setDownStreamFeatureCoord {
########################################################################
    my ($self, $action, $featStartCoordNewHashRef,  
	$featStopCoordNewHashRef) = @_;

    my @action = split(/:/, $action);
    my $actionType = shift (@action);

    my ($coord, $coordChanged);
    if ($actionType =~ /^Insertion/i) {
	$coord = $action[0];
	$coordChanged = length($action[1]);
    }
    elsif ($actionType =~ /^Deletion/i) {
	$coord = $action[1];
	$coordChanged = $action[0] - $action[1] - 1;
    }
    elsif ($actionType =~ /^Substitution/i) {
	$coord = $action[1];
	$coordChanged = $action[0] - $action[1] - 1 + length($action[2]);
    }
    else {
	return; 
    }
    my $downStreamFeatArrayRef = 
	Feature->GetDownStreamFeatureArrayRefForCoord(dbh=>$dbh,
						      chr=>param('chr'),
						      coord=>$coord);
    foreach my $rowRef (@$downStreamFeatArrayRef) {
	my ($featNm, $start, $stop) = @$rowRef;
	if (!$$featStartCoordNewHashRef{$featNm}) {
	    $$featStartCoordNewHashRef{$featNm} = $start;
	}
	if (!$$featStopCoordNewHashRef{$featNm}) {
	    $$featStopCoordNewHashRef{$featNm} = $stop;
	}
	$$featStartCoordNewHashRef{$featNm} += $coordChanged;
	$$featStopCoordNewHashRef{$featNm} += $coordChanged;
    }

}

########################################################################
sub _setChangedFeatureCoord {
########################################################################
    my ($self, $action, $featStartCoordNewHashRef, 
	$featStopCoordNewHashRef, $featStartCoordUpdateHashRef,
	$featStopCoordUpdateHashRef) = @_;

    my @action = split(/:/, $action);
    my $actionType = shift (@action);

    my $coordChanged;
    if ($actionType =~ /^Insertion/i) {

	my $insertAfterCoord = $action[0];
	$coordChanged = length($action[1]);
	
	my $featArrayRef = Feature->GetFeatureArrayRefForCoord(dbh=>$dbh,
					 chr=>param('chr'),
					 coord=>$insertAfterCoord);

	foreach my $rowRef (@$featArrayRef) {

	    my ($featNm, $start, $stop) = @$rowRef;

	    my $newStart = $$featStartCoordUpdateHashRef{$featNm} ||
		           $$featStartCoordNewHashRef{$featNm} ||
			   $start;

	    my $newStop = $$featStopCoordUpdateHashRef{$featNm} ||
		          $$featStopCoordNewHashRef{$featNm} ||
			  $stop;

	    if ($newStart < $newStop) {
		$newStop += $coordChanged;
	    }
	    else {
		$newStart += $coordChanged;
	    }
	    $$featStartCoordUpdateHashRef{$featNm} = $newStart;
	    $$featStopCoordUpdateHashRef{$featNm} = $newStop;
	}

    }
    elsif ($actionType =~ /^Deletion/i) {

	my $coord1 = $action[0];
	my $coord2 = $action[1];

	my $coordChanged = $coord1 - $coord2 - 1;

	my $featArrayRef = Feature->GetFeatureArrayRefForCoords(dbh=>$dbh,
					 chr=>param('chr'),
					 coord1=>$coord1,
					 coord2=>$coord2);
 
	foreach my $rowRef (@$featArrayRef) {
	 
	    my ($featNm, $start, $stop) = @$rowRef;

       	    if ($start < $stop && $coord2 > $stop) {
		$coordChanged = $coord1 - $stop - 1;
	    }
	    elsif ($start > $stop && $coord2 > $start) {
		$coordChanged = $coord1 - $start - 1;
	    }

	    my $newStart = $$featStartCoordUpdateHashRef{$featNm} ||
		           $$featStartCoordNewHashRef{$featNm} ||
			   $start;

	    my $newStop = $$featStopCoordUpdateHashRef{$featNm} ||
		          $$featStopCoordNewHashRef{$featNm} ||
			  $stop;

	    if ($newStart < $newStop) {
		$newStop += $coordChanged;
	    }
	    else {
		$newStart += $coordChanged;
	    }
	    $$featStartCoordUpdateHashRef{$featNm} = $newStart;
	    $$featStopCoordUpdateHashRef{$featNm} = $newStop;
	}
	
    }
    elsif ($actionType =~ /^Substitution/i) {
	
	my $coord1 = $action[0];
	my $coord2 = $action[1];

	my $coordChanged = $coord1 - $coord2 - 1 + length($action[2]);

	my $featArrayRef = Feature->GetFeatureArrayRefForCoords(dbh=>$dbh,
					 chr=>param('chr'),
					 coord1=>$coord1,
					 coord2=>$coord2);
 
	foreach my $rowRef (@$featArrayRef) {
	 
	    my ($featNm, $start, $stop) = @$rowRef;

       	    if ( ($start < $stop && $coord2 > $stop) ||
		 ($start > $stop && $coord2 > $start)) {
		$coordChanged = 0;
		$self->{'_warning4orf'}{$featNm} .= "you have changed a section of the sequence that spans the end of an ORF as well as an intergenic region. The stop coordinate of the ORF is currently the same as it was previously. Use the 'change coordinates of a feature' interface to adjust the coordinate.".br; 
	    }

	    my $newStart = $$featStartCoordUpdateHashRef{$featNm} ||
		           $$featStartCoordNewHashRef{$featNm} ||
			   $start;

	    my $newStop = $$featStopCoordUpdateHashRef{$featNm} ||
		          $$featStopCoordNewHashRef{$featNm} ||
			  $stop;

	    if ($newStart < $newStop) {
		$newStop += $coordChanged;
	    }
	    else {
		$newStart += $coordChanged;
	    }
	    $$featStartCoordUpdateHashRef{$featNm} = $newStart;
	    $$featStopCoordUpdateHashRef{$featNm} = $newStop;
	}
    }
   
}

########################################################################
sub printConfirmationInfo {
########################################################################
    my ($self, $featStartHashRef, $featStopHashRef, $seq4featHashRef, 
	$insertActionHashRef, $deleteActionHashRef, 
	$substituteActionHashRef, $intergenicRegionArrayRef) = @_;
    
    my %colorHash = ('*'=>'yellow',
		     ':'=>'pink',
		     '.'=>'lightgreen');

    foreach my $featNm (sort keys (%$seq4featHashRef)) {

	my $start = $$featStartHashRef{$featNm};
	my $stop = $$featStopHashRef{$featNm};

	($start, $stop) = ($stop, $start) if ($start > $stop); 
	
	print b(font({-color=>'red'},
		   $featNm.": start_coord = ".$$featStartHashRef{$featNm}.
		   ", stop_coord = ".$$featStopHashRef{$featNm})), br; 

	my $currSeq = $$seq4featHashRef{$featNm};

	my @insertAction = split(/\t/, $$insertActionHashRef{$featNm});

	my @actionCoord;
	foreach my $insertAction (@insertAction) {

	    my ($insertAfterCoord, $insertSeq) = split(/:/, $insertAction);
	    
	    print font({-color=>'red'},
		       "Insert '$insertSeq' from coord $insertAfterCoord"),br;

	    $insertAfterCoord = $insertAfterCoord - $start + 1; 

	    my $coord = $self->adjustedCoord($insertAfterCoord, 
					     \@actionCoord);

	    push(@actionCoord, $insertAfterCoord.":".length($insertSeq));

	    $currSeq = $self->createNewSeqForInsertion($currSeq, 
						       $coord, 
						       $insertSeq);
	}

	my @deleteAction = split(/\t/, $$deleteActionHashRef{$featNm});
	foreach my $deleteAction (@deleteAction) {

	    my ($deleteFromCoord, $deleteToCoord) 
		= split(/:/, $deleteAction);

	    print font({-color=>'red'}, 
		       "Delete from coord $deleteFromCoord to $deleteToCoord"),br; 

	    $deleteFromCoord = $deleteFromCoord - $start + 1;
	    $deleteToCoord = $deleteToCoord - $start + 1;

	    my $numDeleted = $deleteToCoord - $deleteFromCoord + 1;

	    my $coord1 = $self->adjustedCoord($deleteFromCoord, 
					     \@actionCoord);

	    my $coord2 = $coord1 + $numDeleted - 1;

	    push(@actionCoord, $deleteToCoord.":-".$numDeleted);

	    $currSeq = $self->createNewSeqForSubstitution($currSeq, 
					            $coord1, $coord2);

	}

	my @substituteAction = split(/\t/, $$substituteActionHashRef{$featNm});
	foreach my $substituteAction (@substituteAction) {
	    
	    my ($substituteFromCoord, $substituteToCoord, $substituteSeq) =
		split(/:/, $substituteAction);
	    
	    print font({-color=>'red'},
		       "Substitute from coord $substituteFromCoord to $substituteToCoord with sequence '$substituteSeq'"), br;

	    $substituteFromCoord = $substituteFromCoord - $start + 1;
	    $substituteToCoord = $substituteToCoord - $start + 1;

	    my $numChanged = length($substituteSeq) - 
		($substituteToCoord - $substituteFromCoord + 1);

	    my $coord1 = $self->adjustedCoord($substituteFromCoord, 
					     \@actionCoord);

	    my $coord2 = $coord1 + ($substituteToCoord-$substituteFromCoord);

	    push(@actionCoord, $substituteToCoord.":".$numChanged);

	    $currSeq = $self->createNewSeqForSubstitution($currSeq, 
					            $coord1, $coord2,
					            $substituteSeq);

	}

	##### create clustalW alignment file
	my @orf = ("OLD_$featNm", "NEW_$featNm");
	my %seq4orf;
	$seq4orf{"OLD_$featNm"} = $$seq4featHashRef{$featNm};
	$seq4orf{"NEW_$featNm"} = $currSeq;

	my $gcg = GCG->new(sequence=>$seq4orf{"NEW_$featNm"},
			   map=>'rmap');
	my $seqfile = $gcg->getSequence; 

	print a({-href=>$configUrl->dictyBaseCGIRoot."$dblink/seqTools?id=$$&map=rmap"}, "View 6 frame translation of the new sequence"),p;


	my $clustalw = ClustalWlocal->new(seqIdArrayRef=>\@orf,
					  seqHashRef=>\%seq4orf);
   
	my $alignfile = $clustalw->alignmentFile;

	$self->showAlign($alignfile, \%colorHash);

	print hr;
    }

    foreach my $intergenicRegion (@$intergenicRegionArrayRef) {
	print b(font({-color=>'red'},
		     "Intergenic Region changes:")), br;
	print $intergenicRegion, p;

	my @intergenicRegion = split(/:/, $intergenicRegion);

	my $action = shift @intergenicRegion;

	my ($newChrSeq, $coord, $extraBP);
	
	my $oldChrSeq = $self->{'_chr_seq'};
	
	if ($action =~ /^insertion/i) {

	    $coord = $intergenicRegion[0];
	    my $seq = $intergenicRegion[1];
   
	    $newChrSeq = substr($oldChrSeq, 0, $coord).
		         $seq.substr($oldChrSeq, $coord); 
	    $extraBP = length($seq);
   
	}
	elsif ($action =~ /^deletion/i) {
	    $coord = $intergenicRegion[0];
	    my $coord2 = $intergenicRegion[1];

	    $newChrSeq = substr($oldChrSeq, 0, $coord-1).
		         substr($oldChrSeq, $coord2);

	    $extraBP = 0 - ($coord2 - $coord + 1);
	    
	}
	elsif ($action =~ /^substitution/i) {
	    $coord = $intergenicRegion[0];
	    my $coord2 = $intergenicRegion[1];
	    my $seq = $intergenicRegion[2];

	    $newChrSeq = substr($oldChrSeq, 0, $coord-1).$seq.
		         substr($oldChrSeq, $coord2);

	    $extraBP = length($seq) - ($coord2 - $coord + 1);

	}
	    
	my $start = $coord - 90;

	my $oldShowLen = 180;
	my $newShowLen = 180;

	if ($extraBP > 0) {
	    
	    $oldShowLen -= $extraBP;

	}
	else {

	    $newShowLen += $extraBP;

	}

	if ($start < 1) { $start = 1; }

	my @interGR = ("OLD_IntergenicRegion", "NEW_IntergenicRegion");
	my %seq4interGR;
	$seq4interGR{"OLD_IntergenicRegion"} 
	          = substr($oldChrSeq, $start, $oldShowLen);
	$seq4interGR{"NEW_IntergenicRegion"} 
	          = substr($newChrSeq, $start, $newShowLen);

	my $clustalw = ClustalWlocal->new(seqIdArrayRef=>\@interGR,
					  seqHashRef=>\%seq4interGR);
   
	my $alignfile = $clustalw->alignmentFile;

	$self->showAlign($alignfile, \%colorHash);

    }


}

########################################################################
sub showAlign {
########################################################################
    my ($self, $alignfile, $colorHashRef) = @_;

    open(IN, "$alignfile") ||
	die "Can't open '$alignfile' for reading:$!", br;
		
    my @list;
    while(<IN>) {
	chomp;
	if (/clustal/i || (/^$/ && !@list)) { next; }
	if (/^$/i && @list) {
	        print br;
	        my $align = HtmlAlignment->new(listArrayRef=>\@list,
					       colorHashRef=>$colorHashRef);
	        $align->showAlignment;
	        undef @list;
	}
	else {
	        push(@list, $_);
	}	
    }
    if (@list) {
	my $align = HtmlAlignment->new(listArrayRef=>\@list,
				       colorHashRef=>$colorHashRef);
	$align->showAlignment;
    }
    close(IN);

}

########################################################################
sub createNewSeqForInsertion {
########################################################################
    my ($self, $currSeq, $insertAfterCoord, $insertSeq) = @_;

    my $newSeq = substr($currSeq, 0, $insertAfterCoord);
    $newSeq .= $insertSeq;
    $newSeq .= substr($currSeq, $insertAfterCoord);

    return $newSeq;
}

########################################################################
sub createNewSeqForSubstitution {
########################################################################
    my ($self, $currSeq, $coord1, $coord2, $substituteSeq) = @_;

    my $newSeq = substr($currSeq, 0, $coord1-1);
    $newSeq .= $substituteSeq;
    $newSeq .= substr($currSeq, $coord2);

    return $newSeq;

}

########################################################################
sub adjustedCoord {
########################################################################
    my ($self, $origCoord, $actionCoordArrayRef) = @_;

    foreach my $coordEntry (@$actionCoordArrayRef) {
	my ($coord, $adjustNum) = split(/:/, $coordEntry);

	### work on here more 
	if ($coord < $origCoord) {
	    $origCoord += $adjustNum;
	}
    }
    return $origCoord;

}

########################################################################
sub checkForInsertion {
########################################################################
    my ($self, $insertAfterCoord, $insertSeq, $featStartHashRef, 
	$featStopHashRef, $seq4featHashRef, $insertActionHashRef,
	$intergenicRegionArrayRef) = @_;

    my $featArrayRef = Feature->GetFeatureArrayRefForCoord(dbh=>$dbh,
					 chr=>param('chr'),
					 coord=>$insertAfterCoord);
    my $found;
    foreach my $rowRef (@$featArrayRef) {
	my ($featNm, $start, $stop) = @$rowRef;
	if (!$$seq4featHashRef{$featNm}) {
	    $$featStartHashRef{$featNm} = $start;
	    $$featStopHashRef{$featNm} = $stop;
	    ($start, $stop) = ($stop, $start) if ($start > $stop);
	    $$seq4featHashRef{$featNm} = substr($self->{'_chr_seq'},
						$start-1, 
						$stop-$start+1);
	}
	if ($$insertActionHashRef{$featNm}) {
	    $$insertActionHashRef{$featNm} .= "\t";
	}
	$$insertActionHashRef{$featNm} .= $insertAfterCoord.":".$insertSeq;

	$found++;
    }
    
    if (!$found) {
	push(@$intergenicRegionArrayRef, "insertion:${insertAfterCoord}:${insertSeq}");
    }     
}

######### update down stream feature coords for insertion
## UPDATE CGM_DDB.feature 
## SET    start_coord = start_coord + 2,
##        stop_coord = stop_coord + 2
## WHERE  chromosome = 6
## AND    (  (start_coord < stop_coord and 
##            start_coord > 500) or
##           (start_coord > stop_coord and
##            stop_coord > 500))


###################################################################
sub checkForDeletion {
###################################################################
    my ($self, $deleteFromCoord, $deleteToCoord, $featStartHashRef, 
	$featStopHashRef, $seq4featHashRef, $deleteActionHashRef,
	$intergenicRegionArrayRef) = @_;

    my $featArrayRef = Feature->GetFeatureArrayRefForCoords(dbh=>$dbh,
					 chr=>param('chr'),
					 coord1=>$deleteFromCoord,
					 coord2=>$deleteToCoord);
 
    my $found;
    foreach my $rowRef (@$featArrayRef) {
	my ($featNm, $start, $stop) = @$rowRef;
	if (!$$seq4featHashRef{$featNm}) {
	    $$featStartHashRef{$featNm} = $start;
	    $$featStopHashRef{$featNm} = $stop;
	    ($start, $stop) = ($stop, $start) if ($start > $stop);
	    $$seq4featHashRef{$featNm} = substr($self->{'_chr_seq'},
						$start-1, 
						$stop-$start+1);
	}
	if ($$deleteActionHashRef{$featNm}) {
	    $$deleteActionHashRef{$featNm} .= "\t";
	}
	$$deleteActionHashRef{$featNm} .= $deleteFromCoord.":".$deleteToCoord;

	$found++;
    }
    
    if (!$found) {
	push(@$intergenicRegionArrayRef, "deletion:${deleteFromCoord}:${deleteToCoord}");
    }     

}

###################################################################
sub checkForSubstitution {
###################################################################
    
    my ($self, $substituteFromCoord, $substituteToCoord, 
	$substituteSeq, $featStartHashRef, $featStopHashRef, 
	$seq4featHashRef, $substituteActionHashRef,
	$intergenicRegionArrayRef) = @_;

    my $featArrayRef = Feature->GetFeatureArrayRefForCoords(dbh=>$dbh,
					 chr=>param('chr'),
					 coord1=>$substituteFromCoord,
					 coord2=>$substituteToCoord);
 
    my $found;
    foreach my $rowRef (@$featArrayRef) {
	my ($featNm, $start, $stop) = @$rowRef;
	if (!$$seq4featHashRef{$featNm}) {
	    $$featStartHashRef{$featNm} = $start;
	    $$featStopHashRef{$featNm} = $stop;
	    ($start, $stop) = ($stop, $start) if ($start > $stop);
	    $$seq4featHashRef{$featNm} = substr($self->{'_chr_seq'},
						$start-1, 
						$stop-$start+1);
	}
	if ($$substituteActionHashRef{$featNm}) {
	    $$substituteActionHashRef{$featNm} .= "\t";
	}
	$$substituteActionHashRef{$featNm} .= $substituteFromCoord.":".$substituteToCoord.":".$substituteSeq;

	$found++;

    }
    
    if (!$found) {
	push(@$intergenicRegionArrayRef, "substitution:${substituteFromCoord}:${substituteToCoord}:$substituteSeq");
    }     

}

########################################################################
sub _processInsertVariables {
########################################################################
    my ($self, $i) = @_;
					 
    my $insertAfterCoord = param("insertAfterCoord$i");
    my $insertSeq = param("insertSeq$i");
    &DeleteUnwantedChar(\$insertAfterCoord);
    &DeleteUnwantedChar(\$insertSeq);
 
    if ($insertAfterCoord && $insertSeq) {
	if ($insertAfterCoord > $self->{'_chr_length'}) {
	    print "The coord you entered for insertion is greater than the chromosome size (".$self->{'_chr_length'}.") for chromosome ".param('chr').".", br;
	    exit;
	}
	if ($insertAfterCoord !~ /^[0-9]+$/) {
	    print "You have to enter a number for the 'Insert after chr coord' box.", br;
	    exit;
	}
	if ($insertSeq !~ /^[ATGC]+$/i) {
	    print "The insert sequence should only contain 'A', 'T', 'G', and 'C'.", br;
	    exit;
	}
	return ($insertAfterCoord, $insertSeq);
    }
    elsif ($insertAfterCoord || $insertSeq) {
	print "If you want to insert a sequence segment into a given chromosome, you have to enter coord into 'Insert after chr coord' box and enter sequence segment into 'insert this sequence' box.", br;
	exit;
    }
}

########################################################################
sub _processDeleteVariables {
########################################################################
    my ($self, $i) = @_;
    
    my $deleteFromCoord = param("deleteFromCoord$i");
    my $deleteToCoord = param("deleteToCoord$i");
    &DeleteUnwantedChar(\$deleteFromCoord);
    &DeleteUnwantedChar(\$deleteToCoord);
    
    if ($deleteFromCoord && $deleteToCoord) {
	if ($deleteFromCoord !~ /^[0-9]+$/ || 
	    $deleteToCoord  !~ /^[0-9]+$/) {
	    print "You have to enter a number for the 'Delete from chr coord' and 'to coord' boxes.", br;
	    exit;
	}
	if ($deleteFromCoord > $self->{'_chr_length'} || 
	    $deleteToCoord > $self->{'_chr_length'}) {
	    print "The coord you entered for deletion is greater than the chromosome size (".$self->{'_chr_length'}.") for chromosome ".param('chr').".", br;
	    exit;
	}
	if ($deleteFromCoord > $deleteToCoord) {
	    print "The start coord you entered for deletion is greater than the stop coord you entered. Please correct them and try again.", br;
	    exit;
	}
	return ($deleteFromCoord, $deleteToCoord);
    }
    elsif ($deleteFromCoord || $deleteToCoord) {
	print "If you want to delete a sequence segment in a given chromosome, you have to enter start and stop coords.", br;
	exit;
    }
}

########################################################################
sub _processSubstituteVariables {
########################################################################
    my ($self, $i) = @_;
    
    my $substituteFromCoord = param("substituteFromCoord$i");
    my $substituteToCoord = param("substituteToCoord$i");
    my $substituteSeq = param("substituteSeq$i");
    &DeleteUnwantedChar(\$substituteFromCoord);
    &DeleteUnwantedChar(\$substituteToCoord);
    
    if ($substituteFromCoord && $substituteToCoord) {
	if ($substituteFromCoord !~ /^[0-9]+$/ || 
	    $substituteToCoord  !~ /^[0-9]+$/) {
	    print "You have to enter a number for the 'Substitute from chr coord' and 'to coord' boxes.", br;
	    exit;
	}
	if ($substituteFromCoord > $self->{'_chr_length'} || 
	    $substituteToCoord > $self->{'_chr_length'}) {
	    print "The coord you entered for substitution is greater than the chromosome size (".$self->{'_chr_length'}.") for chromosome ".param('chr').".", br;
	    exit;
	}
	if ($substituteFromCoord > $substituteToCoord) {
	    print "The start coord you entered for substitution is greater than the stop coord you entered. Please correct them and try again.", br;
	    exit;
	}
	if ($substituteSeq && $substituteSeq !~ /^[ATGC]+$/i) {
	    print "The substitute sequence should only contain 'A', 'T', 'G', and 'C'.", br;
	    exit;
	}
	return ($substituteFromCoord, $substituteToCoord, $substituteSeq);

    }
    elsif ($substituteFromCoord || $substituteToCoord) {
	print "If you want to substitute a sequence segment in a given chromosome, you have to enter start and stop coords.", br;
	exit;
    }
}

########################################################################
sub _rows {
########################################################################
    my ($self, $rowNum) = @_;
    if (!$rowNum) { $rowNum = 5; } 
    my $rows;
    foreach my $i (1..$rowNum) {
	$rows .= $self->_oneRow($i).
	         Tr(td({-colspan=>'4'}, hr));
    }
    return $rows;
}

########################################################################
sub _oneRow {
########################################################################
    my ($self, $i) = @_;
    return Tr(td({-bgcolor=>"#a4abc2",
		  -width=>100,
		  -align=>'CENTER'},
		 "Insertion:").
	      td("Insert after chr coord:".br.
		 textfield(-name=>"insertAfterCoord$i",
			   -value=>param("insertAfterCoord$i"))).
	      td("insert this sequence:".br
		 textfield(-name=>"insertSeq$i",
			   -value=>param("insertSeq$i"))).
	      td("Note:".br.
	         textfield(-name=>"insertNote$i",
			   -value=>param("insertNote$i")))).
	   Tr(td({-bgcolor=>"#a4abc2",
		  -width=>100,
		  -align=>'CENTER'},
		 "Deletion:").
	      td("Delete from chr coord:".br.
		 textfield(-name=>"deleteFromCoord$i",
			   -value=>param("deleteFromCoord$i"))).
	      td("to coord:".br.
		 textfield(-name=>"deleteToCoord$i",
			   -value=>param("deleteToCoord$i"))).
	      td("Note:".br.
	         textfield(-name=>"deleteNote$i",
			   -value=>param("deleteNote$i")))).
           Tr(td({-bgcolor=>"#a4abc2",
		  -width=>100,
		  -align=>'CENTER'},
		 "Substitution:").
	      td("Substitute from chr coord:".br.
		 textfield(-name=>"substituteFromCoord$i",
			   -value=>param("substituteFromCoord$i")).br.
		 "With this sequence:".br.
		 textfield(-name=>"substituteSeq$i",
			   -value=>param("substituteSeq$i"))).
	      td({-valign=>'top'}, 
		 "to coord:".br.
	         textfield(-name=>"substituteToCoord$i",
			   -value=>param("substituteToCoord$i"))).
	      td({-valign=>'top'}, 
		 "Note:".br.
		 textfield(-name=>"substituteNote$i",
			   -value=>param("substituteNote$i"))));
}

########################################################################
sub _err_report {
########################################################################
    my ($self, $err) = @_;
 
    &printStartPage($self->database, $self->title, $self->help);
    
    print b($err);
    
    &printEndPage;

    if ($dbh) { $dbh->disconnect; }
    exit;
}

########################################################################
1;
########################################################################



















