/*****************************************************************************
#   Copyright (C) 1994-2008 by David Gordon.
#   All rights reserved.                           
#                                                                           
#   This software is part of a beta-test version of the Consed/Autofinish
#   package.  It should not be redistributed or
#   used for any commercial purpose, including commercially funded
#   sequencing, without written permission from the author and the
#   University of Washington.
#   
#   This software is provided ``AS IS'' and any express or implied
#   warranties, including, but not limited to, the implied warranties of
#   merchantability and fitness for a particular purpose, are disclaimed.
#   In no event shall the authors or the University of Washington be
#   liable for any direct, indirect, incidental, special, exemplary, or
#   consequential damages (including, but not limited to, procurement of
#   substitute goods or services; loss of use, data, or profits; or
#   business interruption) however caused and on any theory of liability,
#   whether in contract, strict liability, or tort (including negligence
#   or otherwise) arising in any way out of the use of this software, even
#   if advised of the possibility of such damage.
#
#   Building Consed from source is error prone and not simple which is
#   why I provide executables.  Due to time limitations I cannot
#   provide any assistance in building Consed.  Even if you do not
#   modify the source, you may introduce errors due to using a
#   different version of the compiler, a different version of motif,
#   different versions of other libraries than I used, etc.  For this
#   reason, if you discover Consed bugs, I can only offer help with
#   those bugs if you first reproduce those bugs with an executable
#   provided by me--not an executable you have built.
# 
#   Modifying Consed is also difficult.  Although Consed is modular,
#   some modules are used by many other modules.  Thus making a change
#   in one place can have unforeseen effects on many other features.
#   It may takes months for you to notice these other side-effects
#   which may not seen connected at all.  It is not feasable for me to
#   provide help with modifying Consed sources because of the
#   potentially huge amount of time involved.
#
#*****************************************************************************/
//
// consed.cpp
//
// implementation for ConsEd object
//




#include    <stdlib.h>
#include    <iostream.h>
#include    "rwcregexp.h"
#include    <math.h>


#include    "consed.h"
#include    "contigwin.h"
#include    "tedwin.h"
#include    "teditor.h"
#include    "mbt_exception.h"
#include    "guiapp.h"
#include    "editaction.h"
#include    "selection.h"
#include    "guiQuitTypes.h"
#include    "guiQuit.h"
#include    "textbox.h"
#include    "filePopupAndGetFilename.h"
#include    "please_wait.h"
#include    "guiTopWindow.h"
#include    "gui_search_for_string.h"
#include    "choose_contig.h"
#include    "applyEditHistory.h"
#include    "bEditHistoryFileExists.h"
#include    "bGuiGetAnswerYesNo.h"
#include    "deleteFileIfItExists.h"
#include    "compareContigs.h"
#include    "consed_version.h"
#include    "perhapsCreateSocketToControlConsed.h"
#include    "consedParameters.h"
#include    <unistd.h>
#include    "popupInfoOrErrorMessageNoFormat.h"
#include    "restrictionFragmentMobility.h"
#include    "filePopupAndGetAceFilename.h"
// without the below include, despite no compiler or linker error,
// the dtor of assemblyView would not be called by delete pAssemblyView_
// and thus the widgets would not be destroyed (but the assemblyView
// object would) so exposure events would cause a segmentation fault.
#include    "assemblyView.h"
#include    "guiMultiContigNavigator.h"
#include    "customNavigation.h"
#include    "popupErrorMessage.h"
#include    "bIsNumericMaybeWithWhitespace.h"



//
// static class members
//
ConsEd* ConsEd::pGlobalConsEd_ = 0;  // pointer to sole instance
bool ConsEd::bConsEdCreated_ = false;  // flag indicates instance exists

ConsEd :: ConsEd( ) 
   :
    pAssembly_(0),
    bDebug_( false ),
    bUsingPhdFiles_( true ),
    pChooseContig_( 0 ),
    //    pGuiMultiContigNavigator_( NULL ),
    pGuiSearchForString_( NULL ),
    pAssemblyView_( NULL )
{
   //
   // fail crudely if one already exists - we may not have
   // a gui at all yet
   //
   if (bConsEdCreated_) {
      cerr << "Fatal program error:  ConsEd ctor called twice." << endl;
      cerr.flush();
      exit(-1);
   }

   // save pointer to yourself
   pGlobalConsEd_ = this;

   // remember you have been created (allow no others)
   bConsEdCreated_ = true;

   // create the first ContigWin on startup
   // ContigWin ctor will automatically add itself to ConsEd's array
   // ContigWin* pNewContigWin = new ContigWin( );

   // new up a current selection
   pSelection_ = new selection();

   // load the default directories for the chromatigrams and phd files
   // These directories are chromat_dir and phd_dir respectively, unless
   // the environment variables CONSED_CHROMAT_DIR and CONSED_PHD_DIR
   // are set.


   char *pChromatDir = getenv( "CONSED_CHROMAT_DIR" );

   if ( pChromatDir ) 
     filChromatDir_ = pChromatDir;
   else
      filChromatDir_ = "../chromat_dir";

   char *pPHDDir = getenv( "CONSED_PHD_DIR" );
   if ( pPHDDir )
     filPHDDir_ = pPHDDir;
   else
     filPHDDir_ = "../phd_dir";
        
   char *pDebug = getenv( "CONSED_DEBUG" );

   if (pDebug) 
     bDebug_ = true;

}


