/*
 *  Notepad
 *
 *  Copyright 2000 Mike McCormack <Mike_McCormack@looksmart.com.au>
 *  Copyright 1997,98 Marcel Baur <mbaur@g26.ethz.ch>
 *  Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
 *  Copyright 2002 Andriy Palamarchuk
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 */

#include <windows.h>
#include <shlwapi.h>
#include <stdio.h>

#include "main.h"
#include "dialog.h"
#include "notepad_res.h"
#include "wine/unicode.h"

NOTEPAD_GLOBALS Globals;
static ATOM aFINDMSGSTRING;
static RECT main_rect;

static const WCHAR notepad_reg_key[] = {'S','o','f','t','w','a','r','e','\\',
                                        'M','i','c','r','o','s','o','f','t','\\','N','o','t','e','p','a','d','\0'};
static const WCHAR value_fWrap[]            = {'f','W','r','a','p','\0'};
static const WCHAR value_iPointSize[]       = {'i','P','o','i','n','t','S','i','z','e','\0'};
static const WCHAR value_iWindowPosDX[]     = {'i','W','i','n','d','o','w','P','o','s','D','X','\0'};
static const WCHAR value_iWindowPosDY[]     = {'i','W','i','n','d','o','w','P','o','s','D','Y','\0'};
static const WCHAR value_iWindowPosX[]      = {'i','W','i','n','d','o','w','P','o','s','X','\0'};
static const WCHAR value_iWindowPosY[]      = {'i','W','i','n','d','o','w','P','o','s','Y','\0'};
static const WCHAR value_lfCharSet[]        = {'l','f','C','h','a','r','S','e','t','\0'};
static const WCHAR value_lfClipPrecision[]  = {'l','f','C','l','i','p','P','r','e','c','i','s','i','o','n','\0'};
static const WCHAR value_lfEscapement[]     = {'l','f','E','s','c','a','p','e','m','e','n','t','\0'};
static const WCHAR value_lfItalic[]         = {'l','f','I','t','a','l','i','c','\0'};
static const WCHAR value_lfOrientation[]    = {'l','f','O','r','i','e','n','t','a','t','i','o','n','\0'};
static const WCHAR value_lfOutPrecision[]   = {'l','f','O','u','t','P','r','e','c','i','s','i','o','n','\0'};
static const WCHAR value_lfPitchAndFamily[] = {'l','f','P','i','t','c','h','A','n','d','F','a','m','i','l','y','\0'};
static const WCHAR value_lfQuality[]        = {'l','f','Q','u','a','l','i','t','y','\0'};
static const WCHAR value_lfStrikeOut[]      = {'l','f','S','t','r','i','k','e','O','u','t','\0'};
static const WCHAR value_lfUnderline[]      = {'l','f','U','n','d','e','r','l','i','n','e','\0'};
static const WCHAR value_lfWeight[]         = {'l','f','W','e','i','g','h','t','\0'};
static const WCHAR value_lfFaceName[]       = {'l','f','F','a','c','e','N','a','m','e','\0'};
static const WCHAR value_iMarginTop[]       = {'i','M','a','r','g','i','n','T','o','p','\0'};
static const WCHAR value_iMarginBottom[]    = {'i','M','a','r','g','i','n','B','o','t','t','o','m','\0'};
static const WCHAR value_iMarginLeft[]      = {'i','M','a','r','g','i','n','L','e','f','t','\0'};
static const WCHAR value_iMarginRight[]     = {'i','M','a','r','g','i','n','R','i','g','h','t','\0'};
static const WCHAR value_szHeader[]         = {'s','z','H','e','a','d','e','r','\0'};
static const WCHAR value_szFooter[]         = {'s','z','T','r','a','i','l','e','r','\0'};

/***********************************************************************
 *
 *           SetFileNameAndEncoding
 *
 *  Sets global file name and encoding (which is used to preselect original
 *  encoding in Save As dialog, and when saving without using the Save As
 *  dialog).
 */
VOID SetFileNameAndEncoding(LPCWSTR szFileName, ENCODING enc)
{
    lstrcpyW(Globals.szFileName, szFileName);
    Globals.szFileTitle[0] = 0;
    GetFileTitleW(szFileName, Globals.szFileTitle, sizeof(Globals.szFileTitle));
    Globals.encFile = enc;
}

