/*
 * misc notepad functions
 * Copyright (C) 1984-2000 Microsoft Corporation
 */

#include "precomp.h"

BOOL fCase = FALSE;         // Flag specifying case sensitive search 
BOOL fReverse = FALSE;      // Flag for direction of search 

extern HWND hDlgFind;       // handle to modeless FindText window 

LPTSTR ReverseScan(
    LPTSTR lpSource,
    LPTSTR lpLast,
    LPTSTR lpSearch,
    BOOL fCaseSensitive )
{
   TCHAR cLastCharU;
   TCHAR cLastCharL;
   INT   iLen;

   cLastCharU= (TCHAR) (INT_PTR) CharUpper( (LPTSTR)(INT_PTR)(*lpSearch) );
   cLastCharL= (TCHAR) (INT_PTR) CharLower( (LPTSTR)(INT_PTR)(*lpSearch) );

   iLen = lstrlen(lpSearch);

   if (!lpLast)
      lpLast = lpSource + lstrlen(lpSource);

   do
   {
      if (lpLast == lpSource)
         return NULL;

      --lpLast;

      if (fCaseSensitive)
      {
         if (*lpLast != *lpSearch)
            continue;
      }
      else
      {
           if( !( *lpLast == cLastCharU || *lpLast == cLastCharL ) )
            continue;
      }

      if (fCaseSensitive)
      {
         if (!_tcsncmp( lpLast, lpSearch, iLen))
            break;
      }
      else
      {
         //
         // compare whole string using locale specific comparison.
         // do not use C runtime version since it may be wrong.
         //

         if( 2 == CompareString( LOCALE_USER_DEFAULT,
                    NORM_IGNORECASE | SORT_STRINGSORT | NORM_STOP_ON_NULL,
                    lpLast,   iLen,
                    lpSearch, iLen) )
            break;
      }
   } while (TRUE);

   return lpLast;
}

LPTSTR ForwardScan(LPTSTR lpSource, LPTSTR lpSearch, BOOL fCaseSensitive )
{
   TCHAR cFirstCharU;
   TCHAR cFirstCharL;
   int iLen = lstrlen(lpSearch);

   cFirstCharU= (TCHAR) (INT_PTR) CharUpper( (LPTSTR)(INT_PTR)(*lpSearch) );
   cFirstCharL= (TCHAR) (INT_PTR) CharLower( (LPTSTR)(INT_PTR)(*lpSearch) );

   while (*lpSource)
   {
      if (fCaseSensitive)
      {
         if (*lpSource != *lpSearch)
         {
            lpSource++;
            continue;
         }
      }
      else
      {
         if( !( *lpSource == cFirstCharU || *lpSource == cFirstCharL ) )
         {
            lpSource++;
            continue;
         }
      }

      if (fCaseSensitive)
      {
         if (!_tcsncmp( lpSource, lpSearch, iLen))
            break;
      }
      else
      {
         if( 2 == CompareString( LOCALE_USER_DEFAULT,
                    NORM_IGNORECASE | SORT_STRINGSORT | NORM_STOP_ON_NULL,
                    lpSource, iLen,
                    lpSearch, iLen) )
            break;
      }

      lpSource++;
   }

   return *lpSource ? lpSource : NULL;
}