// passed pointer to a derived class of EditAction
// tells it to go do itself.
void ConsEd :: doEditAction(EditAction* pEditAction, 
                            const bool bWriteToEditHistoryFile) {

   doEditAction2( pEditAction,
                  bWriteToEditHistoryFile,
                  true ); // bRefresh
}


void ConsEd :: doEditActionNoRefresh( EditAction* pEditAction,
                                      const bool bWriteToEditHistoryFile ) {

   doEditAction2( pEditAction,
                  bWriteToEditHistoryFile,
                  false ); // bRefresh
}



void ConsEd :: doEditAction2( EditAction* pEdAct, 
                              const bool bWriteToEditHistoryFile,
                              const bool bRefresh ) {

   try {
      pEdAct->doEdit( bWriteToEditHistoryFile );
   }
   catch (ExceptionBase eb) {
      // this is one of ours, report it if not done so
      if (! eb.bUserNotified() ) {
         GuiApp::popupErrorMessage(eb.szGetDesc());
         eb.setUserNotified();
         throw eb;  // rethrow modified exception
      }
   }
   catch(...) {
      throw;  // re-throw the exception (for now)
   }

   // update the edit history list
   dapEditHistory_.append(pEdAct);


   if ( bRefresh ) {

      // send refresh message to all ContigWins.  let EditAction 
      // virtual member funs for refreshing ContigWins and Teditors
      // decide what refreshing/redrawing is really needed
      for (int nContigWin = 0; 
        nContigWin < dapContigWin_.length(); 
        nContigWin++) {
         dapContigWin_[nContigWin]->editNotify(pEdAct);
      }
   }



}





// called when the XtApplication timeout says it's
// time to blink the cursor.  sends cursor draw message
// to all ContigWins.
void ConsEd :: blinkAllCursors( const bool bBlinkOn) {
   for (int nContigWin = 0; 
        nContigWin < dapContigWin_.length(); 
        nContigWin++) {

      dapContigWin_[nContigWin]->drawCursor(true);  // checks blink state
      dapContigWin_[nContigWin]->drawTeditorCursors(true); // ditto
   }

   for( int n = 0; n < aCompareContigs_.length(); ++n ) {
      aCompareContigs_[n]->blinkAllCursors( bBlinkOn );
   }

   if ( pAssemblyView_ ) {
      pAssemblyView_->drawClickedObjects( bBlinkOn );
   }

}



void ConsEd :: unsetAllCursors() {
   // important in the case of addNewReads since then the
   // old cursor position may no longer be valid, such
   // as if it is at the beginning of a read

   for (int nContigWin = 0; 
        nContigWin < dapContigWin_.length(); 
        nContigWin++) {
      dapContigWin_[nContigWin]->clearAllCursors();
   }
}





void ConsEd :: undoLastEditAction( const bool bWriteToEditHistoryFile ) {
   // pick up a pointer to the last edit action.
   EditAction* pEdActLast = dapEditHistory_.last();

   if ( ! pEdActLast ) {
      GuiApp :: popupErrorMessage(
          "Internal program error \nmore undo's than edits in ConsEd::undoLastEditAction \nPlease report to consed author" );
      exit( 0 );
   }

   try {
      pEdActLast->undoEdit( bWriteToEditHistoryFile );
   }
   catch (ExceptionBase eb) {
      // this is one of ours, report it if not done so
      if (! eb.bUserNotified() ) {
         GuiApp::popupErrorMessage(eb.szGetDesc());
         eb.setUserNotified();
         throw eb;  // rethrow modified exception
      }
   }
   catch(...) {
      throw;  // re-throw the exception (for now)
   }

   // update the edit history list by removing the last one
   dapEditHistory_.removeLast();

   // send refresh message to all ContigWins.  let EditAction 
   // virtual member funs for refreshing ContigWins and Teditors
   // decide what refreshing/redrawing is really needed
   for (int nContigWin = 0; 
        nContigWin < dapContigWin_.length(); 
        nContigWin++) {
      dapContigWin_[nContigWin]->editNotify(pEdActLast);
   }


}


// the ContigWin ctor tells the ConsEd that there's a new
// one to add to the list or an old one to delete.
// (list used for refresh notification, etc.)
void ConsEd :: addNewContigWin(ContigWin* pCw) {
   dapContigWin_.append(pCw);
}

void ConsEd :: removeContigWin(ContigWin* pCw) {
   size_t nIndex = dapContigWin_.index(pCw);
   assert (nIndex != RW_NPOS);
   dapContigWin_.removeAt(nIndex);
}