/******************************************************************************
 *      get_dpi
 *
 * Get the dpi from registry HKCC\Software\Fonts\LogPixels.
 */
DWORD get_dpi(void)
{
    static const WCHAR dpi_key_name[] = {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s','\0'};
    static const WCHAR dpi_value_name[] = {'L','o','g','P','i','x','e','l','s','\0'};
    DWORD dpi = 96;
    HKEY hkey;

    if (RegOpenKeyW(HKEY_CURRENT_CONFIG, dpi_key_name, &hkey) == ERROR_SUCCESS)
    {
        DWORD type, size, new_dpi;

        size = sizeof(new_dpi);
        if(RegQueryValueExW(hkey, dpi_value_name, NULL, &type, (LPBYTE)&new_dpi, &size) == ERROR_SUCCESS)
        {
            if(type == REG_DWORD && new_dpi != 0)
                dpi = new_dpi;
        }
        RegCloseKey(hkey);
    }
    return dpi;
}

/***********************************************************************
 *
 *           NOTEPAD_SaveSettingToRegistry
 *
 *  Save setting to registry HKCU\Software\Microsoft\Notepad.
 */
static VOID NOTEPAD_SaveSettingToRegistry(void)
{
    HKEY hkey;
    DWORD disp;

    if(RegCreateKeyExW(HKEY_CURRENT_USER, notepad_reg_key, 0, NULL,
                REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &disp) == ERROR_SUCCESS)
    {
        DWORD data;
        WINDOWPLACEMENT wndpl;

        wndpl.length = sizeof(WINDOWPLACEMENT);
        GetWindowPlacement(Globals.hMainWnd, &wndpl);
        main_rect = wndpl.rcNormalPosition;

#define SET_NOTEPAD_REG(hkey, value_name, value_data) do { DWORD data = value_data; RegSetValueExW(hkey, value_name, 0, REG_DWORD, (LPBYTE)&data, sizeof(DWORD)); }while(0)
        SET_NOTEPAD_REG(hkey, value_fWrap,            Globals.bWrapLongLines);
        SET_NOTEPAD_REG(hkey, value_iWindowPosX,      main_rect.left);
        SET_NOTEPAD_REG(hkey, value_iWindowPosY,      main_rect.top);
        SET_NOTEPAD_REG(hkey, value_iWindowPosDX,     main_rect.right - main_rect.left);
        SET_NOTEPAD_REG(hkey, value_iWindowPosDY,     main_rect.bottom - main_rect.top);
        SET_NOTEPAD_REG(hkey, value_lfCharSet,        Globals.lfFont.lfCharSet);
        SET_NOTEPAD_REG(hkey, value_lfClipPrecision,  Globals.lfFont.lfClipPrecision);
        SET_NOTEPAD_REG(hkey, value_lfEscapement,     Globals.lfFont.lfEscapement);
        SET_NOTEPAD_REG(hkey, value_lfItalic,         Globals.lfFont.lfItalic);
        SET_NOTEPAD_REG(hkey, value_lfOrientation,    Globals.lfFont.lfOrientation);
        SET_NOTEPAD_REG(hkey, value_lfOutPrecision,   Globals.lfFont.lfOutPrecision);
        SET_NOTEPAD_REG(hkey, value_lfPitchAndFamily, Globals.lfFont.lfPitchAndFamily);
        SET_NOTEPAD_REG(hkey, value_lfQuality,        Globals.lfFont.lfQuality);
        SET_NOTEPAD_REG(hkey, value_lfStrikeOut,      Globals.lfFont.lfStrikeOut);
        SET_NOTEPAD_REG(hkey, value_lfUnderline,      Globals.lfFont.lfUnderline);
        SET_NOTEPAD_REG(hkey, value_lfWeight,         Globals.lfFont.lfWeight);
        SET_NOTEPAD_REG(hkey, value_iMarginTop,       Globals.iMarginTop);
        SET_NOTEPAD_REG(hkey, value_iMarginBottom,    Globals.iMarginBottom);
        SET_NOTEPAD_REG(hkey, value_iMarginLeft,      Globals.iMarginLeft);
        SET_NOTEPAD_REG(hkey, value_iMarginRight,     Globals.iMarginRight);
#undef SET_NOTEPAD_REG

        /* Store the current value as 10 * twips */
        data = MulDiv(abs(Globals.lfFont.lfHeight), 720 , get_dpi());
        RegSetValueExW(hkey, value_iPointSize, 0, REG_DWORD, (LPBYTE)&data, sizeof(DWORD));

        RegSetValueExW(hkey, value_lfFaceName, 0, REG_SZ, (LPBYTE)&Globals.lfFont.lfFaceName,
                      lstrlenW(Globals.lfFont.lfFaceName) * sizeof(Globals.lfFont.lfFaceName[0]));

        RegSetValueExW(hkey, value_szHeader, 0, REG_SZ, (LPBYTE)&Globals.szHeader,
                      lstrlenW(Globals.szHeader) * sizeof(Globals.szHeader[0]));

        RegSetValueExW(hkey, value_szFooter, 0, REG_SZ, (LPBYTE)&Globals.szFooter,
                      lstrlenW(Globals.szFooter) * sizeof(Globals.szFooter[0]));

        RegCloseKey(hkey);
    }
}

/***********************************************************************
 *
 *           NOTEPAD_LoadSettingFromRegistry
 *
 *  Load setting from registry HKCU\Software\Microsoft\Notepad.
 */
static VOID NOTEPAD_LoadSettingFromRegistry(void)
{
    static const WCHAR systemW[] = { 'S','y','s','t','e','m','\0' };
    HKEY hkey;
    INT base_length, dx, dy;

    base_length = (GetSystemMetrics(SM_CXSCREEN) > GetSystemMetrics(SM_CYSCREEN))?
        GetSystemMetrics(SM_CYSCREEN) : GetSystemMetrics(SM_CXSCREEN);

    dx = base_length * .95;
    dy = dx * 3 / 4;
    SetRect( &main_rect, 0, 0, dx, dy );

    Globals.bWrapLongLines  = TRUE;
    Globals.iMarginTop = 2500;
    Globals.iMarginBottom = 2500;
    Globals.iMarginLeft = 2000;
    Globals.iMarginRight = 2000;
    
    Globals.lfFont.lfHeight         = -12;
    Globals.lfFont.lfWidth          = 0;
    Globals.lfFont.lfEscapement     = 0;
    Globals.lfFont.lfOrientation    = 0;
    Globals.lfFont.lfWeight         = FW_REGULAR;
    Globals.lfFont.lfItalic         = FALSE;
    Globals.lfFont.lfUnderline      = FALSE;
    Globals.lfFont.lfStrikeOut      = FALSE;
    Globals.lfFont.lfCharSet        = DEFAULT_CHARSET;
    Globals.lfFont.lfOutPrecision   = OUT_DEFAULT_PRECIS;
    Globals.lfFont.lfClipPrecision  = CLIP_DEFAULT_PRECIS;
    Globals.lfFont.lfQuality        = DEFAULT_QUALITY;
    Globals.lfFont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
    lstrcpyW(Globals.lfFont.lfFaceName, systemW);

    LoadStringW(Globals.hInstance, STRING_PAGESETUP_HEADERVALUE,
                Globals.szHeader, ARRAY_SIZE(Globals.szHeader));
    LoadStringW(Globals.hInstance, STRING_PAGESETUP_FOOTERVALUE,
                Globals.szFooter, ARRAY_SIZE(Globals.szFooter));

    if(RegOpenKeyW(HKEY_CURRENT_USER, notepad_reg_key, &hkey) == ERROR_SUCCESS)
    {
        WORD  data_helper[MAX_PATH];
        DWORD type, data, size;

#define QUERY_NOTEPAD_REG(hkey, value_name, ret) do { DWORD type, data; DWORD size = sizeof(DWORD); if(RegQueryValueExW(hkey, value_name, 0, &type, (LPBYTE)&data, &size) == ERROR_SUCCESS) if(type == REG_DWORD) ret = data; } while(0)
        QUERY_NOTEPAD_REG(hkey, value_fWrap,            Globals.bWrapLongLines);
        QUERY_NOTEPAD_REG(hkey, value_iWindowPosX,      main_rect.left);
        QUERY_NOTEPAD_REG(hkey, value_iWindowPosY,      main_rect.top);
        QUERY_NOTEPAD_REG(hkey, value_iWindowPosDX,     dx);
        QUERY_NOTEPAD_REG(hkey, value_iWindowPosDY,     dy);
        QUERY_NOTEPAD_REG(hkey, value_lfCharSet,        Globals.lfFont.lfCharSet);
        QUERY_NOTEPAD_REG(hkey, value_lfClipPrecision,  Globals.lfFont.lfClipPrecision);
        QUERY_NOTEPAD_REG(hkey, value_lfEscapement,     Globals.lfFont.lfEscapement);
        QUERY_NOTEPAD_REG(hkey, value_lfItalic,         Globals.lfFont.lfItalic);
        QUERY_NOTEPAD_REG(hkey, value_lfOrientation,    Globals.lfFont.lfOrientation);
        QUERY_NOTEPAD_REG(hkey, value_lfOutPrecision,   Globals.lfFont.lfOutPrecision);
        QUERY_NOTEPAD_REG(hkey, value_lfPitchAndFamily, Globals.lfFont.lfPitchAndFamily);
        QUERY_NOTEPAD_REG(hkey, value_lfQuality,        Globals.lfFont.lfQuality);
        QUERY_NOTEPAD_REG(hkey, value_lfStrikeOut,      Globals.lfFont.lfStrikeOut);
        QUERY_NOTEPAD_REG(hkey, value_lfUnderline,      Globals.lfFont.lfUnderline);
        QUERY_NOTEPAD_REG(hkey, value_lfWeight,         Globals.lfFont.lfWeight);
        QUERY_NOTEPAD_REG(hkey, value_iMarginTop,       Globals.iMarginTop);
        QUERY_NOTEPAD_REG(hkey, value_iMarginBottom,    Globals.iMarginBottom);
        QUERY_NOTEPAD_REG(hkey, value_iMarginLeft,      Globals.iMarginLeft);
        QUERY_NOTEPAD_REG(hkey, value_iMarginRight,     Globals.iMarginRight);
#undef QUERY_NOTEPAD_REG

        main_rect.right = main_rect.left + dx;
        main_rect.bottom = main_rect.top + dy;

        size = sizeof(DWORD);
        if(RegQueryValueExW(hkey, value_iPointSize, 0, &type, (LPBYTE)&data, &size) == ERROR_SUCCESS)
            if(type == REG_DWORD)
                /* The value is stored as 10 * twips */
                Globals.lfFont.lfHeight = -MulDiv(abs(data), get_dpi(), 720);

        size = sizeof(Globals.lfFont.lfFaceName);
        if(RegQueryValueExW(hkey, value_lfFaceName, 0, &type, (LPBYTE)&data_helper, &size) == ERROR_SUCCESS)
            if(type == REG_SZ)
                lstrcpyW(Globals.lfFont.lfFaceName, data_helper);

        size = sizeof(Globals.szHeader);
        if(RegQueryValueExW(hkey, value_szHeader, 0, &type, (LPBYTE)&data_helper, &size) == ERROR_SUCCESS)
            if(type == REG_SZ)
                lstrcpyW(Globals.szHeader, data_helper);

        size = sizeof(Globals.szFooter);
        if(RegQueryValueExW(hkey, value_szFooter, 0, &type, (LPBYTE)&data_helper, &size) == ERROR_SUCCESS)
            if(type == REG_SZ)
                lstrcpyW(Globals.szFooter, data_helper);
        RegCloseKey(hkey);
    }
}

/***********************************************************************
 *
 *           NOTEPAD_MenuCommand
 *
 *  All handling of main menu events
 */
static int NOTEPAD_MenuCommand(WPARAM wParam)
{
    switch (wParam)
    {
    case CMD_NEW:               DIALOG_FileNew(); break;
    case CMD_OPEN:              DIALOG_FileOpen(); break;
    case CMD_SAVE:              DIALOG_FileSave(); break;
    case CMD_SAVE_AS:           DIALOG_FileSaveAs(); break;
    case CMD_PRINT:             DIALOG_FilePrint(); break;
    case CMD_PAGE_SETUP:        DIALOG_FilePageSetup(); break;
    case CMD_PRINTER_SETUP:     DIALOG_FilePrinterSetup();break;
    case CMD_EXIT:              DIALOG_FileExit(); break;

    case CMD_UNDO:             DIALOG_EditUndo(); break;
    case CMD_CUT:              DIALOG_EditCut(); break;
    case CMD_COPY:             DIALOG_EditCopy(); break;
    case CMD_PASTE:            DIALOG_EditPaste(); break;
    case CMD_DELETE:           DIALOG_EditDelete(); break;
    case CMD_SELECT_ALL:       DIALOG_EditSelectAll(); break;
    case CMD_TIME_DATE:        DIALOG_EditTimeDate();break;

    case CMD_SEARCH:           DIALOG_Search(); break;
    case CMD_SEARCH_NEXT:      DIALOG_SearchNext(); break;
    case CMD_REPLACE:          DIALOG_Replace(); break;
                               
    case CMD_WRAP:             DIALOG_EditWrap(); break;
    case CMD_FONT:             DIALOG_SelectFont(); break;

    case CMD_HELP_CONTENTS:    DIALOG_HelpContents(); break;
    case CMD_HELP_ABOUT_NOTEPAD: DIALOG_HelpAboutNotepad(); break;

    default:
	break;
    }
   return 0;
}

/***********************************************************************
 * Data Initialization
 */
static VOID NOTEPAD_InitData(VOID)
{
    LPWSTR p = Globals.szFilter;
    static const WCHAR txt_files[] = { '*','.','t','x','t',0 };
    static const WCHAR all_files[] = { '*','.','*',0 };

    LoadStringW(Globals.hInstance, STRING_TEXT_FILES_TXT, p, MAX_STRING_LEN);
    p += lstrlenW(p) + 1;
    lstrcpyW(p, txt_files);
    p += lstrlenW(p) + 1;
    LoadStringW(Globals.hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
    p += lstrlenW(p) + 1;
    lstrcpyW(p, all_files);
    p += lstrlenW(p) + 1;
    *p = '\0';
    Globals.hDevMode = NULL;
    Globals.hDevNames = NULL;

    CheckMenuItem(GetMenu(Globals.hMainWnd), CMD_WRAP,
            MF_BYCOMMAND | (Globals.bWrapLongLines ? MF_CHECKED : MF_UNCHECKED));
}

/***********************************************************************
 * Enable/disable items on the menu based on control state
 */
static VOID NOTEPAD_InitMenuPopup(HMENU menu, int index)
{
    int enable;

    EnableMenuItem(menu, CMD_UNDO,
        SendMessageW(Globals.hEdit, EM_CANUNDO, 0, 0) ? MF_ENABLED : MF_GRAYED);
    EnableMenuItem(menu, CMD_PASTE,
        IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED);
    enable = SendMessageW(Globals.hEdit, EM_GETSEL, 0, 0);
    enable = (HIWORD(enable) == LOWORD(enable)) ? MF_GRAYED : MF_ENABLED;
    EnableMenuItem(menu, CMD_CUT, enable);
    EnableMenuItem(menu, CMD_COPY, enable);
    EnableMenuItem(menu, CMD_DELETE, enable);
    
    EnableMenuItem(menu, CMD_SELECT_ALL,
        GetWindowTextLengthW(Globals.hEdit) ? MF_ENABLED : MF_GRAYED);
}

static LPWSTR NOTEPAD_StrRStr(LPWSTR pszSource, LPWSTR pszLast, LPWSTR pszSrch)
{
    int len = lstrlenW(pszSrch);
    pszLast--;
    while (pszLast >= pszSource)
    {
        if (StrCmpNW(pszLast, pszSrch, len) == 0)
            return pszLast;
        pszLast--;
    }
    return NULL;
}

/***********************************************************************
 * The user activated the Find dialog
 */
void NOTEPAD_DoFind(FINDREPLACEW *fr)
{
    LPWSTR content;
    LPWSTR found;
    int len = lstrlenW(fr->lpstrFindWhat);
    int fileLen;
    DWORD pos;

    fileLen = GetWindowTextLengthW(Globals.hEdit) + 1;
    content = HeapAlloc(GetProcessHeap(), 0, fileLen * sizeof(WCHAR));
    if (!content) return;
    GetWindowTextW(Globals.hEdit, content, fileLen);

    SendMessageW(Globals.hEdit, EM_GETSEL, 0, (LPARAM)&pos);
    switch (fr->Flags & (FR_DOWN|FR_MATCHCASE))
    {
        case 0:
            found = StrRStrIW(content, content+pos-len, fr->lpstrFindWhat);
            break;
        case FR_DOWN:
            found = StrStrIW(content+pos, fr->lpstrFindWhat);
            break;
        case FR_MATCHCASE:
            found = NOTEPAD_StrRStr(content, content+pos-len, fr->lpstrFindWhat);
            break;
        case FR_DOWN|FR_MATCHCASE:
            found = StrStrW(content+pos, fr->lpstrFindWhat);
            break;
        default:    /* shouldn't happen */
            return;
    }
    HeapFree(GetProcessHeap(), 0, content);

    if (found == NULL)
    {
        DIALOG_StringMsgBox(Globals.hFindReplaceDlg, STRING_NOTFOUND, fr->lpstrFindWhat,
            MB_ICONINFORMATION|MB_OK);
        return;
    }

    SendMessageW(Globals.hEdit, EM_SETSEL, found - content, found - content + len);
}

static void NOTEPAD_DoReplace(FINDREPLACEW *fr)
{
    LPWSTR content;
    int len = lstrlenW(fr->lpstrFindWhat);
    int fileLen;
    DWORD pos;
    DWORD pos_start;

    fileLen = GetWindowTextLengthW(Globals.hEdit) + 1;
    content = HeapAlloc(GetProcessHeap(), 0, fileLen * sizeof(WCHAR));
    if (!content) return;
    GetWindowTextW(Globals.hEdit, content, fileLen);

    SendMessageW(Globals.hEdit, EM_GETSEL, (WPARAM)&pos_start, (LPARAM)&pos);
    switch (fr->Flags & (FR_DOWN|FR_MATCHCASE))
    {
        case FR_DOWN:
            if ( pos-pos_start == len && StrCmpNIW(fr->lpstrFindWhat, content+pos_start, len) == 0)
                SendMessageW(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)fr->lpstrReplaceWith);
            break;
        case FR_DOWN|FR_MATCHCASE:
            if ( pos-pos_start == len && StrCmpNW(fr->lpstrFindWhat, content+pos_start, len) == 0)
                SendMessageW(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)fr->lpstrReplaceWith);
            break;
        default:    /* shouldn't happen */
            return;
    }
    HeapFree(GetProcessHeap(), 0, content);

    NOTEPAD_DoFind(fr);
}

