/*****************************************************************************
#   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.
#
#*****************************************************************************/
//
// guidraw.cpp
//
// implementation for GuiContigWin class
//
// gordon 6-March-1995
//




#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/DrawingA.h>
#include <Xm/ScrolledW.h>
#include <Xm/ScrollBar.h>

#include <iostream.h>
#include <string.h>
#include <stdio.h>

#include "motifutils.h"
#include "guiapp.h"
#include "guicontigwin.h"
#include "mbt_errors.h"
#include "numutil.h"
#include "contigwin.h"
#include "consed.h"
#include "consedParameters.h"



////////////////////////////////////////////////////////////
//
// PURPOSE:  Clears all old bases so next redraw is able to
//      to just draw the bases until fragments end
//
// HOW TO USE IT: nothing to it
// 
// INPUTS: nix
//
// OUTPUTS: nix
//
// REV: Feb 2, 1995 (David Gordon)
//
////////////////////////////////////////////////////////////


void GuiContigWin :: guiClearContigWindow() {
   XClearArea( 
              XtDisplay( widContigWin_ ), 
              XtWindow( widContigWin_ ),
              0, 
              0, 
              0,
              0,
              false
              );
   
}



void    GuiContigWin :: guiDrawScaleHorizontalLine() {

   XDrawLine( XtDisplay(widContigWin_), 
             XtWindow( widContigWin_ ), pGctScale_->gcGet(),
             nFragmentBasesStartPixelX(), nScaleLinePixelY(), 
             nGetContigWindowPixelWidth(),
             nScaleLinePixelY() );
}





void    GuiContigWin :: guiDrawScaleBigTick( const int nCharScreenPosition ) {

   int nPixelScreenPosition = nPixelXFromScreenChar( nCharScreenPosition );

   int nMiddleOfCharPosition = 
     (int) (nPixelScreenPosition + .5 * GuiApp::pGetGuiApp()->nGetFontWidth() );



   XDrawLine(XtDisplay(widContigWin_), 
                      XtWindow( widContigWin_ ), 
                      pGctScale_->gcGet(),
                      nMiddleOfCharPosition, // x1
                      nScaleLinePixelY(),    // y1
                      nMiddleOfCharPosition, // x2
                      nScaleLinePixelY() + nScaleMarksHeight() ); // y2
}



void    GuiContigWin :: guiDrawScaleBigBigTick( const int nCharScreenPosition ) {

   int nPixelScreenPosition = nPixelXFromScreenChar( nCharScreenPosition );

   int nMiddleOfCharPosition = 
     (int) (nPixelScreenPosition + .5 * GuiApp::pGetGuiApp()->nGetFontWidth() );



   XDrawLine(XtDisplay(widContigWin_), 
                      XtWindow( widContigWin_ ), 
                      pGctScale_->gcGet(),
                      nMiddleOfCharPosition, // x1
                      nScaleLinePixelY(),    // y1
                      nMiddleOfCharPosition, // x2
                      nScaleLinePixelY() + 
             (int) ( 1.5 * nScaleMarksHeight()) ) ; // y2
}



void    GuiContigWin :: guiDrawScaleLittleTick( const int nCharScreenPosition ) {

   int nPixelScreenPosition = nPixelXFromScreenChar( nCharScreenPosition );

   int nMiddleOfCharPosition = 
     (int) (nPixelScreenPosition + .5 * GuiApp::pGetGuiApp()->nGetFontWidth() );



   XDrawLine(XtDisplay(widContigWin_), 
                      XtWindow( widContigWin_ ), 
                      pGctScale_->gcGet(),
                      nMiddleOfCharPosition, // x1
                      nScaleLinePixelY(),    // y1
                      nMiddleOfCharPosition, // x2
                      nScaleLinePixelY() + 
             (int) ( .3 * nScaleMarksHeight())  ); // y2
}




