/*
 * PROJECT:         ReactOS Applications Manager
 * LICENSE:         GPL - See COPYING in the top level directory
 * FILE:            base/applications/rapps/winmain.c
 * PURPOSE:         Main program
 * PROGRAMMERS:     Dmitry Chapyshev (dmitry@reactos.org)
 */

#include "rapps.h"

HWND hMainWnd;
HINSTANCE hInst;
HIMAGELIST hImageListView = NULL;
HIMAGELIST hImageTreeView = NULL;
INT SelectedEnumType = ENUM_ALL_COMPONENTS;
SETTINGS_INFO SettingsInfo;


VOID
FillDafaultSettings(PSETTINGS_INFO pSettingsInfo)
{
    pSettingsInfo->bSaveWndPos = TRUE;
    pSettingsInfo->bUpdateAtStart = FALSE;
    pSettingsInfo->bLogEnabled = TRUE;
    wcscpy(pSettingsInfo->szDownloadDir, L"C:\\Downloads");
    pSettingsInfo->bDelInstaller = FALSE;

    pSettingsInfo->Maximized = FALSE;
    pSettingsInfo->Left = 0;
    pSettingsInfo->Top = 0;
    pSettingsInfo->Right = 680;
    pSettingsInfo->Bottom = 450;
}

static BOOL
LoadSettings(VOID)
{
    HKEY hKey;
    DWORD dwSize;

    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\ReactOS\\rapps", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
    {
        dwSize = sizeof(SETTINGS_INFO);
        if (RegQueryValueExW(hKey, L"Settings", NULL, NULL, (LPBYTE)&SettingsInfo, &dwSize) == ERROR_SUCCESS)
        {
            RegCloseKey(hKey);
            return TRUE;
        }

        RegCloseKey(hKey);
    }

    return FALSE;
}

VOID
SaveSettings(HWND hwnd)
{
    WINDOWPLACEMENT wp;
    HKEY hKey;

    if (SettingsInfo.bSaveWndPos)
    {
        wp.length = sizeof(WINDOWPLACEMENT);
        GetWindowPlacement(hwnd, &wp);

        SettingsInfo.Left = wp.rcNormalPosition.left;
        SettingsInfo.Top  = wp.rcNormalPosition.top;
        SettingsInfo.Right  = wp.rcNormalPosition.right;
        SettingsInfo.Bottom = wp.rcNormalPosition.bottom;
        SettingsInfo.Maximized = (IsZoomed(hwnd) || (wp.flags & WPF_RESTORETOMAXIMIZED));
    }

    if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"Software\\ReactOS\\rapps", 0, NULL,
        REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS)
    {
        RegSetValueEx(hKey, L"Settings", 0, REG_BINARY, (LPBYTE)&SettingsInfo, sizeof(SETTINGS_INFO));
        RegCloseKey(hKey);
    }
}

VOID
FreeInstalledAppList(VOID)
{
    INT Count = ListView_GetItemCount(hListView) - 1;
    PINSTALLED_INFO Info;

    while (Count >= 0)
    {
        Info = ListViewGetlParam(Count);
        if (Info)
        {
            RegCloseKey(Info->hSubKey);
            HeapFree(GetProcessHeap(), 0, Info);
        }
        Count--;
    }
}

BOOL
CALLBACK
EnumInstalledAppProc(INT ItemIndex, LPWSTR lpName, INSTALLED_INFO Info)
{
    PINSTALLED_INFO ItemInfo;
    WCHAR szText[MAX_PATH];
    INT Index;

    ItemInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(INSTALLED_INFO));
    if (!ItemInfo) return FALSE;

    *ItemInfo = Info;

    Index = ListViewAddItem(ItemIndex, 0, lpName, (LPARAM)ItemInfo);

    /* Get version info */
    GetApplicationString((HKEY)ItemInfo->hSubKey, L"DisplayVersion", szText);
    ListView_SetItemText(hListView, Index, 1, szText);
    /* Get comments */
    GetApplicationString((HKEY)ItemInfo->hSubKey, L"Comments", szText);
    ListView_SetItemText(hListView, Index, 2, szText);

    return TRUE;
}