// Search forward or backward in the edit control text for the given pattern
// It is the responsibility of the caller to set the cursor
BOOL Search (TCHAR * szKey)
{
    BOOL      bStatus= FALSE;
    TCHAR   * pStart, *pMatch;
    DWORD     StartIndex, LineNum, EndIndex;
    DWORD     SelStart, SelEnd, i;
    HANDLE    hEText;           // handle to edit text
    UINT      uSelState;
    HMENU     hMenu;
    BOOL      bSelectAll = FALSE;


    if (!*szKey)
        return( bStatus );

    SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&SelStart, (LPARAM)&SelEnd);


    // when we finish the search, we highlight the text found, and continue 
    // the search after the end of the highlighted position (in forward 
    // case) or from the begining of the highlighted position in the reverse
    // direction (in reverse case). this would break if the user has 
    // selected all text. this hack would take care of it. (this is consistent
    // with VC editors' search too.

    hMenu = GetMenu(hwndNP);
    uSelState = GetMenuState(GetSubMenu(hMenu, 1), M_SELECTALL, MF_BYCOMMAND);
    if (uSelState == MF_GRAYED)
    {
        bSelectAll = TRUE;
        SelStart = SelEnd =0;
    }


    //
    // get pointer to edit control text to search
    //

    hEText= (HANDLE) SendMessage( hwndEdit, EM_GETHANDLE, 0, 0 );
    if( !hEText )  // silently return if we can't get it
    {
        return( bStatus );
    }

    pStart = LocalLock( hEText );

    if( !pStart ) {
        return( bStatus );
    }

    if (fReverse)
    {
        // Get current line number 
        LineNum= (DWORD)SendMessage(hwndEdit, EM_LINEFROMCHAR, SelStart, 0);
        // Get index to start of the line
        StartIndex= (DWORD)SendMessage(hwndEdit, EM_LINEINDEX, LineNum, 0);
        // Set upper limit for search text
        EndIndex= SelStart;
        pMatch= NULL;

        // Search line by line, from LineNum to 0
        i = LineNum;
        while (TRUE)
        {
            pMatch= ReverseScan(pStart+StartIndex,pStart+EndIndex,szKey,fCase);
            if (pMatch)
               break;
            // current StartIndex is the upper limit for the next search 
            EndIndex= StartIndex;

            if (i)
            {
                // Get start of the next line
                i-- ;
                StartIndex = (DWORD)SendMessage(hwndEdit, EM_LINEINDEX, i, 0);
            }
            else
               break ;
        }
    }
    else
    {
            pMatch= ForwardScan(pStart+SelEnd, szKey, fCase);
    }

    LocalUnlock(hEText);

    if (pMatch == NULL)
    {
        //
        // alert user on not finding any text unless it is replace all
        //
        if( !(FR.Flags & FR_REPLACEALL) )
        {
            HANDLE hPrevCursor= SetCursor( hStdCursor );
            AlertBox( hDlgFind ? hDlgFind : hwndNP,
                      szNN,
                      szCFS,
                      szSearch,
                      MB_APPLMODAL | MB_OK | MB_ICONINFORMATION);
            SetCursor( hPrevCursor );
        }
    }
    else
    {
        SelStart = (DWORD)(pMatch - pStart);
        SendMessage( hwndEdit, EM_SETSEL, SelStart, SelStart+lstrlen(szKey));

        // since we are selecting the found text, enable SelectAll again.
        if (bSelectAll)
        {
            EnableMenuItem(GetSubMenu(hMenu, 1), M_SELECTALL, MF_ENABLED);
        }

        //
        // show the selected text unless it is replace all
        //

        if( !(FR.Flags & FR_REPLACEALL) )
        {
            SendMessage(hwndEdit, EM_SCROLLCARET, 0, 0);
            UpdateStatusBar( TRUE );
        }
        bStatus= TRUE;   // found
    }

    return( bStatus );
}

// Recreate notepad edit window, get text from old window and put in new window. 
// Called when user changes style from wrap on/off 
//
// Called with the style of the new window
//