void    GuiContigWin :: guiDrawScaleNumber( const int nCharScreenPosition,
                                               char *szLabel ) {
   int nPixelScreenPosition = nPixelXFromScreenChar( nCharScreenPosition );

   int  nLen = strlen( szLabel );

   // center the number around the base that it is numbering
   // Why the +1 ?  This moves the number to the right another 
   // half character width, thus centering it over the base.

   int  nPixelOffset = ( ( nLen - 1 ) * GuiApp::pGetGuiApp()->nGetFontWidth() ) / 2;

   nPixelScreenPosition -= nPixelOffset;


   XDrawImageString( XtDisplay(widContigWin_), 
                                XtWindow( widContigWin_ ), 
                                pGctScaleNumbers_->gcGet(),
                                nPixelScreenPosition, 
                                nScaleNumbersBaseline(), 
                                szLabel, 
                                nLen ); 
}



////////////////////////////////////////////////////////////
//
// PURPOSE: to draw a string of bases in the same color on 
//          the same line
//
// HOW TO USE IT:
//
// 
// INPUTS:
//
// OUTPUTS:
//
// REV: (David Gordon)
//
// chrisa 9-mar-95
//    now takes the postion of the sequencing direction 
//    separator line into account
//
////////////////////////////////////////////////////////////

void    GuiContigWin :: guiDrawReadColorWord(const int nScreenCharX, 
                                       const int nReadLine,
                                       const char *szCharsToDraw,
                                       const int nNumberOfChars,
                                       GuiColorText* pGctToUse )
{
    int     nStartXPixel;
    int     nStartYPixel;
    
    nStartXPixel = nPixelXFromScreenChar( nScreenCharX );

    nStartYPixel = nFragLineToPixelY( nReadLine );
    if (nStartYPixel < 0 ) return;


    XDrawImageString( XtDisplay(widContigWin_),
                      XtWindow( widContigWin_),
                        pGctToUse->gcGet(),
                        nStartXPixel,
                        nStartYPixel,
                        szCharsToDraw,
                        nNumberOfChars);
}



// draws the consensus bases
// will only draw a chunk of them that is the same color

void    GuiContigWin :: guiDrawConsensusColorWord(const int nScreenCharX, 
                                       const char *szCharsToDraw,
                                       const int nNumberOfChars,
                                       GuiColorText* pGctToUse )
{
    int     nStartXPixel;
    int     nStartYPixel;
    
    nStartXPixel = nPixelXFromScreenChar( nScreenCharX );

    nStartYPixel = nConsensusBaselinePixelY(),

    XDrawImageString( XtDisplay(widContigWin_),
                      XtWindow( widContigWin_),
                        pGctToUse->gcGet(),
                        nStartXPixel,
                        nStartYPixel,
                        szCharsToDraw,
                        nNumberOfChars);
}




void GuiContigWin :: guiDrawReadPrefix( const int nReadLine,
                                        const RWCString& soReadPrefix,
                                        GuiColorText* pGuiColorText ) {


   int nStartPixelX = nReadPrefixPixelX();
   int nStartPixelY = nFragLineToPixelY( nReadLine );

   XDrawImageString( XtDisplay( widContigWin_ ),
                     XtWindow( widContigWin_ ),
                     pGuiColorText->gcGet(),
                     nStartPixelX,
                     nStartPixelY,
                     soReadPrefix.data(),
                     MIN( soReadPrefix.length(), 
                          pCP->nMaxCharsDisplayedForReadPrefix_ ) );



}




////////////////////////////////////////////////////////////
//
// PURPOSE: draws a single fragment name
//
// REV: (David Gordon) Feb 22, 1995
//
////////////////////////////////////////////////////////////