VOID
FreeAvailableAppList(VOID)
{
    INT Count = ListView_GetItemCount(hListView) - 1;
    PVOID Info;

    while (Count >= 0)
    {
        Info = ListViewGetlParam(Count);
        if (Info)
            HeapFree(GetProcessHeap(), 0, Info);
        Count--;
    }
}

BOOL
CALLBACK
EnumAvailableAppProc(APPLICATION_INFO Info)
{
    PAPPLICATION_INFO ItemInfo;
    INT Index;

    if (!IsInstalledApplication(Info.szRegName, FALSE) &&
        !IsInstalledApplication(Info.szRegName, TRUE))
    {
        ItemInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(APPLICATION_INFO));
        if (!ItemInfo) return FALSE;

        *ItemInfo = Info;

        Index = ListViewAddItem(Info.Category, 0, Info.szName, (LPARAM)ItemInfo);
        ListView_SetItemText(hListView, Index, 1, Info.szVersion);
        ListView_SetItemText(hListView, Index, 2, Info.szDesc);
    }

    return TRUE;
}

VOID
UpdateApplicationsList(INT EnumType)
{
    WCHAR szBuffer1[MAX_STR_LEN], szBuffer2[MAX_STR_LEN];
    HICON hIcon;

    if (hImageListView) ImageList_Destroy(hImageListView);
    (VOID) ListView_DeleteAllItems(hListView);

    /* Create image list */
    hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE,
                                      LISTVIEW_ICON_SIZE,
                                      GetSystemColorDepth() | ILC_MASK,
                                      0, 1);

    hIcon = LoadImage(hInst,
                      MAKEINTRESOURCE(IDI_MAIN),
                      IMAGE_ICON,
                      LISTVIEW_ICON_SIZE,
                      LISTVIEW_ICON_SIZE,
                      LR_CREATEDIBSECTION);

    ImageList_AddIcon(hImageListView, hIcon);
    DestroyIcon(hIcon);

    if (EnumType == -1) EnumType = SelectedEnumType;

    if (IS_INSTALLED_ENUM(SelectedEnumType))
        FreeInstalledAppList();
    else if (IS_AVAILABLE_ENUM(SelectedEnumType))
        FreeAvailableAppList();

    if (IS_INSTALLED_ENUM(EnumType))
    {
        /* Enum installed applications and updates */
        EnumInstalledApplications(EnumType, TRUE, EnumInstalledAppProc);
        EnumInstalledApplications(EnumType, FALSE, EnumInstalledAppProc);
    }
    else if (IS_AVAILABLE_ENUM(EnumType))
    {
        /* Enum availabled applications */
        EnumAvailableApplications(EnumType, EnumAvailableAppProc);
    }

    /* Set image list for ListView */
    (VOID) ListView_SetImageList(hListView, hImageListView, LVSIL_SMALL);

    SelectedEnumType = EnumType;

    LoadStringW(hInst, IDS_APPS_COUNT, szBuffer2, sizeof(szBuffer2) / sizeof(WCHAR));
    swprintf(szBuffer1, szBuffer2, ListView_GetItemCount(hListView));
    SetStatusBarText(szBuffer1);

    SetWelcomeText();
}

VOID
InitApplicationsList(VOID)
{
    WCHAR szText[MAX_STR_LEN];

    /* Add columns to ListView */
    LoadStringW(hInst, IDS_APP_NAME, szText, sizeof(szText) / sizeof(WCHAR));
    ListViewAddColumn(0, szText, 200, LVCFMT_LEFT);

    LoadStringW(hInst, IDS_APP_INST_VERSION, szText, sizeof(szText) / sizeof(WCHAR));
    ListViewAddColumn(1, szText, 90, LVCFMT_RIGHT);

    LoadStringW(hInst, IDS_APP_DESCRIPTION, szText, sizeof(szText) / sizeof(WCHAR));
    ListViewAddColumn(3, szText, 250, LVCFMT_LEFT);

    UpdateApplicationsList(ENUM_ALL_COMPONENTS);
}