void ConsEd :: deleteAllContigWinsForContig( Contig* pContig ) {
   for( int nContigWin = dapContigWin_.length() - 1; nContigWin >= 0; --nContigWin ) {
      ContigWin* pContigWin = dapContigWin_[ nContigWin ];
      if ( pContigWin->pContig_ == pContig ) {
         delete pContigWin;
         // this will remove it from the list dapContigWin_
      }
   }
}


void ConsEd :: deleteAllCompareContigWindowsForContig( Contig* pContig ) {
   for( int n = (int) aCompareContigs_.length() - 1; n >= 0; --n ) {
      compareContigs* pCompareContigs = aCompareContigs_[ n ];
      
      if ( pCompareContigs->pContig1_ == pContig ||
           pCompareContigs->pContig2_ == pContig ) {
         delete pCompareContigs;
      }
   }
}




void ConsEd :: deleteAllContigWins() {

   // note:  this should not be called unless you are sure that all compare contig windows
   // are already down.  Otherwise, consed could crash.  Instead, use ConsEd::closeAllWindows

   for( int nContigWin = dapContigWin_.length() - 1; nContigWin >= 0; --nContigWin ) {
      ContigWin* pContigWin = dapContigWin_[ nContigWin ];
      delete pContigWin;
   }
}




//
// Purpose:  Finds the startup contigwin so that a contig can be put
//      in it
//
// What it does:  This searches through the array of contigwin's and returns
//          the first one that doesn't have an associated contig.
//

ContigWin*   ConsEd ::  pContigWinGetWithNoContig() {
   ContigWin*   pContigWin = NULL;

   int  n = 0;
   bool bFound = False;
   while( !bFound && n < dapContigWin_.length() ) {
      if ( dapContigWin_[n]->pGetContig() == NULL ) {
         bFound = True;
         pContigWin = dapContigWin_[n];
      }
      else
        ++n;
   }

   return( pContigWin );
}

   
ContigWin*   ConsEd     ::  pGetFirstContigWin() 
     { return( dapContigWin_[0] ); }


void ConsEd :: quitConsed() {

   if (! pAssemblyGet() ) exit(0);
   if (! pAssemblyGet()->bChanged() ||
       pCP->bReadOnly_ ) {

      // just so that people don't accidentally exit out of large
      // assemblies
      if ( pGetAssembly()->nGetNumberOfReadsInAssembly() > 
           pCP->nAskAgainIfWantToQuitConsedIfThisManyReads_ ) {
         if ( !bGuiGetAnswerYesNo( "Are you sure you want to quit? (y/n)" ) )
            return;
      }

      exit( 0 );
   }

   etypeQuit eWhatQuittingUserWantsToDo;

   guiQuit* pGuiQuit = 
     new guiQuit();
   
   pGuiQuit->popupAndGetAnswer( eWhatQuittingUserWantsToDo );
   delete pGuiQuit;
   
   
   if (eWhatQuittingUserWantsToDo == eCancel )
     return;
   else if (eWhatQuittingUserWantsToDo == eQuitWithoutSaving ) {
     deleteFileIfItExists( 
         ConsEd::pGetAssembly()->soGetAceFileName() + ".wrk" );
     exit(0);
   }
   else if (eWhatQuittingUserWantsToDo == eQuitWithoutSavingButSaveEditHistory )
     exit(0);
   else if (eWhatQuittingUserWantsToDo == eSaveBeforeQuitting ) {
      bool bUserPushedCancel = false;
      pAssemblyGet()->saveAssembly( bUserPushedCancel, 0 );
      if (! bUserPushedCancel )
         exit(0);
   }
   else assert(false);
}


void ConsEd :: setAssemblyNameOnAllContigwins( const RWCString& soAssemblyName ) {

   for (int nContigWin = 0; 
        nContigWin < dapContigWin_.length(); 
        nContigWin++) {
      dapContigWin_[nContigWin]->setAceFileLabel( soAssemblyName );
   }
}


void ConsEd :: showXResources() {
   TextBox* pTB = new TextBox( "Fallback X Resources");

   RWCString soXResources;

   GuiApp :: pGetGuiApp()->getFallbackResources( soXResources );

   pTB->append( soXResources );

   pTB->makeVisible();
}






void ConsEd :: showConsedVersion() {
   TextBox* pTB = new TextBox( "Consed Version");

   pTB->append( szConsedVersion );

   pTB->makeVisible();
}






