/*****************************************************************************
#   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.
#
#*****************************************************************************/
#include    "guiCheckPrimer.h"
#include    "handleWindowManagerDelete2.h"
#include    <Xm/Form.h>
#include    <Xm/TextF.h>
#include    <Xm/Label.h>
#include    <Xm/Xm.h>
#include    <Xm/PushB.h>
#include    "popupErrorMessage.h"
#include    "bIsNumericMaybeWithWhitespace.h"
#include    <stdio.h>
#include    "contig.h"
#include    "please_wait.h"
#include    "hp_exception_kludge.h"
#include    <Xm/ToggleB.h>
#include    <Xm/RowColumn.h>
#include    "findPrimerMatchesElsewhere.h"
#include    "findPrimerSelfMatches.h"
#include    "calculatePrimerMeltingTemperatures.h"
#include    "findPrimerMatchesAgainstSequencesInFile.h"
#include    "consedParameters.h"
#include    "checkPrimersForMononucleotideRepeats.h"
#include    "findTemplatesForPrimers.h"
#include    "checkPrimersForACGT.h"
#include    "textbox.h"
#include    "assembly.h"
#include    "consed.h"
#include    "whyIsPrimerNotAcceptableTypes.h"
#include    "createPrimerCandidate.h"
#include    "findStickiestSelfMatch.h"
#include    "primerStickiestFalseMatch.h"
#include    "complement_so.h"





static void cbUserPushedCheckPrimer( Widget wid,
                                     XtPointer pClientData,
                                     XtPointer pCallData ) {
   
   guiCheckPrimer* pGuiCheckPrimer =
      (guiCheckPrimer*) pClientData;

   TRY_CATCH_WRAPPER( pGuiCheckPrimer->userPushedCheckPrimer();
                      );
}



static void cbUserPushedDismiss( Widget wid, 
                                 XtPointer pClientData,
                                 XtPointer pCallData ) {

   guiCheckPrimer* pGuiCheckPrimer =
      (guiCheckPrimer*) pClientData;

   TRY_CATCH_WRAPPER( delete pGuiCheckPrimer;
                      );
}



guiCheckPrimer :: ~guiCheckPrimer() {
   XtPopdown( widPopupShell_ );
   XtDestroyWidget( widPopupShell_ );
}