HTREEITEM
AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex)
{
    WCHAR szText[MAX_STR_LEN];
    INT Index;
    HICON hIcon;

    hIcon = LoadImage(hInst,
                      MAKEINTRESOURCE(IconIndex),
                      IMAGE_ICON,
                      TREEVIEW_ICON_SIZE,
                      TREEVIEW_ICON_SIZE,
                      LR_CREATEDIBSECTION);

    Index = ImageList_AddIcon(hImageTreeView, hIcon);
    DestroyIcon(hIcon);

    LoadStringW(hInst, TextIndex, szText, sizeof(szText) / sizeof(TCHAR));

    return TreeViewAddItem(hRootItem, szText, Index, Index, TextIndex);
}

VOID
InitCategoriesList(VOID)
{
    HTREEITEM hRootItem1, hRootItem2;

    /* Create image list */
    hImageTreeView = ImageList_Create(TREEVIEW_ICON_SIZE,
                                      TREEVIEW_ICON_SIZE,
                                      GetSystemColorDepth() | ILC_MASK,
                                      0, 1);

    hRootItem1 = AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY);
    AddCategory(hRootItem1, IDS_APPLICATIONS, IDI_APPS);
    AddCategory(hRootItem1, IDS_UPDATES, IDI_APPUPD);

    hRootItem2 = AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY);
    AddCategory(hRootItem2, IDS_CAT_AUDIO, IDI_CAT_AUDIO);
    AddCategory(hRootItem2, IDS_CAT_VIDEO, IDI_CAT_VIDEO);
    AddCategory(hRootItem2, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS);
    AddCategory(hRootItem2, IDS_CAT_GAMES, IDI_CAT_GAMES);
    AddCategory(hRootItem2, IDS_CAT_INTERNET, IDI_CAT_INTERNET);
    AddCategory(hRootItem2, IDS_CAT_OFFICE, IDI_CAT_OFFICE);
    AddCategory(hRootItem2, IDS_CAT_DEVEL, IDI_CAT_DEVEL);
    AddCategory(hRootItem2, IDS_CAT_EDU, IDI_CAT_EDU);
    AddCategory(hRootItem2, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER);
    AddCategory(hRootItem2, IDS_CAT_FINANCE, IDI_CAT_FINANCE);
    AddCategory(hRootItem2, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE);
    AddCategory(hRootItem2, IDS_CAT_TOOLS, IDI_CAT_TOOLS);
    AddCategory(hRootItem2, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS);
    AddCategory(hRootItem2, IDS_CAT_LIBS, IDI_CAT_LIBS);
    AddCategory(hRootItem2, IDS_CAT_OTHER, IDI_CAT_OTHER);

    (VOID) TreeView_SetImageList(hTreeView, hImageTreeView, TVSIL_NORMAL);

    (VOID) TreeView_Expand(hTreeView, hRootItem2, TVE_EXPAND);
    (VOID) TreeView_Expand(hTreeView, hRootItem1, TVE_EXPAND);

    (VOID) TreeView_SelectItem(hTreeView, hRootItem1);
}

BOOL
InitControls(HWND hwnd)
{
    if (SettingsInfo.bSaveWndPos)
    {
        MoveWindow(hwnd, SettingsInfo.Left, SettingsInfo.Top,
                   SettingsInfo.Right - SettingsInfo.Left,
                   SettingsInfo.Bottom - SettingsInfo.Top, TRUE);

        if (SettingsInfo.Maximized) ShowWindow(hwnd, SW_MAXIMIZE);
    }

    if (CreateStatusBar(hwnd) &&
        CreateToolBar(hwnd) &&
        CreateListView(hwnd) &&
        CreateTreeView(hwnd) &&
        CreateRichEdit(hwnd) &&
        CreateVSplitBar(hwnd) &&
        CreateHSplitBar(hwnd))
    {
        WCHAR szBuffer1[MAX_STR_LEN], szBuffer2[MAX_STR_LEN];

        InitApplicationsList();

        InitCategoriesList();

        LoadStringW(hInst, IDS_APPS_COUNT, szBuffer2, sizeof(szBuffer2) / sizeof(WCHAR));
        swprintf(szBuffer1, szBuffer2, ListView_GetItemCount(hListView));
        SetStatusBarText(szBuffer1);
        return TRUE;
    }

    return FALSE;
}