void ConsEd :: askUserForFileToOpen() {

   FileName soAceFileToOpen;

   filePopupAndGetAceFilename* pFilePopupAndGetAceFilename =
      new filePopupAndGetAceFilename();
   
   soAceFileToOpen = 
      pFilePopupAndGetAceFilename->filWaitForUserToSelectAceFile();
   
   PleaseWait*   pPleaseWait = new PleaseWait( GuiApp::pGetGuiApp()->widGetTopLevel() );

   // openAssemblyFile has its own try/catch
   openAssemblyFile(soAceFileToOpen);
   
   try {
      pGetGuiTopWindow()->setAssemblyName( soAceFileToOpen.soGetBasename() );


      if ( ! consedParameters::pGetConsedParameters()->bErrorMessageDisplayedAtStartup_ )
         XRaiseWindow( 
                   XtDisplay( GuiApp::pGetGuiApp()->widGetTopLevel() ),
                   XtWindow(  GuiApp::pGetGuiApp()->widGetTopLevel() ) 
                   );

      delete pPleaseWait;
      setAssemblyNameOnAllContigwins( soAceFileToOpen.soGetBasename() );
         
      perhapsApplyEditHistoryFile( soAceFileToOpen );

      perhapsCreateSocketToControlConsed();

      // if open failed, won't get this far
         
      updateContigListOnMainConsedWindow();
   }
   catch(ExceptionBase eb) {
      // this is one of ours, report it if not done so
      if (! eb.bUserNotified() ) {
         GuiApp::popupErrorMessage(eb.szGetDesc());
      }
   }

}




void ConsEd :: openAssemblyFile(const char* szFilePath) {
   // delete pAssembly_;   // toasted, whatever you were

   try {
      new Assembly(szFilePath, 
                   true ); // bSetGlobalAssembly
   }
   catch (ExceptionBase eb) {

      eb.addStringToDesc( "ace file = " );
      eb.addStringToDesc( szFilePath );
      // failed for whatever reason
      // consedParameters::pGetConsedParameters()->bOKToUseGui_ = false;
      popupInfoOrErrorMessageNoFormat( NULL,  // shell of parent
                                 true,  // error message not into
                                 eb.szGetDesc(),
                                 false ); // assembly object is not valid
      exit(1);
   }


   if ( pCP->bTerminateAfterStartup_ ) {
      exit( EXIT_SUCCESS );
   }
}




void    ConsEd :: searchForString() {

   if ( pCP->bAllowMultipleSearchForStringWindows_ ) {
      pGuiSearchForString_ = new GuiSearchForString();
      pGuiSearchForString_->popupAskForSearchString();
   }
   else {
      if ( pGuiSearchForString_ ) {
         pGuiSearchForString_->raiseWindow();
      }
      else {
         pGuiSearchForString_ = new GuiSearchForString();
         pGuiSearchForString_->popupAskForSearchString();
      }
   }
}


void ConsEd :: updateContigListOnMainConsedWindow() {
   // is there a current assembly?
   if (! pAssemblyGet() ) { return; }

   if ( pChooseContig_ ) delete pChooseContig_;

   pChooseContig_ = new ChooseContig( pAssemblyGet(), 
                                      "" ); // pattern--show all reads

   pChooseContig_->fillThyself( true ); // limit # of reads

   pGetGuiTopWindow()->showListOfContigs( pChooseContig_ );
}




void ConsEd :: choseContig( const int nIndex, const bool bReadListNotContigList ) {

   Contig *pContig;
   LocatedFragment* pLocatedFragment;

   pChooseContig_->getChosenContig( nIndex, bReadListNotContigList,
                                    pContig, pLocatedFragment );

   int nStartPaddedConsPos;

   if (bReadListNotContigList) {

      choseContig2( pLocatedFragment );

   }
   else {
      nStartPaddedConsPos =pContig->nGetFirstDisplayableContigPos();

      ContigWin* pContigWin = 
         pPutContigInAContigWin( pContig, nStartPaddedConsPos );
   }
}


void ConsEd :: choseContig2( LocatedFragment* pLocFrag ) {

   int nStartPaddedConsPos;
      
   if (pLocFrag->bComp() )
      nStartPaddedConsPos = 
         pLocFrag->nGetHighQualityEndUnlessAllBad();
   else
      nStartPaddedConsPos = 
         pLocFrag->nGetHighQualityStartUnlessAllBad();

   ContigWin* pContigWin = 
      pScrollExistingContigWinOrMakeNewContigWin( 
                pLocFrag->pContig_, nStartPaddedConsPos );

   pContigWin->moveCursorToFragPos( nStartPaddedConsPos, pLocFrag );
      
   // raise the window
   pContigWin->raiseWindow();


}





// This routine does not use a contigwin that already has this contig
// in it.  It forces up a new copy of the contig.

ContigWin* ConsEd :: pPutContigInAContigWin( Contig* pContig,
                                            const int nStartPaddedConsPos ) {
   //  We need a ContigWin to put it in.

   ContigWin* pContigWin = new ContigWin();

   pContigWin->setContig( pContig );
   pContigWin->initializeContigWinForNewContig( nStartPaddedConsPos, 
                                                true ); // put in center
   return( pContigWin );
}


ContigWin* ConsEd :: pGetContigWinByContig( Contig* pContig ) {

  int nContigWin = 0;
  bool bFound = false;
  while( !bFound && nContigWin < dapContigWin_.length() ) {
    if (pContig == dapContigWin_[ nContigWin ]->pGetContig() )
      bFound = true;
    else
        nContigWin++;
  }

  if (bFound )
    return( dapContigWin_[ nContigWin ] );
  else
    return( NULL );
}