BOOL NpReCreate( long style )
{
    RECT    rcT1;
    HWND    hwndT1;
    HANDLE  hT1;
    int     cchTextNew;
    TCHAR*  pchText;
    BOOL    fWrapIsOn = ((style & WS_HSCROLL) != 0);
    HCURSOR hPrevCursor;
    BOOL    bModified;     // modify flag from old edit buffer

    ULONG    SelectionStart;  // NotepadEx: save selection and cursor pos.
    ULONG    SelectionEnd;    // when entering/leaving wordwrap

    // NotepadEx: save old cursor position
    SendMessage(hwndEdit, EM_GETSEL, (WPARAM) &SelectionStart, (LPARAM) &SelectionEnd);

    // if wordwrap, remove soft carriage returns 
    hPrevCursor= SetCursor( hWaitCursor );     // this may take some time...
    if( fWrapIsOn ) 
    {
        GotoAndScrollInView(1);  // get around MLE bug
        SendMessage(hwndEdit, EM_FMTLINES, FALSE, 0L);
    }

    bModified= (SendMessage( hwndEdit, EM_GETMODIFY, 0,0 ) != 0);

    cchTextNew= (int)SendMessage( hwndEdit, WM_GETTEXTLENGTH, 0, 0L );
    hT1= LocalAlloc( LMEM_MOVEABLE, ByteCountOf(cchTextNew + 1) );
    if( !hT1 )
    {
        // failed, restore wordwrap; insert soft carriage returns
        if( fWrapIsOn )
        {
            SendMessage(hwndEdit, EM_FMTLINES, TRUE, 0L);
        }
        SetCursor( hPrevCursor );
        return FALSE;
    }

    GetClientRect( hwndNP, (LPRECT)&rcT1 );

    //
    // save the current edit control text.
    //

    pchText= LocalLock (hT1);
    SendMessage( hwndEdit, WM_GETTEXT, cchTextNew+1, (LPARAM)pchText );
    hwndT1= CreateWindowEx( WS_EX_CLIENTEDGE,
        TEXT("Edit"),
        TEXT(""), // pchText
        style,
        0,
        0,
        rcT1.right,
        rcT1.bottom,
        hwndNP,
        (HMENU)ID_EDIT,
        hInstanceNP, NULL );
    if( !hwndT1 )
    {
        SetCursor( hPrevCursor );
        if( fWrapIsOn )      // restore wordwrap
        {
            SendMessage( hwndEdit, EM_FMTLINES, TRUE, 0L );
        }
        LocalUnlock(hT1);
        LocalFree(hT1);
        return FALSE;
    }

    //
    // The user can "add" styles to the edit window after it is
    // created (like WS_EX_RTLREADING) when language packs are installed.
    // Preserve these styles when changing the word wrap.
    //

    SetWindowLong( hwndT1 ,
                   GWL_EXSTYLE ,
                   GetWindowLong( hwndEdit , GWL_EXSTYLE )|WS_EX_CLIENTEDGE ) ;

    // Set font before set text to save time calculating
    SendMessage( hwndT1, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0) );

    if (!SendMessage (hwndT1, WM_SETTEXT, 0, (LPARAM) pchText))
    {
        SetCursor( hPrevCursor );
        if( fWrapIsOn )   // restore wordwrap
        {
            SendMessage( hwndEdit, EM_FMTLINES, TRUE, 0L );
        }
        DestroyWindow( hwndT1 );
        LocalUnlock( hT1 );
        LocalFree( hT1 );
        return FALSE;
    }
    LocalUnlock(hT1);


    DestroyWindow( hwndEdit );     // out with the old
    hwndEdit = hwndT1;             // in with the new

    // free the earlier allocated memory in hEdit

    if (hEdit)
        LocalFree(hEdit);

    hEdit = hT1;

    // limit text for safety's sake.
    PostMessage( hwndEdit, EM_LIMITTEXT, (WPARAM)CCHNPMAX, 0L );

    ShowWindow(hwndNP, SW_SHOW);
    SendMessage( hwndEdit, EM_SETMODIFY, bModified, 0L );
    SetFocus(hwndEdit);

    SetCursor( hPrevCursor );   // restore cursor

    // redraw the status bar

    if( fStatus )
    {
        RECT rcClient;
        GetClientRect(hwndNP, &rcClient);
        NPSize(rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
        UpdateStatusBar( TRUE );
        ShowWindow( hwndStatus, SW_SHOW );
   }

    return TRUE;
}