VOID
MainWndOnCommand(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    WORD wCommand = LOWORD(wParam);

    if (lParam == (LPARAM)hSearchBar)
    {
        WCHAR szBuf[MAX_STR_LEN];

        switch (HIWORD(wParam))
        {
            case EN_SETFOCUS:
            {
                WCHAR szWndText[MAX_STR_LEN];

                LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, sizeof(szBuf) / sizeof(WCHAR));
                GetWindowTextW(hSearchBar, szWndText, MAX_STR_LEN);
                if (wcscmp(szBuf, szWndText) == 0) SetWindowTextW(hSearchBar, L"");
            }
            break;

            case EN_KILLFOCUS:
            {
                GetWindowTextW(hSearchBar, szBuf, MAX_STR_LEN);
                if (wcslen(szBuf) < 1)
                {
                    LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, sizeof(szBuf) / sizeof(WCHAR));
                    SetWindowTextW(hSearchBar, szBuf);
                }
            }
            break;

            case EN_CHANGE:
                /* TODO: Implement search */
                break;
        }

        return;
    }

    switch (wCommand)
    {
        case ID_OPEN_LINK:
            ShellExecuteW(hwnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE);
            HeapFree(GetProcessHeap(), 0, pLink);
            break;

        case ID_COPY_LINK:
            CopyTextToClipboard(pLink);
            HeapFree(GetProcessHeap(), 0, pLink);
            break;

        case ID_SETTINGS:
            CreateSettingsDlg(hwnd);
            break;

        case ID_EXIT:
            PostMessageW(hwnd, WM_CLOSE, 0, 0);
            break;

        case ID_INSTALL:
            if (DownloadApplication(-1))
            /* TODO: Implement install dialog
             *   if (InstallApplication(-1))
             */
                UpdateApplicationsList(-1);
            break;

        case ID_UNINSTALL:
            if (UninstallApplication(-1, FALSE))
                UpdateApplicationsList(-1);
            break;

        case ID_MODIFY:
            if (UninstallApplication(-1, TRUE))
                UpdateApplicationsList(-1);
            break;

        case ID_REGREMOVE:
            RemoveAppFromRegistry(-1);
            break;

        case ID_REFRESH:
            UpdateApplicationsList(-1);
            break;

        case ID_HELP:
            MessageBoxW(hwnd, L"Help not implemented yet", NULL, MB_OK);
            break;

        case ID_ABOUT:
            ShowAboutDialog();
            break;
    }
}