ContigWin* ConsEd :: pGetSecondContigWinByContig( Contig* pContig ) {

   bool bFound = false;
   int nContigWin;
   for( nContigWin = 0; nContigWin < dapContigWin_.length(); ++nContigWin ) {
      if ( pContig == dapContigWin_[ nContigWin ]->pGetContig() ) {
         bFound = true;
         break;
      }
   }

   if ( !bFound )
      return( NULL );

   ++nContigWin; // advance over the found one
   bFound = false;
   for( ; nContigWin < dapContigWin_.length(); ++nContigWin ) {
      if ( pContig == dapContigWin_[ nContigWin ]->pGetContig() ) {
         bFound = true;
         break;
      }
   }
         
   if ( bFound ) {
      return( dapContigWin_[ nContigWin ] );
   }
   else 
      return( NULL );
}




ContigWin* ConsEd :: pScrollExistingContigWinOrMakeNewContigWin( 
                              Contig* pContig,
                              const int nStartPaddedConsPos ) {

  // first try to find this contig in an existing contigwin

  ContigWin* pContigWin = pGetContigWinByContig( pContig );

  if (pContigWin ) {
    pContigWin->scrollToConsensusPosInCenter( nStartPaddedConsPos );
    return( pContigWin );
  }
  else {
     // check that this pContig exists--we must do this by
     // checking the *pointers*--not the values.  index uses
     // values.  So use indexPointers instead.  This fixes a bug
     // in which the old memory still had the contig name in it,
     // which is what Contig::operator== uses to check if two
     // contigs are equal which is used by index.

     if ( pGetAssembly()->dapContigs_.indexByPointers( pContig ) == RW_NPOS ) {
        THROW_ERROR2( "This contig has changed too much." );
     }


    return( pPutContigInAContigWin( pContig, nStartPaddedConsPos ) );
  }
}



ContigWin* ConsEd :: pScrollExistingContigWinOrMakeNewContigWinAndSetCursorOnConsensus( 
      Contig* pContig,
      const int nConsPos ) {

   // first try to find this contig in an existing contigwin

   ContigWin* pContigWin = pGetContigWinByContig( pContig );

   if ( pContigWin ) {
      pContigWin->scrollToConsensusPosInCenterAndSetCursorOnConsensus( nConsPos );
   }
   else {
      // check that this Contig exists--we must do this by checking the
      // *pointers*--not the values.  index uses values.  So use 
      // indexPointers instead.  This fixes a bug in which the old 
      // memory still had the contig name in it,
      // which is what Contig::operator== uses to check if two
      // contigs are equal which is used by index.

      if ( pGetAssembly()->dapContigs_.indexByPointers( pContig ) == RW_NPOS ) {
         THROW_ERROR2( "This contig has changed too much." );
      }

      pContigWin = pPutContigInAContigWin( pContig, nConsPos );

      // not sure if this is necessary...yes, it is.  pPutcontigInAContigWin
      // does do a lot of what happens below, but it does not sort the
      // reads at the cursor (if the user has that option set), nor does
      // it set the cursor
      pContigWin->scrollToConsensusPosInCenterAndSetCursorOnConsensus( nConsPos );
   }

   return pContigWin;
}

                                                                       






void ConsEd :: perhapsApplyEditHistoryFile( const RWCString& soAceFileOpened ) {

   // if the edit history is applied when the user started consed -read_only,
   // the EditAction's will throw and exception which will not be caught
   // and Consed will crash
   if ( pCP->bReadOnly_ ) return;

  bool bEditHistoryFileExistss = bEditHistoryFileExists( soAceFileOpened );

  bool bApplyEdits;

  if (bEditHistoryFileExistss ) {
    RWCString soQuestion( "There is an edit history file (a .wrk file).\nConsed may have crashed during a previous \nsession using this same ace file:\n");
    soQuestion += soAceFileOpened;
    soQuestion += "\nDo you want to apply those edits?\n(If you do not apply the edits, you may lose them.)";

    bApplyEdits = bGuiGetAnswerYesNo( soQuestion );
  }
  else
    bApplyEdits = false;

  if (bApplyEdits) {
    PleaseWait*   pPleaseWait = new PleaseWait(  GuiApp::pGetGuiApp()->widGetTopLevel() );
    applyEditHistory( soAceFileOpened );
    delete pPleaseWait;
    pAssemblyGet()->setOpenEditHistoryForAppendFlag();
  }
  else
    pAssemblyGet()->clearOpenEditHistoryForAppendFlag();
}



void ConsEd :: createCompareContigs( ContigWin* pContigWin, int nConsensusPosition ) {

  aCompareContigs_.insert( new compareContigs( pContigWin, nConsensusPosition ) );
}


compareContigs* ConsEd :: pCreateCompareContigsForAssemblyView() {
   compareContigs* pCC = new compareContigs();
   aCompareContigs_.insert( pCC );
   return( pCC );
}