int    GuiContigWin :: nGuiDrawFragmentName( const int nReadLine,
                                             const RWCString& soReadName,
                                             const bool bIsHighlighted
                                             )
{
    int     nError;

    int nStartXPixel = nFragmentNameXPixelPosition();
    int nStartYPixel = nFragLineToPixelY( nReadLine );

    RWCString soReadNameThatFits = soReadName;
    if ( soReadNameThatFits.length() > nRoomForReadNames_ ) {
       soReadNameThatFits.keepOnlyLastPartOfString( nRoomForReadNames_ );
    }
    
    
    GC gc;

    if (bIsHighlighted )
       gc = pGctFragmentNamesHighlighted_->gcGet();
    else
       gc = pGctFragmentNames_->gcGet();
          
    nError = XDrawImageString( 
                        XtDisplay(widContigWin_),
                        XtWindow( widContigWin_),
                        gc,
                        nStartXPixel,
                        nStartYPixel,
                        soReadNameThatFits,
                        soReadNameThatFits.length() );

    return( nError );
}




////////////////////////////////////////////////////////////
//
// PURPOSE: draws sequencing direction arrow
//
// REV: (David Gordon) Feb 23, 1995
//
////////////////////////////////////////////////////////////




int    GuiContigWin :: nGuiDrawSequencingDirectionArrow( 
                                      const int nReadLine,
                                      const bool bFragmentComplemented,
                                      const bool bThisReadsTraceIsUp
                                                     )
{
    int     nStartXPixel;
    int     nStartYPixel;
    int     nError;
    XPoint  aPoint[4];

    //cout << "draw arrow at nLine = " << nLine << " comp = " << 
    //  bFragmentComplemented << endl;
    //cout.flush();

    nStartXPixel = nDirectionIndicatorPixelX();
    nStartYPixel = nDirectionIndicatorPixelY(nReadLine);

    if (bFragmentComplemented) {
       for( int nPoint = 0; nPoint < 4; ++nPoint ) {
          aPoint[ nPoint ].x = aPointLeft[ nPoint ].x + nStartXPixel;
          aPoint[ nPoint ].y = aPointLeft[ nPoint ].y + nStartYPixel;
       }
    }
    else {
       for( int nPoint = 0; nPoint < 4; ++nPoint ) {
          aPoint[ nPoint ].x = aPointRight[ nPoint ].x + nStartXPixel;
          aPoint[ nPoint ].y = aPointRight[ nPoint ].y + nStartYPixel;
       }
    }


    // XFillPolygon doesn't return anything--funny this has existed
    // for 10 years returning something (2005)

    XFillPolygon(    XtDisplay(widContigWin_),
                        XtWindow( widContigWin_),
                              
                        ( bThisReadsTraceIsUp ? 
                          pGctSequencingDirectionTraceIsUp_->gcGet() :
                          pGctSequencingDirection_->gcGet() ),

                        aPoint,
                        4,
                        Convex,
                        CoordModeOrigin );

    return( 1 );
}



void GuiContigWin :: guiDrawConsensusLabel() {
   
   XDrawImageString( XtDisplay(widContigWin_),
                      XtWindow( widContigWin_),

                     (( pContigWin_->nIndexOfTopVisibleRead_ == 0 ) ?
                     pGctConsensusLabel_->gcGet() :
                     pGuiColorTextWhenVerticalScrollbarScrolledDown_->gcGet() ),

                        nFragmentNameXPixelPosition(),
                        nConsensusBaselinePixelY(),
                        "CONSENSUS",
                        strlen( "CONSENSUS" )
                 );
}



void GuiContigWin :: guiDrawConsensusSeparatorLine() {

   // draw the consensus separator line
   XDrawLine( XtDisplay(widContigWin_), 
             XtWindow( widContigWin_ ), 

                     (( pContigWin_->nIndexOfTopVisibleRead_ == 0 ) ?
                     pGctConsensusLabel_->gcGet() :
                     pGuiColorTextWhenVerticalScrollbarScrolledDown_->gcGet() ),
             nFragmentBasesStartPixelX(), nConsensusSeparatorLinePixelY(), 
             nGetContigWindowPixelWidth(), nConsensusSeparatorLinePixelY() );

}