VOID
MainWndOnSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    HDWP hdwp = BeginDeferWindowPos(5);
    INT SearchBarWidth = GetWindowWidth(hSearchBar);
    INT RichPos = GetWindowHeight(hRichEdit);
    INT NewPos = HIWORD(lParam) - (RichPos + SPLIT_WIDTH + GetWindowHeight(hStatusBar));
    INT VSplitterPos;

    /* Size status bar */
    SendMessage(hStatusBar, WM_SIZE, 0, 0);

    /* Size tool bar */
    SendMessage(hToolBar, TB_AUTOSIZE, 0, 0);

    /* Size SearchBar */
    MoveWindow(hSearchBar, LOWORD(lParam) - SearchBarWidth - 4, 5, SearchBarWidth, 22, TRUE);

    /*
     * HIWORD(lParam) - Height of main window
     * LOWORD(lParam) - Width of main window
     */

    /* Size vertical splitter bar */
    DeferWindowPos(hdwp,
                   hVSplitter,
                   0,
                   (VSplitterPos = GetWindowWidth(hTreeView)),
                   GetWindowHeight(hToolBar),
                   SPLIT_WIDTH,
                   HIWORD(lParam) - GetWindowHeight(hToolBar) - GetWindowHeight(hStatusBar),
                   SWP_NOZORDER|SWP_NOACTIVATE);

    /* Size TreeView */
    DeferWindowPos(hdwp,
                   hTreeView,
                   0,
                   0,
                   GetWindowHeight(hToolBar),
                   VSplitterPos,
                   HIWORD(lParam) - GetWindowHeight(hToolBar) - GetWindowHeight(hStatusBar),
                   SWP_NOZORDER|SWP_NOACTIVATE);

    while (NewPos < SPLIT_WIDTH + GetWindowHeight(hToolBar))
    {
        RichPos--;
        NewPos = HIWORD(lParam) - (RichPos +
                 SPLIT_WIDTH + GetWindowHeight(hStatusBar));
    }
    SetHSplitterPos(NewPos);

    /* Size ListView */
    DeferWindowPos(hdwp,
                   hListView,
                   0,
                   VSplitterPos + SPLIT_WIDTH,
                   GetWindowHeight(hToolBar),
                   LOWORD(lParam) - (VSplitterPos + SPLIT_WIDTH),
                   GetHSplitterPos() - GetWindowHeight(hToolBar),
                   SWP_NOZORDER|SWP_NOACTIVATE);

    /* Size RichEdit */
    DeferWindowPos(hdwp,
                   hRichEdit,
                   0,
                   VSplitterPos + SPLIT_WIDTH,
                   GetHSplitterPos() + SPLIT_WIDTH,
                   LOWORD(lParam) - (VSplitterPos + SPLIT_WIDTH),
                   RichPos,
                   SWP_NOZORDER|SWP_NOACTIVATE);

    /* Size horizontal splitter bar */
    DeferWindowPos(hdwp,
                   hHSplitter,
                   0,
                   VSplitterPos + SPLIT_WIDTH,
                   GetHSplitterPos(),
                   LOWORD(lParam) - (VSplitterPos + SPLIT_WIDTH),
                   SPLIT_WIDTH,
                   SWP_NOZORDER|SWP_NOACTIVATE);

    EndDeferWindowPos(hdwp);
}