void ConsEd :: ccompareContigs(ContigWin* pContigWin) {
  if (!pContigWin->pGetEditCursor()->bCursorValid() ) {
    GuiApp::popupErrorMessage( "You must first click on a read to set the cursor" );
    return;
  }

  compareContigs* pCompareContigs;
  bool bFirstNotSecondContigInCompareContigsWindow;
  findCompareContigWindow( pCompareContigs, bFirstNotSecondContigInCompareContigsWindow );

  
  int nConsensusPosition = pContigWin->pGetEditCursor()->nConsPosGet();

  if ( bFirstNotSecondContigInCompareContigsWindow ) {
     createCompareContigs( pContigWin, nConsensusPosition );
  }
  else {
     pCompareContigs->addSecondContig( pContigWin, nConsensusPosition );
  }
}



char* ConsEd :: szGetConsedVersion() {  return( szConsedVersion ); }



void ConsEd :: refreshAllContigWinsAndAllTeditors() {

   // send refresh message to all ContigWins.

   for (int nContigWin = 0; 
        nContigWin < dapContigWin_.length(); 
        nContigWin++) {
      dapContigWin_[nContigWin]->refreshContigWinAndTeditor();
   }
}


void ConsEd :: refreshAllContigWins() {


   // send refresh message to all ContigWins.

   for (int nContigWin = 0; 
        nContigWin < dapContigWin_.length(); 
        nContigWin++) {
      dapContigWin_[nContigWin]->drawBasesAndConsensus( true ); // erase first
   }
}
   



// just for switching between ways of ordering reads
void ConsEd :: getNewContigViewsForAllContigWins() {

   for (int nContigWin = 0; 
        nContigWin < dapContigWin_.length(); 
        nContigWin++) {
      dapContigWin_[nContigWin]->getNewContigView();
   }

}




EditAction* ConsEd :: pGetLastEditAction() {

   EditAction* pEditAction;

   if ( dapEditHistory_.isEmpty() )
      pEditAction = NULL;
   else
      pEditAction = dapEditHistory_.last();

   return( pEditAction );
}




void ConsEd :: disallowUndos() {
   
   dapEditHistory_.clearAndDestroy();

   for( int nContigWin = 0; nContigWin < dapContigWin_.length(); ++nContigWin ) {
      ContigWin* pContigWin = dapContigWin_[ nContigWin ];

      Teditor* pTeditor = pContigWin->pGetTeditor();

      if ( pTeditor ) {

         for( int nTedWin = 0; nTedWin < pTeditor->nNumberOfTedWins(); ++nTedWin ) {
            TedWin* pTedWin = pTeditor->pGetTedWin( nTedWin );
            
            GuiTedWin* pGuiTedWin = pTedWin->pGetGuiTedWin();

            pGuiTedWin->deactivateUndoButton();
         }
      }
   }
}


void ConsEd :: findCompareContigWindow( 
                   compareContigs*& pCompareContigs,
                   bool& bFirstNotSecondContigInCompareContigsWindow ) {

   
   for( int n = 0; n < aCompareContigs_.length(); ++n ) {
      compareContigs* pCompareContigsTemp = aCompareContigs_[n];

      // case in which the compare contigs window is currently in use
      // by 2 contigs

      if ( pCompareContigsTemp->pContig2_ && pCompareContigsTemp->pContig1_ )
         continue;

      // case in which a join has been made using this compare contigs window

      if ( !pCompareContigsTemp->pContig2_ &&
           !pCompareContigsTemp->pContig1_ )
         continue;


      bFirstNotSecondContigInCompareContigsWindow = false;
      pCompareContigs = pCompareContigsTemp;
      return;
   }

   // if reached here, then all the compareContigs windows were full

   bFirstNotSecondContigInCompareContigsWindow = true;
}


bool ConsEd :: bFoundAndDeletedThisCompareContigsFromList( 
                                   compareContigs* pCompareContigs ) {

   for( int n = 0; n < aCompareContigs_.length(); ++n ) {
      compareContigs* pCompareContigsTemp = aCompareContigs_[n];

      if ( pCompareContigsTemp == pCompareContigs ) {
         aCompareContigs_.removeAt( n );
         return( true );
      }
   }

   // if reached here, couldn't find the compareContigs in the list
   return( false );
}


bool ConsEd :: bIsThisContigWinOnAnyCompareContigsWindow( ContigWin* pContigWin ) {

   for( int n = 0; n < aCompareContigs_.length(); ++n ) {
      compareContigs* pCompareContigs = aCompareContigs_[n];

      if ( pCompareContigs->pContigWin1_ == pContigWin ||
           pCompareContigs->pContigWin2_ == pContigWin ) 
         return( true );
   }

   return( false );
}