static void NOTEPAD_DoReplaceAll(FINDREPLACEW *fr)
{
    LPWSTR content;
    LPWSTR found;
    int len = lstrlenW(fr->lpstrFindWhat);
    int fileLen;
    DWORD pos;

    SendMessageW(Globals.hEdit, EM_SETSEL, 0, 0);
    while(TRUE){
        fileLen = GetWindowTextLengthW(Globals.hEdit) + 1;
        content = HeapAlloc(GetProcessHeap(), 0, fileLen * sizeof(WCHAR));
        if (!content) return;
        GetWindowTextW(Globals.hEdit, content, fileLen);

        SendMessageW(Globals.hEdit, EM_GETSEL, 0, (LPARAM)&pos);
        switch (fr->Flags & (FR_DOWN|FR_MATCHCASE))
        {
            case FR_DOWN:
                found = StrStrIW(content+pos, fr->lpstrFindWhat);
                break;
            case FR_DOWN|FR_MATCHCASE:
                found = StrStrW(content+pos, fr->lpstrFindWhat);
                break;
            default:    /* shouldn't happen */
                return;
        }
        HeapFree(GetProcessHeap(), 0, content);

        if(found == NULL)
        {
            SendMessageW(Globals.hEdit, EM_SETSEL, 0, 0);
            return;
        }
        SendMessageW(Globals.hEdit, EM_SETSEL, found - content, found - content + len);
        SendMessageW(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)fr->lpstrReplaceWith);
    }
}