LRESULT CALLBACK
MainWindowProc(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch (Msg)
    {
        case WM_CREATE:
            if (!InitControls(hwnd))
                PostMessage(hwnd, WM_CLOSE, 0, 0);

            if (SettingsInfo.bUpdateAtStart)
                UpdateAppsDB();
            break;

        case WM_COMMAND:
            MainWndOnCommand(hwnd, wParam, lParam);
            break;

        case WM_NOTIFY:
        {
            LPNMHDR data = (LPNMHDR)lParam;

            switch (data->code)
            {
                case TVN_SELCHANGED:
                {
                    if (data->hwndFrom == hTreeView)
                    {
                        switch (((LPNMTREEVIEW)lParam)->itemNew.lParam)
                        {
                            case IDS_INSTALLED:
                                UpdateApplicationsList(ENUM_ALL_COMPONENTS);
                                break;

                            case IDS_APPLICATIONS:
                                UpdateApplicationsList(ENUM_APPLICATIONS);
                                break;

                            case IDS_UPDATES:
                                UpdateApplicationsList(ENUM_UPDATES);
                                break;

                            case IDS_AVAILABLEFORINST:
                                UpdateApplicationsList(ENUM_ALL_AVAILABLE);
                                break;

                            case IDS_CAT_AUDIO:
                                UpdateApplicationsList(ENUM_CAT_AUDIO);
                                break;

                            case IDS_CAT_DEVEL:
                                UpdateApplicationsList(ENUM_CAT_DEVEL);
                                break;

                            case IDS_CAT_DRIVERS:
                                UpdateApplicationsList(ENUM_CAT_DRIVERS);
                                break;

                            case IDS_CAT_EDU:
                                UpdateApplicationsList(ENUM_CAT_EDU);
                                break;

                            case IDS_CAT_ENGINEER:
                                UpdateApplicationsList(ENUM_CAT_ENGINEER);
                                break;

                            case IDS_CAT_FINANCE:
                                UpdateApplicationsList(ENUM_CAT_FINANCE);
                                break;

                            case IDS_CAT_GAMES:
                                UpdateApplicationsList(ENUM_CAT_GAMES);
                                break;

                            case IDS_CAT_GRAPHICS:
                                UpdateApplicationsList(ENUM_CAT_GRAPHICS);
                                break;

                            case IDS_CAT_INTERNET:
                                UpdateApplicationsList(ENUM_CAT_INTERNET);
                                break;

                            case IDS_CAT_LIBS:
                                UpdateApplicationsList(ENUM_CAT_LIBS);
                                break;

                            case IDS_CAT_OFFICE:
                                UpdateApplicationsList(ENUM_CAT_OFFICE);
                                break;

                            case IDS_CAT_OTHER:
                                UpdateApplicationsList(ENUM_CAT_OTHER);
                                break;

                            case IDS_CAT_SCIENCE:
                                UpdateApplicationsList(ENUM_CAT_SCIENCE);
                                break;

                            case IDS_CAT_TOOLS:
                                UpdateApplicationsList(ENUM_CAT_TOOLS);
                                break;

                            case IDS_CAT_VIDEO:
                                UpdateApplicationsList(ENUM_CAT_VIDEO);
                                break;
                        }
                    }
                }
                break;

                case LVN_KEYDOWN:
                {
                    LPNMLVKEYDOWN pnkd = (LPNMLVKEYDOWN) lParam;

                    if (pnkd->hdr.hwndFrom == hListView)
                    {
                        INT ItemIndex = (INT) SendMessage(hListView, LVM_GETNEXTITEM, -1, LVNI_FOCUSED);

                        if (pnkd->wVKey == VK_UP) ItemIndex -= 1;
                        if (pnkd->wVKey == VK_DOWN) ItemIndex += 1;

                        if (IS_INSTALLED_ENUM(SelectedEnumType))
                            ShowInstalledAppInfo(ItemIndex);
                        if (IS_AVAILABLE_ENUM(SelectedEnumType))
                            ShowAvailableAppInfo(ItemIndex);
                    }
                }
                break;

                case LVN_COLUMNCLICK:
                {
                    LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam;

                    (VOID) ListView_SortItems(hListView, ListViewCompareFunc, pnmv->iSubItem);
                    bAscending = !bAscending;
                }
                break;

                case NM_CLICK:
                    if (data->hwndFrom == hListView)
                    {
                        if (IS_INSTALLED_ENUM(SelectedEnumType))
                            ShowInstalledAppInfo(-1);
                        if (IS_AVAILABLE_ENUM(SelectedEnumType))
                            ShowAvailableAppInfo(-1);
                    }
                    break;

                case NM_RCLICK:
                    if (data->hwndFrom == hListView)
                        ShowPopupMenu(hListView, IDR_APPLICATIONMENU);
                    break;

                case EN_LINK:
                    RichEditOnLink(hwnd, (ENLINK*)lParam);
                    break;

                case TTN_GETDISPINFO:
                    ToolBarOnGetDispInfo((LPTOOLTIPTEXT)lParam);
                    break;
            }
        }
        break;

        case WM_PAINT:
        break;

        case WM_SIZE:
        {
            if ((GetClientWindowHeight(hMainWnd) - GetWindowHeight(hStatusBar) - SPLIT_WIDTH) < GetHSplitterPos())
            {
                INT NewSplitPos = GetClientWindowHeight(hwnd) - 100 - GetWindowHeight(hStatusBar) - SPLIT_WIDTH;
                if (NewSplitPos > GetWindowHeight(hToolBar) + SPLIT_WIDTH)
                    SetHSplitterPos(NewSplitPos);
            }

            MainWndOnSize(hwnd, wParam, lParam);
        }
        break;

        case WM_SIZING:
        {
            int RichEditHeight = GetWindowHeight(hRichEdit);
            LPRECT pRect = (LPRECT)lParam;

            while (RichEditHeight <= 100)
            {
                if (GetHSplitterPos() - 1 < GetWindowHeight(hToolBar) + GetWindowHeight(hListView) + SPLIT_WIDTH)
                    break;
                SetHSplitterPos(GetHSplitterPos() - 1);
                RichEditHeight++;
            }

            if (pRect->right-pRect->left < 565)
                pRect->right = pRect->left + 565;

            if (pRect->bottom-pRect->top < 300)
                pRect->bottom = pRect->top + 300;
            return TRUE;
        }

        case WM_DESTROY:
        {
            ShowWindow(hwnd, SW_HIDE);
            SaveSettings(hwnd);

            FreeLogs();

            if (IS_AVAILABLE_ENUM(SelectedEnumType))
                FreeAvailableAppList();
            if (IS_INSTALLED_ENUM(SelectedEnumType))
                FreeInstalledAppList();
            if (hImageListView) ImageList_Destroy(hImageListView);
            if (hImageTreeView) ImageList_Destroy(hImageTreeView);

            PostQuitMessage(0);
            return 0;
        }
        break;
    }

    return DefWindowProc(hwnd, Msg, wParam, lParam);
}