void ConsEd :: deleteOtherCompareContigsWindowsWithSameContigs( 
                     compareContigs* pCompareContigsForJoin,
                     Contig* pContig1,
                     Contig* pContig2 ) {

   for( int n = aCompareContigs_.length() - 1; n >= 0; --n ) {
      compareContigs* pCompareContigs = aCompareContigs_[ n ];
      
      
      bool bFoundContig = false;
      if ( pCompareContigs->pContigWin1_ ) {
         if ( ( pCompareContigs->pContigWin1_->pContig_ == pContig1 ) ||
              ( pCompareContigs->pContigWin1_->pContig_ == pContig2 ) )
            bFoundContig = true;
      }
      if ( pCompareContigs->pContigWin2_ ) {
         if ( ( pCompareContigs->pContigWin2_->pContig_ == pContig1 ) ||
              ( pCompareContigs->pContigWin2_->pContig_ == pContig2 ) )
            bFoundContig = true;
      }

      if ( bFoundContig ) {

         // this compare contigs window has one of the contigs that will
         // be joined together.  Thus we want to delete the contig and hence
         // the compare contigs window

         if ( pCompareContigsForJoin != pCompareContigs )
            delete pCompareContigs;
      }
   }
}


void ConsEd :: deleteAllCompareContigWindows() {

   for( int n = 0; n < aCompareContigs_.length(); ++n ) {
      compareContigs* pCompareContigs = aCompareContigs_[ n ];

      delete pCompareContigs;
   }
}
                                                                
   
float ConsEd :: fGetPositionOnGelFromRestrictionFragmentSize( 
                                          const int nFragSize ) {

   int nMaxIndex = sizeof( nRestrictionFragmentSize ) / sizeof( int ) - 1;

   // boundary cases:
   // 1) below lowest value
   // 2) above highest value

   if ( nFragSize < nRestrictionFragmentSize[0] ) {
      return( fInterpolateGelPosition(
                         nFragSize,
                         nRestrictionFragmentSize[0],
                         fPositionOnGel[0],
                         nRestrictionFragmentSize[1],
                         fPositionOnGel[1] ) );
   }

   if ( nFragSize > nRestrictionFragmentSize[ nMaxIndex ] ) {
      return( fInterpolateGelPosition(
                         nFragSize,
                         nRestrictionFragmentSize[ nMaxIndex - 1 ],
                         fPositionOnGel[ nMaxIndex - 1 ],
                         nRestrictionFragmentSize[ nMaxIndex ],
                         fPositionOnGel[ nMaxIndex ] ) );
   }

   int nLowerIndex;
   int nUpperIndex;

   getRangeOfFragmentIndicesForFragmentSize( nFragSize, 
                                             nLowerIndex, 
                                             nUpperIndex );


   if ( nLowerIndex == nUpperIndex ) {
      return( fPositionOnGel[ nLowerIndex ] );
   }


   // normal case in which nFragSize is within the range--must interpolate

   return( fInterpolateGelPosition( 
                         nFragSize, 
                         nRestrictionFragmentSize[ nLowerIndex ],
                         fPositionOnGel[ nLowerIndex ],
                         nRestrictionFragmentSize[ nUpperIndex ],
                         fPositionOnGel[ nUpperIndex ] ) );
}




float ConsEd :: fInterpolateGelPosition( const int nFragSize,
                                         const int nLowerFragSize,
                                         const float fLowerGelPosition,
                                         const int nUpperFragSize,
                                         const float fUpperGelPosition ) {

   float fLogLowerFragSize = log10( (double) nLowerFragSize );
   float fLogUpperFragSize = log10( (double) nUpperFragSize );



   float fA = ( fLowerGelPosition - fUpperGelPosition ) /
      ( 1.0/fLogLowerFragSize - 1.0 / fLogUpperFragSize );

   float fB = ( fLowerGelPosition * fLogLowerFragSize 
                - fUpperGelPosition * fLogUpperFragSize ) /
      ( fLogLowerFragSize - fLogUpperFragSize );

   return( fA / log10( (double) nFragSize ) + fB );
}


void ConsEd :: getRangeOfFragmentIndicesForFragmentSize( 
                                        const int nFragSize, 
                                        int&  nLowerIndex, 
                                        int&  nUpperIndex ) {

   int nMaxIndex = sizeof( nRestrictionFragmentSize ) / sizeof( int ) - 1;

   int nRangeStart = 0;
   int nRangeEnd = nMaxIndex;


   assert( nRestrictionFragmentSize[nRangeStart] <= nFragSize );
   assert( nFragSize <= nRestrictionFragmentSize[ nRangeEnd ] );

   // this will *always* return an interval that is size 1, even
   // if nFragSize matches one or the other of the ends of the interval

   while( true ) {

      int nRangeLen = nRangeEnd - nRangeStart;
      
      if ( nRangeLen == 1 ) {
         nLowerIndex = nRangeStart;
         nUpperIndex = nRangeEnd;
         return;
      }

      int nTestIndex = ( nRangeLen / 2 ) + nRangeStart;

      if ( nFragSize < nRestrictionFragmentSize[ nTestIndex ]  ) {
         
         // .  .  .  .  +  .  .  (+ is nTestIndex)
         //      * (pTMatch)
         // So overshot.
         nRangeEnd = nTestIndex;
      }
      else {

         // .  .  +  .  .  .  .
         //               * (pTMatch)

         nRangeStart = nTestIndex;
      } // if ( nRestrictionFragmentSize[ nTestIndex ] == nFragSize ) { else
   } // while( true );
}

   
bool ConsEd :: bCouldGetFragmentSizeFromGelPosition( 
                                  const float fGelPosition,
                                  int& nFragSize ) {

   int nMinFragSize = 1;
   
   // first, find a max frag size

   const int nTooBig = 100000000; // E8

   int nTryMaxFragSize = 3000;
   while( fGelPosition < 
          fGetPositionOnGelFromRestrictionFragmentSize( nTryMaxFragSize ) &&
          nTryMaxFragSize < nTooBig ) {
      nTryMaxFragSize *= 2;
   }

   int nMaxFragSize = nTryMaxFragSize;

   if ( nTryMaxFragSize >= nTooBig ) {
      return( false );
   }

   while( ( nMinFragSize + 2 )  <= nMaxFragSize ) {
      int nMiddle = ( nMinFragSize + nMaxFragSize ) / 2;

      if ( fGelPosition >= 
           fGetPositionOnGelFromRestrictionFragmentSize( nMiddle ) ) {
         nMaxFragSize = nMiddle;
      }
      else
         nMinFragSize = nMiddle;
   }

   assert( fGetPositionOnGelFromRestrictionFragmentSize( nMaxFragSize ) <=
           fGelPosition && fGelPosition <=
           fGetPositionOnGelFromRestrictionFragmentSize( nMinFragSize ) );

   nFragSize = nMinFragSize;

   return( true );
}