/***********************************************************************
 *
 *           NOTEPAD_WndProc
 */
static LRESULT WINAPI NOTEPAD_WndProc(HWND hWnd, UINT msg, WPARAM wParam,
                               LPARAM lParam)
{
    if (msg == aFINDMSGSTRING)      /* not a constant so can't be used in switch */
    {
        FINDREPLACEW *fr = (FINDREPLACEW *)lParam;

        if (fr->Flags & FR_DIALOGTERM)
            Globals.hFindReplaceDlg = NULL;
        if (fr->Flags & FR_FINDNEXT)
        {
            Globals.lastFind = *fr;
            NOTEPAD_DoFind(fr);
        }
        if (fr->Flags & FR_REPLACE)
        {
            Globals.lastFind = *fr;
            NOTEPAD_DoReplace(fr);
        }
        if (fr->Flags & FR_REPLACEALL)
        {
            Globals.lastFind = *fr;
            NOTEPAD_DoReplaceAll(fr);
        }
        return 0;
    }
    
    switch (msg) {

    case WM_CREATE:
    {
        static const WCHAR editW[] = { 'e','d','i','t',0 };
        DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL |
                        ES_AUTOVSCROLL | ES_MULTILINE | ES_NOHIDESEL;
        RECT rc;
        GetClientRect(hWnd, &rc);

        if (!Globals.bWrapLongLines) dwStyle |= WS_HSCROLL | ES_AUTOHSCROLL;

        Globals.hEdit = CreateWindowExW(WS_EX_CLIENTEDGE, editW, NULL,
                             dwStyle, 0, 0, rc.right, rc.bottom, hWnd,
                             NULL, Globals.hInstance, NULL);

        Globals.hFont = CreateFontIndirectW(&Globals.lfFont);
        SendMessageW(Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, FALSE);
        SendMessageW(Globals.hEdit, EM_LIMITTEXT, 0, 0);
        break;
    }

    case WM_COMMAND:
        NOTEPAD_MenuCommand(LOWORD(wParam));
        break;

    case WM_DESTROYCLIPBOARD:
        /*MessageBoxW(Globals.hMainWnd, "Empty clipboard", "Debug", MB_ICONEXCLAMATION);*/
        break;

    case WM_CLOSE:
        if (DoCloseFile()) {
            DestroyWindow(hWnd);
        }
        break;

    case WM_QUERYENDSESSION:
        if (DoCloseFile()) {
            return 1;
        }
        break;

    case WM_DESTROY:
        NOTEPAD_SaveSettingToRegistry();

        PostQuitMessage(0);
        break;

    case WM_SIZE:
        SetWindowPos(Globals.hEdit, NULL, 0, 0, LOWORD(lParam), HIWORD(lParam),
                     SWP_NOOWNERZORDER | SWP_NOZORDER);
        break;

    case WM_SETFOCUS:
        SetFocus(Globals.hEdit);
        break;

    case WM_DROPFILES:
    {
        WCHAR szFileName[MAX_PATH];
        HANDLE hDrop = (HANDLE) wParam;

        DragQueryFileW(hDrop, 0, szFileName, ARRAY_SIZE(szFileName));
        DragFinish(hDrop);
        DoOpenFile(szFileName, ENCODING_AUTO);
        break;
    }
    
    case WM_INITMENUPOPUP:
        NOTEPAD_InitMenuPopup((HMENU)wParam, lParam);
        break;

    default:
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }
    return 0;
}