void guiCheckPrimer :: display() {

   widPopupShell_ = XtVaCreatePopupShell( 
                       "Check Primer",
                       topLevelShellWidgetClass,
                       widParentShell_,
                       XmNtitle, (char*) "Check Primer",
                       XmNtransient, False,
                       XmNdeleteResponse, XmDO_NOTHING,
                       NULL );

   handleWindowManagerDelete2( widPopupShell_, cbUserPushedDismiss, this );

   Widget widForm = XtVaCreateManagedWidget( "form",
                                             xmFormWidgetClass,
                                             widPopupShell_,
                                             XmNancestorSensitive, True,
                                             NULL );


   const int nLabelPosition = 47;
   const int nFieldPosition = 53;
   
   Widget widLeftPositionLabel = XtVaCreateManagedWidget(
                       "Left End of Primer:",
                       xmLabelWidgetClass,
                       widForm,
                       XmNtopAttachment, XmATTACH_FORM,
                       XmNleftAttachment, XmATTACH_FORM,
                       XmNrightAttachment, XmATTACH_POSITION,
                       XmNrightPosition, nLabelPosition,
                       XmNalignment, XmALIGNMENT_END,
                       NULL );
   
   widLeftPosition_ = XtVaCreateManagedWidget(
                       "left",
                       xmTextFieldWidgetClass,
                       widForm,
                       XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
                       XmNtopWidget, widLeftPositionLabel,
                       XmNleftAttachment, XmATTACH_POSITION,
                       XmNleftPosition, nFieldPosition,
                       XmNcolumns, 10,
                       NULL );

   Widget widRightPositionLabel = XtVaCreateManagedWidget(
                       "Right End of Primer:",
                       xmLabelWidgetClass,
                       widForm,
                       XmNtopAttachment, XmATTACH_WIDGET,
                       XmNtopWidget, widLeftPosition_,
                       XmNtopOffset, 10,
                       XmNleftAttachment, XmATTACH_FORM,
                       XmNrightAttachment, XmATTACH_POSITION,
                       XmNrightPosition, nLabelPosition,
                       XmNalignment, XmALIGNMENT_END,
                       NULL );

   widRightPosition_ = XtVaCreateManagedWidget(
                       "right",
                       xmTextFieldWidgetClass,
                       widForm,
                       XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
                       XmNtopWidget, widRightPositionLabel,
                       XmNleftAttachment, XmATTACH_POSITION,
                       XmNleftPosition, nFieldPosition,
                       XmNcolumns, 10,
                       NULL );

   
   Widget widStrandLabel = XtVaCreateManagedWidget(
                       "Strand:",
                       xmLabelWidgetClass,
                       widForm,
                       XmNtopAttachment, XmATTACH_WIDGET,
                       XmNtopWidget, widRightPosition_,
                       XmNtopOffset, 10,
                       XmNleftAttachment, XmATTACH_FORM,
                       XmNrightAttachment, XmATTACH_POSITION,
                       XmNrightPosition, nLabelPosition,
                       XmNalignment, XmALIGNMENT_END,
                       NULL );

   Widget widStrandRadioBox = XmCreateRadioBox( widForm,
                                                "radio",
                                                NULL,
                                                0 );

   XtVaSetValues( widStrandRadioBox,
                  XmNtraversalOn, True,
                  XmNtopAttachment, XmATTACH_WIDGET,
                  XmNtopWidget, widRightPosition_,
                  XmNleftAttachment, XmATTACH_POSITION,
                  XmNleftPosition, nFieldPosition,
                  XmNorientation, XmHORIZONTAL,
                  XmNshadowThickness, 0,
                  XmNborderWidth, 0,
                  XmNmarginWidth, 0,
                  NULL );

   widPointsLeft_ = XtVaCreateManagedWidget( 
                  "<-",
                  xmToggleButtonWidgetClass,
                  widStrandRadioBox,
                  XmNset, False,
                  NULL );

   Widget widPointsRight = XtVaCreateManagedWidget(
                  "->",
                  xmToggleButtonWidgetClass,
                  widStrandRadioBox,
                  XmNset, True,
                  NULL );

   XtManageChild( widStrandRadioBox );


   Widget widWholeCloneOrSubcloneLabel = XtVaCreateManagedWidget(
                  "Template Type:",
                  xmLabelWidgetClass,
                  widForm,
                  XmNtopAttachment, XmATTACH_WIDGET,
                  XmNtopWidget, widStrandRadioBox,
                  XmNtopOffset, 10,
                  XmNleftAttachment, XmATTACH_FORM,
                  XmNrightAttachment, XmATTACH_POSITION,
                  XmNrightPosition, nLabelPosition,
                  XmNalignment, XmALIGNMENT_END,
                  NULL );

   Widget widTypeRadioBox = XmCreateRadioBox( widForm,
                                              "radio2",
                                              NULL,
                                              0 );

   XtVaSetValues( widTypeRadioBox,
                  XmNtraversalOn, True,
                  XmNtopAttachment, XmATTACH_WIDGET,
                  XmNtopWidget, widStrandRadioBox,
                  XmNleftAttachment, XmATTACH_POSITION,
                  XmNleftPosition, nFieldPosition,
                  XmNorientation, XmHORIZONTAL,
                  XmNshadowThickness, 0,
                  XmNborderWidth, 0,
                  XmNmarginWidth, 0,
                  NULL );

   widSubcloneTemplate_ = XtVaCreateManagedWidget(
                  "subclone",
                  xmToggleButtonWidgetClass,
                  widTypeRadioBox,
                  XmNset, True,
                  NULL );

   Widget widWholeCloneTemplate = XtVaCreateManagedWidget(
                  "whole clone",
                  xmToggleButtonWidgetClass,
                  widTypeRadioBox,
                  XmNset, False,
                  NULL );

   XtManageChild( widTypeRadioBox );


   Widget widCheckPrimer = XtVaCreateManagedWidget(
                  "Check Primer",
                  xmPushButtonWidgetClass,
                  widForm,
                  XmNtopAttachment, XmATTACH_WIDGET,
                  XmNtopWidget, widTypeRadioBox,
                  XmNleftAttachment, XmATTACH_POSITION,
                  XmNleftPosition, 25,
                  XmNrightAttachment, XmATTACH_POSITION,
                  XmNrightPosition, 45,
                  XmNbottomAttachment, XmATTACH_FORM,
                  XmNbottomOffset, 10,
                  NULL );

   XtAddCallback( widCheckPrimer, 
                  XmNactivateCallback,
                  cbUserPushedCheckPrimer,
                  this );


   widDismissButton_ = XtVaCreateManagedWidget(
                  "Dismiss",
                  xmPushButtonWidgetClass,
                  widForm,
                  XmNtopAttachment, XmATTACH_WIDGET,
                  XmNtopWidget, widTypeRadioBox,
                  XmNleftAttachment, XmATTACH_POSITION,
                  XmNleftPosition, 55,
                  XmNrightAttachment, XmATTACH_POSITION,
                  XmNrightPosition, 75,
                  XmNbottomAttachment, XmATTACH_FORM,
                  XmNbottomOffset, 10,
                  NULL );

   XtAddCallback( widDismissButton_,
                  XmNactivateCallback,
                  cbUserPushedDismiss,
                  this );
   
   XtPopup( widPopupShell_, XtGrabNone );
}





   
                       