// draw a single consensus char, at passed screen relative
// position
// This is used to draw the cursor, or to blink off the cursor

void GuiContigWin :: guiDrawOneConsensusChar(const char cBase, 
                                             const int nScreenCharPos,
                                             GuiColorText* pGctToUse) {

   // transfer base to null terminated string for X draw routine
   char szBase[2];   
   szBase[0] = cBase;
   szBase[1] = '\0';

   int nStartXPixel = nPixelXFromScreenChar( nScreenCharPos );


   XDrawImageString(XtDisplay(widContigWin_),
                    XtWindow( widContigWin_),
                    pGctToUse->gcGet(),
                    nStartXPixel,
                    nConsensusBaselinePixelY(),
                    szBase,
                    1 );
}


// draws the line that separates the forward & reverse reads
void GuiContigWin :: nGuiDrawSepLine() const {
   XDrawLine(XtDisplay(widContigWin_), 
             XtWindow( widContigWin_ ), 
             pGctSequencingDirection_->gcGet(),
             nDirectionIndicatorPixelX(), 
             nDirectionSepLinePixelY(), 
             nGetContigWindowPixelWidth(), 
             nDirectionSepLinePixelY() );

}



int GuiContigWin :: nFragLineToPixelY( int nFragLine ) const {
   // nFragLine is 0-based

   int nPix;


   int     nScreenLine = nFragLine - pContigWin_->nIndexOfTopVisibleRead_;
   // nScreenLine is zero based also


   if (nScreenLine < 0 ) return( -1 );

   if ( !pContigWin_->bDrawSeparatorBetweenTopAndBottomStrandReads() ) {

      nPix = nForFragLineBaselinePixelY() +
         GuiApp::nGetFontHeight() * nScreenLine;
   }
   else {

      if (nScreenLine >= nForFragsVisible()) {
         // the frag line is in the reverse area, after showing 
         // some forward reads and the forward/reverse separator line
         // yes, so offset from start of the reverse lines

         int nReverseLine = nScreenLine - nForFragsVisible();

         nPix = nRevFragLineBaselinePixelY() +
            GuiApp::nGetFontHeight() * nReverseLine;

      }
      else {
         // case in which this fragment is in the forward read area
         nPix = nForFragLineBaselinePixelY() +
            GuiApp::nGetFontHeight() * nScreenLine;
      }
   }

   return nPix;
}



int GuiContigWin :: nGetFragmentBasesMaxPixelY() {
   return nRevFragLineTopPixelY() +
      ( nRevFragsVisible() * GuiApp::nGetFontHeight() );
}



void    GuiContigWin :: guiDrawHalfCharacterTag(
                                       const bool bReadTagNotConsensusTag,
                                       const int nScreenCharX, 
                                       const int nReadLine,
                                       const int nNumberOfChars,
                                       GuiColorText* pGctToUse )
{

    int nStartXPixel = nPixelXFromScreenChar( nScreenCharX );
    int nWidth = ( GuiApp::nGetFontWidth() * nNumberOfChars );

    int nBottomPixel;
    if (bReadTagNotConsensusTag ) {
       nBottomPixel = nFragLineToPixelY( nReadLine ) +
          GuiApp::nGetFontDescent() - 1;
    }
    else {
       nBottomPixel = nConsensusBaselinePixelY() +
          GuiApp::nGetFontDescent() - 1;
    }

    int nHeight = ( GuiApp::nGetFontHeight() *
       consedParameters::pGetConsedParameters()->nTagColorPerCentOfBase_ )/100;

    int nTopYPixel = nBottomPixel - nHeight + 1;

    if ( (nTopYPixel + nHeight) < 0 ) return;


    XFillRectangle( 
             XtDisplay(widContigWin_), 
             XtWindow( widContigWin_ ), 
             pGctToUse->gcGetReverse(),
             nStartXPixel,
             nTopYPixel,
             nWidth,
             nHeight );

}