void ConsEd :: whatToDoBeforeModifyAssembly() {

   if ( pAssemblyView_ ) {
      delete pAssemblyView_;

      pAssemblyView_ = NULL;

   }


   pCP->bNeedToSetContigTemplateArrays_ = true;

}



void ConsEd :: closeAllWindows() {

   deleteAllCompareContigWindows();
   deleteAllContigWins();
   if ( pAssemblyView_ ) {
      delete pAssemblyView_;
      pAssemblyView_ = NULL; 
   }

   for( int nGui = aGuiMultiContigNavigator_.length() - 1; nGui >= 0; --nGui ) {
      delete aGuiMultiContigNavigator_[ nGui ];  // the destructor removes itself from aGuiMultiContigNavigator_
   }

   if ( pGuiSearchForString_ ) {
      delete pGuiSearchForString_;
      pGuiSearchForString_ = NULL;
   }
}
   

void ConsEd :: addGuiMultiContigNavigator( guiMultiContigNavigator* pGuiMultiContigNav ) {

   aGuiMultiContigNavigator_.insert( pGuiMultiContigNav );

}


void ConsEd :: startUpConsedWithCustomNavigationFile( 
        const FileName& filCustomNavigationFile ) {

   gotoList* pGotoList = new gotoList();
   RWCString soTitle;

   try {
      readCustomNavigationFile( filCustomNavigationFile, soTitle, pGotoList );
   }
   catch( ExceptionBase eb ) {
      if ( !eb.bUserNotified() ) {
         popupErrorMessage( eb.szGetDesc() );
      }
      sleep( 3 );
      exit( 1 );
   }

   guiMultiContigNavigator* pGui = 
      new guiMultiContigNavigator( soTitle, // title
                                   soTitle, // first line
                                   "", // second line
                                   75, // width in chars
                                   soEmptyString, // title of special purpose button
                                   NULL, // callback for special purpose button
                                   NULL, // client data for special purpose button
                                   NULL, // widTopLevelShellToBeConnectedTo, NULL means top level window
                                   pGotoList );
                                   


   ConsEd::pGetConsEd()->addGuiMultiContigNavigator( pGui );


}


void ConsEd :: startUpConsedAtMainContigPos( const RWCString& soUnpaddedPos ) {

   int nUnpaddedPos;
   if ( !bIsNumericMaybeWithWhitespace( soUnpaddedPos, nUnpaddedPos ) ) {
      cerr << "fatal: " << soUnpaddedPos << " is not numeric" << endl;
      exit( 1 );
   }

   // find the contig with most reads

   Assembly* pAssembly = ConsEd::pGetAssembly();

   Contig* pContigWithMostReads = 0;
   int nNumberOfReads = -666;

   for( int nContig = 0; nContig < pAssembly->nNumContigs(); ++nContig ) {
      Contig* pContig = pAssembly->pGetContig( nContig );

      if ( pContig->nGetNumberOfFragsInContig() >
           nNumberOfReads ) {
         nNumberOfReads = pContig->nGetNumberOfFragsInContig();
         pContigWithMostReads = pContig;
      }
   }

   if ( !pContigWithMostReads ) {
      cerr << "fatal:  couldn't find contig with most reads" << endl;
      exit( 1 );
   }

   int nConsPos = pContigWithMostReads->nPaddedIndex( nUnpaddedPos );

   ContigWin* pContigWin = 
      ConsEd::pGetConsEd()->pScrollExistingContigWinOrMakeNewContigWin( 
              pContigWithMostReads,
              nConsPos );

   pContigWin->moveCursorToConsPos( nConsPos );

   // raise the window

   pContigWin->raiseWindow();
   
}