static int AlertFileDoesNotExist(LPCWSTR szFileName)
{
   int nResult;
   WCHAR szMessage[MAX_STRING_LEN];
   WCHAR szResource[MAX_STRING_LEN];

   LoadStringW(Globals.hInstance, STRING_DOESNOTEXIST, szResource, ARRAY_SIZE(szResource));
   wsprintfW(szMessage, szResource, szFileName);

   LoadStringW(Globals.hInstance, STRING_ERROR, szResource, ARRAY_SIZE(szResource));

   nResult = MessageBoxW(Globals.hMainWnd, szMessage, szResource,
                         MB_ICONEXCLAMATION | MB_YESNOCANCEL);

   return(nResult);
}

static void HandleCommandLine(LPWSTR cmdline)
{
    WCHAR delimiter;
    int opt_print=0;
    
    /* skip white space */
    while (*cmdline == ' ') cmdline++;

    /* skip executable name */
    delimiter = (*cmdline == '"' ? '"' : ' ');

    if (*cmdline == delimiter) cmdline++;

    while (*cmdline && *cmdline != delimiter) cmdline++;

    if (*cmdline == delimiter) cmdline++;

    while (*cmdline == ' ' || *cmdline == '-' || *cmdline == '/')
    {
        WCHAR option;

        if (*cmdline++ == ' ') continue;

        option = *cmdline;
        if (option) cmdline++;
        while (*cmdline == ' ') cmdline++;

        switch(option)
        {
            case 'p':
            case 'P':
                opt_print=1;
                break;
        }
    }

    if (*cmdline)
    {
        /* file name is passed in the command line */
        LPCWSTR file_name;
        BOOL file_exists;
        WCHAR buf[MAX_PATH];

        if (cmdline[0] == '"')
        {
            WCHAR* wc;
            cmdline++;
            wc=cmdline;
            /* Note: Double-quotes are not allowed in Windows filenames */
            while (*wc && *wc != '"') wc++;
            /* On Windows notepad ignores further arguments too */
            *wc = 0;
        }

        if (FileExists(cmdline))
        {
            file_exists = TRUE;
            file_name = cmdline;
        }
        else
        {
            static const WCHAR txtW[] = { '.','t','x','t',0 };

            /* try to find file with ".txt" extension */
            if (strchrW(PathFindFileNameW(cmdline), '.'))
            {
                file_exists = FALSE;
                file_name = cmdline;
            }
            else
            {
                lstrcpynW(buf, cmdline, MAX_PATH - lstrlenW(txtW) - 1);
                lstrcatW(buf, txtW);
                file_name = buf;
                file_exists = FileExists(buf);
            }
        }

        if (file_exists)
        {
            DoOpenFile(file_name, ENCODING_AUTO);
            InvalidateRect(Globals.hMainWnd, NULL, FALSE);
            if (opt_print)
                DIALOG_FilePrint();
        }
        else
        {
            switch (AlertFileDoesNotExist(file_name)) {
            case IDYES:
                SetFileNameAndEncoding(file_name, ENCODING_ANSI);
                UpdateWindowCaption();
                break;

            case IDNO:
                break;

            case IDCANCEL:
                DestroyWindow(Globals.hMainWnd);
                break;
            }
        }
     }
}