int WINAPI
wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
{
    WNDCLASSEXW WndClass = {0};
    WCHAR szWindowClass[] = L"ROSAPPMGR";
    WCHAR szWindowName[MAX_STR_LEN];
    WCHAR szErrorText[MAX_STR_LEN];
    HANDLE hMutex = NULL;
    MSG Msg;

    hInst = hInstance;

    if (!IsUserAnAdmin())
    {
        LoadStringW(hInst, IDS_USER_NOT_ADMIN, szErrorText, sizeof(szErrorText) / sizeof(WCHAR));
        MessageBox(0, szErrorText, NULL, MB_OK | MB_ICONWARNING);
        return 1;
    }

    hMutex = CreateMutexW(NULL, FALSE, szWindowClass);
    if ((!hMutex) || (GetLastError() == ERROR_ALREADY_EXISTS))
    {
        /* If already started, it is found its window */
        HWND hWindow = FindWindowW(szWindowClass, NULL);

        /* Activate window */
        ShowWindow(hWindow, SW_SHOWNORMAL);
        SetForegroundWindow(hWindow);
        return 1;
    }

    if (!LoadSettings())
    {
        FillDafaultSettings(&SettingsInfo);
    }

    InitLogs();

    InitCommonControls();

    /* Create the window */
    WndClass.cbSize        = sizeof(WNDCLASSEXW);
    WndClass.lpszClassName = szWindowClass;
    WndClass.lpfnWndProc   = MainWindowProc;
    WndClass.hInstance     = hInstance;
    WndClass.style         = CS_HREDRAW | CS_VREDRAW;
    WndClass.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN));
    WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    WndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    WndClass.lpszMenuName  = MAKEINTRESOURCEW(IDR_MAINMENU);

    if (RegisterClassExW(&WndClass) == (ATOM)0) goto Exit;

    LoadStringW(hInst, IDS_APPTITLE, szWindowName, sizeof(szWindowName) / sizeof(WCHAR));

    hMainWnd = CreateWindowExW(WS_EX_WINDOWEDGE,
                               szWindowClass,
                               szWindowName,
                               WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                               CW_USEDEFAULT,
                               CW_USEDEFAULT,
                               680,
                               450,
                               NULL,
                               NULL,
                               hInstance,
                               NULL);

    if (!hMainWnd) goto Exit;

    /* Show it */
    ShowWindow(hMainWnd, SW_SHOW);
    UpdateWindow(hMainWnd);

    /* Message Loop */
    while (GetMessage(&Msg, NULL, 0, 0))
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

Exit:
    if (hMutex) CloseHandle(hMutex);

    return 0;
}