void    GuiContigWin :: guiDrawBasesWithoutBackground( 
                                       const bool bReadNotConsensus,
                                       const int nScreenCharX, 
                                       const int nReadLine,
                                       const RWCString& soBasesToDraw,
                                       GuiColorText* pGctToUse )
{
    int     nStartXPixel;
    int     nStartYPixel;
    
    nStartXPixel = nPixelXFromScreenChar( nScreenCharX );

    if (bReadNotConsensus ) 
       nStartYPixel = nFragLineToPixelY( nReadLine );
    else
       nStartYPixel = nConsensusBaselinePixelY();

    if (nStartYPixel < 0 ) return;


    XDrawString(    XtDisplay(widContigWin_),
                    XtWindow( widContigWin_),
                    pGctToUse->gcGet(),
                    nStartXPixel,
                    nStartYPixel,
                    (char*) soBasesToDraw.data(),
                    soBasesToDraw.length() );
}



void    GuiContigWin :: guiDrawAminoAcid( 
                                     const char cAminoAcid,
                                     const int nScreenCharX,
                                     const int nReadingFrame,
                                     GuiColorText* pGctToUse  )
{
   
   int nStartXPixel = nPixelXFromScreenChar( nScreenCharX );

   int nStartYPixel = nReadingFrameToPixelY( nReadingFrame );

   XDrawString( XtDisplay( widContigWin_),
                XtWindow( widContigWin_),
                pGctToUse->gcGet(),
                nStartXPixel,
                nStartYPixel,
                &cAminoAcid,
                1 ); // number of chars
}


   
void    GuiContigWin :: guiDrawProteinTranslationLabel(
                                     char* szLabel,
                                     const int nReadingFrame ) {

   int nStartXPixel = nFragmentNameXPixelPosition();
   int nStartYPixel = nReadingFrameToPixelY( nReadingFrame );

   XDrawString( XtDisplay( widContigWin_ ),
                XtWindow( widContigWin_ ),
                pGctProteinTranslationLabels_->gcGet(),
                nStartXPixel,   
                nStartYPixel,
                szLabel,
                strlen( szLabel ) );
}

   


void    GuiContigWin :: guiDrawVerticalLineAtCursor( const int nScreenCharPos ) {
   
   int nPixelScreenPosition = nPixelXFromScreenChar( nScreenCharPos );

   // the +2 is so it the line doesn't go through the middle of T's
   int nMiddleOfCharPosition =
      (int) ( nPixelScreenPosition + .5 * GAPP->nGetFontWidth() ) + 2;


   // do not try to draw beyond end of window--fixed June 2009 in
   // which green line would not go all the way down (see notes June 24, 2009)

   int nMaxPixel = nGetContigWindowPixelHeight();

   int nBottomPixelY = nGetFragmentBasesMaxPixelY();
   if ( nBottomPixelY > nMaxPixel ) {
      nBottomPixelY = nMaxPixel;
   }


   XDrawLine( XtDisplay( widContigWin_ ),
              XtWindow(  widContigWin_ ),
              pGctVerticalLineAtCursor_->gcGet(),
              nMiddleOfCharPosition,
              nConsensusSeparatorLinePixelY(),
              nMiddleOfCharPosition,
              nBottomPixelY );


}



void GuiContigWin :: guiDrawHorizontalLineAtCursor( const int nReadLine ) {


   int nPixelY = nFragLineToPixelY( nReadLine ) +
          GuiApp::nGetFontDescent() - 1;
   

   int nPixelXLeft = 0;
   int nPixelXRight = nGetContigWindowPixelWidth();



   XDrawLine( XtDisplay( widContigWin_ ),
              XtWindow( widContigWin_ ),
              pGctHorizontalLineAtCursor_->gcGet(),
              nPixelXLeft,
              nPixelY,
              nPixelXRight,
              nPixelY );
}