/***********************************************************************
 *
 *           WinMain
 */
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
{
    MSG msg;
    HACCEL hAccel;
    WNDCLASSEXW class;
    HMONITOR monitor;
    MONITORINFO info;
    INT x, y;
    static const WCHAR className[] = {'N','o','t','e','p','a','d',0};
    static const WCHAR winName[]   = {'N','o','t','e','p','a','d',0};

    aFINDMSGSTRING = RegisterWindowMessageW(FINDMSGSTRINGW);

    ZeroMemory(&Globals, sizeof(Globals));
    Globals.hInstance       = hInstance;
    NOTEPAD_LoadSettingFromRegistry();

    ZeroMemory(&class, sizeof(class));
    class.cbSize        = sizeof(class);
    class.lpfnWndProc   = NOTEPAD_WndProc;
    class.hInstance     = Globals.hInstance;
    class.hIcon         = LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(IDI_NOTEPAD));
    class.hIconSm       = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_NOTEPAD), IMAGE_ICON,
                                     GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
                                     LR_SHARED);
    class.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
    class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    class.lpszMenuName  = MAKEINTRESOURCEW(MAIN_MENU);
    class.lpszClassName = className;

    if (!RegisterClassExW(&class)) return FALSE;

    /* Setup windows */

    monitor = MonitorFromRect( &main_rect, MONITOR_DEFAULTTOPRIMARY );
    info.cbSize = sizeof(info);
    GetMonitorInfoW( monitor, &info );

    x = main_rect.left;
    y = main_rect.top;
    if (main_rect.left >= info.rcWork.right ||
        main_rect.top >= info.rcWork.bottom ||
        main_rect.right < info.rcWork.left ||
        main_rect.bottom < info.rcWork.top)
        x = y = CW_USEDEFAULT;

    Globals.hMainWnd =
        CreateWindowW(className, winName, WS_OVERLAPPEDWINDOW, x, y,
                      main_rect.right - main_rect.left, main_rect.bottom - main_rect.top,
                      NULL, NULL, Globals.hInstance, NULL);
    if (!Globals.hMainWnd)
    {
        ShowLastError();
        ExitProcess(1);
    }

    NOTEPAD_InitData();
    DIALOG_FileNew();

    ShowWindow(Globals.hMainWnd, show);
    UpdateWindow(Globals.hMainWnd);
    DragAcceptFiles(Globals.hMainWnd, TRUE);

    HandleCommandLine(GetCommandLineW());

    hAccel = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(ID_ACCEL));

    while (GetMessageW(&msg, 0, 0, 0))
    {
        if (!TranslateAcceleratorW(Globals.hMainWnd, hAccel, &msg) && !IsDialogMessageW(Globals.hFindReplaceDlg, &msg))
	{
	    TranslateMessage(&msg);
            DispatchMessageW(&msg);
	}
    }
    return msg.wParam;
}