void guiCheckPrimer :: userPushedCheckPrimer() {
   
   // get left and right positions.  Check that they numeric and
   // within the bounds of this contig.   Get whether this is a
   // subclone template primer or a whole clone primer.

   char* szValue;
   szValue = XmTextFieldGetString( widLeftPosition_ );
   
   RWCString soLeftPosition( szValue );
   free( szValue );

   szValue = XmTextFieldGetString( widRightPosition_ );

   RWCString soRightPosition( szValue );
   free( szValue );

   int nUnpaddedLeft;
   int nUnpaddedRight;

   if ( !bIsNumericMaybeWithWhitespace( soLeftPosition, nUnpaddedLeft ) ) {
      popupErrorMessage( "left position is not numeric" );
      return;
   }

   if ( !bIsNumericMaybeWithWhitespace( soRightPosition, nUnpaddedRight ) ) {
      popupErrorMessage( "right position is not numeric" );
      return;
   }

   if ( nUnpaddedLeft > nUnpaddedRight ) {
      popupErrorMessage( "left position must be <= right position" );
      return;
   }

   if ( nUnpaddedLeft < pContig_->nGetUnpaddedStartIndex() ) {
      popupErrorMessage( "%d <= left position must be true",
                         pContig_->nGetUnpaddedStartIndex() );
      return;
   }

   if ( pContig_->nGetUnpaddedEndIndex() < nUnpaddedRight ) {
      popupErrorMessage( "right position must be <= %d",
                         pContig_->nGetUnpaddedEndIndex() );
      return;
   }

   bool bTopStrandPrimer = 
      ( ( XmToggleButtonGetState( widPointsLeft_ ) == True ) ?
                             false : true );

   bool bCloneNotSubcloneTemplate = 
      ( ( XmToggleButtonGetState( widSubcloneTemplate_ ) == True ) ?
        false : true );

   Assembly* pAssembly = ConsEd::pGetAssembly();   

   cout << "about to set padded to unpadded conversion table...";
   cout.flush();
   pAssembly->setContigMatchTablesInAllContigs();
   cout << "done" << endl;
   cout << "about to set unpadded to padded conversion table...";
   cout.flush();
   cout << "done" << endl;

   pCP->setParametersForSequencingPrimersNotPCRPrimers( true );
 
   // needed by nPaddedIndexFast in displayPrimers.cpp
   if ( bCloneNotSubcloneTemplate )
      pAssembly->setAllPaddedPositionsArrays();
   else
      pContig_->setPaddedPositionsArray();

   
   int nUnpaddedLength = nUnpaddedRight - nUnpaddedLeft + 1;


   TextBox* pTB = new TextBox( "What is Wrong With This Primer", 30 );

   pTB->appendWithArgs( "%s primer %s from %d to %d of %s\n",
                ( bCloneNotSubcloneTemplate ? "whole clone template" : 
                  "subclone template" ),
                ( bTopStrandPrimer ? "top strand" : "bottom strand" ),
                nUnpaddedLeft,
                nUnpaddedRight,
                pContig_->soGetName().data() );

   if ( nUnpaddedLength < pCP->nPrimersMinimumLengthOfAPrimerToUse_ ) {
      pTB->appendWithArgs( "\nPrimer unacceptable because its length %d is less than the minimum length %d\n",
                           nUnpaddedLength,
                           pCP->nPrimersMinimumLengthOfAPrimerToUse_ );
   }

   if ( nUnpaddedLength > pCP->nPrimersMaximumLengthOfAPrimerToUse_ ) {
      pTB->appendWithArgs( "\nPrimer unacceptable because its length %d is more than the maximum length %d\n",
                           nUnpaddedLength,
                           pCP->nPrimersMaximumLengthOfAPrimerToUse_ );
   }

   
   // just so we can go on to the other checks, such as 
   // findPrimerMatchesElsewhere, lets change the primer range

   pCP->nPrimersMinimumLengthOfAPrimerToUse_ = nUnpaddedLength;
   pCP->nPrimersMaximumLengthOfAPrimerToUse_ = nUnpaddedLength;
   
   
   primerType primer;
   createPrimerCandidate( &primer,
                          nUnpaddedLeft,
                          nUnpaddedLength,
                          bTopStrandPrimer,
                          pContig_,
                          nUnpaddedLeft ); // nUnpaddedPosOfCursor--unimportant


   RWCString soPrimerBases( primer.szPrimer_, primer.nUnpaddedLength_ );

   pTB->appendWithArgs( "\nsequence: %s\n",
                soPrimerBases.data() );

   

   if ( primer.bAcceptable_ ) {
      pTB->appendWithArgs( "\nprimer bases are acceptable quality (worst is %d)\n",
                   (int) primer.ucMinQuality_ );

   }
   else {
      if ( primer.nWhyIsPrimerNotAcceptable_ == BAD_PRIMER_TOO_LOW_QUALITY ) {
         pTB->appendWithArgs( "\nPrimer unacceptable because bases were not all quality %d or greater\n",
                      pCP->nPrimersMinQuality_ );
         pTB->append( "This means that there is a significant probability that the \nprimer will not anneal to the desired location.\n" );
      }
      else if ( primer.nWhyIsPrimerNotAcceptable_ == BAD_PRIMER_IN_SINGLE_SUBCLONE_REGION ) {
         pTB->append( "\nPrimer unacceptable because it is in a single subclone region\n" );
      }
      else if ( primer.nWhyIsPrimerNotAcceptable_ == BAD_PRIMER_WHERE_HIGH_QUALITY_DISCREPANCIES ) {
         pTB->append( "\nPrimer is unacceptable because there are high quality discrepancies at its location\n" );
      }
      else if ( primer.nWhyIsPrimerNotAcceptable_ == BAD_PRIMER_WHERE_UNALIGNED_HIGH_QUALITY_REGION ) {
         pTB->append( "\nPrimer is unacceptable because there is an unaligned high quality region at its location\n" );
      }
      else {
         pTB->appendWithArgs( "\nPrimer is unacceptable due to reason %d\n",
                      primer.nWhyIsPrimerNotAcceptable_ );
      }

   }

   // in order to do other checks, pretend the primer has passed all
   // checks up to here:

   primer.bAcceptable_ = true;
   
   
   findPrimerMatchesElsewhere( &primer,
                               1, // nNumberOfPrimers
                               bTopStrandPrimer,
                               bCloneNotSubcloneTemplate );

   if ( !primer.bAcceptable_ ) {
      pTB->append( "\nPrimer is unacceptable because it had false matches elsewhere:\n" );
      
   }
   else {
      pTB->append( "\nPrimer does not have too severe false matches:\n" );
   }

   RWCString soBigOutput( (size_t) 2000 );

   primerStickiestFalseMatch( &primer,
                             1, // primer number
                             soBigOutput );

   cerr << soBigOutput << " " << soBigOutput.length() << endl;
   int n;
   for( n = 0; n < soBigOutput.length(); ++n )
      putchar( soBigOutput[n] );

   pTB->append( soBigOutput );


   primer.bAcceptable_ = true;
   
   if ( pCP->bPrimersScreenForVector_ ) {
      findPrimerMatchesAgainstSequencesInFile( &primer,
                                               1, // nNumberOfPrimers
                                               bCloneNotSubcloneTemplate );

      if ( !primer.bAcceptable_ ) {
         pTB->append( "\nPrimer unacceptable because it has matches against vector sequence in file\n" );
      }
      else {
         pTB->append( "\nPrimer does not have unacceptable matches against vector sequence in file\n" );
      }
   }

   primer.bAcceptable_ = true;
   
   calculatePrimerMeltingTemperatures( &primer,
                                       1 );

   if ( !primer.bAcceptable_ ) {
      pTB->appendWithArgs( "\nPrimer unacceptable because melting temperature %d is not within your range of %d to %d\n",
                           (int) primer.dMeltingTemperature_,
                           pCP->nPrimersMinMeltingTempToUse_,
                           pCP->nPrimersMaxMeltingTempToUse_ );
   }
   else {
      pTB->appendWithArgs( "\nPrimer melting temperature %d is within range\n",
                           (int) primer.dMeltingTemperature_ );

   }

   primer.bAcceptable_ = true;

   findStickiestSelfMatch( &primer );

   if ( primer.nSelfMatchScore_ > 
        pCP->nPrimersMaxSelfMatchScore_ ) {
      pTB->appendWithArgs( "\nPrimer unacceptable:  will form primer-dimer:\n");
   }
   else {
      pTB->append( "\nPrimer will not form primer-dimer:\n" );
   }

   pTB->appendWithArgs( "Primer self match score %d and maximum acceptable =  %d\n",
                           primer.nSelfMatchScore_,
                           pCP->nPrimersMaxSelfMatchScore_ );


   pTB->appendWithArgs( "nSelfMatchOffsetOfComplementedPrimer_ = %d nSelfMatchPosOf3PrimeSubOligo_ = %d\n",
                     primer.nSelfMatchOffsetOfComplementedPrimer_,
                     primer.nSelfMatchPosOf3PrimeSubOligo_ );


   // show the alignment of the primer against its reverse complement:
   // (I could show it against itself in the reverse orientation, and then
   // show complementary bases, but it is easier for the eye to look
   // for the same bases so I show the reverse *complement*

   RWCString soPrimerComplement = soComplementSO( soPrimerBases );

   int nSpacesBeforePrimer = 0;
   if ( primer.nSelfMatchOffsetOfComplementedPrimer_ < 0 ) {
      nSpacesBeforePrimer = -primer.nSelfMatchOffsetOfComplementedPrimer_;
   }
   

   for( n = 0; n < nSpacesBeforePrimer; ++n ) {
      pTB->append( " " );
   }
   pTB->append( soPrimerBases );
   pTB->append( "\n" );
   
   if ( primer.nSelfMatchOffsetOfComplementedPrimer_ > 0 ) {
      for( n = 0; n < primer.nSelfMatchOffsetOfComplementedPrimer_; ++n ) {
         pTB->append( " " );
      }
   }

   pTB->append( soPrimerComplement );
   pTB->append( "\n" );
   
   int nWhereToIndicateSuboligo = 
      nSpacesBeforePrimer + primer.nSelfMatchPosOf3PrimeSubOligo_;
   // might need to add 1

   for( n = 0; n < nWhereToIndicateSuboligo; ++n ) 
      pTB->append( " " );
   pTB->append( "^\n" );
   
                          
                                               
   primer.bAcceptable_ = true;

   int nNumberOfAcceptablePrimers;

   checkPrimersForMononucleotideRepeats( &primer,
                                         1,
                                         nNumberOfAcceptablePrimers );

   if ( !primer.bAcceptable_ ) {
      pTB->append( "\nPrimer not acceptable because of mononucleotide run\n" );
   }
   else
      pTB->append( "\nPrimer does not have too long mononucleotide run\n" );

   pTB->appendWithArgs( "Longest mononucleotide run = %d and threshold = %d\n",
                primer.nLengthOfMononucleotideRun_,
                pCP->nPrimersMaxLengthOfMononucleotideRepeat_ );

   primer.bAcceptable_ = true;

   checkPrimersForACGT( &primer,
                        1,
                        nNumberOfAcceptablePrimers );

   if (!primer.bAcceptable_ ) {
      pTB->append( "\nPrimer not acceptable because there are characters besides acgt in it.\n" );

   }

   pTB->append( "\n" );

   primer.bAcceptable_ = true;

   if ( !bCloneNotSubcloneTemplate &&
        pCP->bPrimersPickTemplatesForPrimers_ ) {

      findTemplatesForPrimers( &primer,
                               1,
                               nNumberOfAcceptablePrimers,
                               bTopStrandPrimer );

      int nNumberOfTemplates = 0;
      for( int nTemplate = 0; nTemplate < nNUMBER_OF_TEMPLATES; ++nTemplate ) {
         if ( primer.pSubcloneTemplate_[ nTemplate ] ) {
            subcloneTTemplate* pSub = primer.pSubcloneTemplate_[ nTemplate ];
            pTB->appendWithArgs( " template: %s\n",
                                 pSub->soTemplateName_.data() );
            ++nNumberOfTemplates;
         }
      }


      if ( !primer.bAcceptable_ ) {
         if ( primer.nWhyIsPrimerNotAcceptable_ == BAD_PRIMER_HAS_NO_TEMPLATE ) {
         
            pTB->append( "Primer not acceptable because could not find any template for primer.\n" );
         }
         else if ( primer.nWhyIsPrimerNotAcceptable_ == BAD_PRIMER_NOT_ENOUGH_TEMPLATES ) {
            pTB->appendWithArgs( "Primer not acceptable because could only find %d templates but needed a minimum of %d templates\n",
                                 nNumberOfTemplates,
                                 pCP->nPrimersMinNumberOfTemplatesForPrimers_ );
         }
      }
   }


   pTB->makeVisible();
}