/*
 * Stall Shutdown ver 1.0 - Shutdown any windows computer after a user
 *                          specified amount of time, coded so I could watch 
 *                          family guy in bed and not have to get out again to 
 *                          turn my computer off. Should compile first time in
 *                          Bloodshed Dev-C++ 4.9.9.2
 *
 * Feel free to use this code for anything you want, just don't pass it off as your own.
 *
 * If you have any questions/comments, drop me a line,
 * Cheers
 *
 * Author:         Matt. "Dirty Monkey" Stevens
 * Circa:          15/08/08 16:34
 * Contact:        matt@DirtyMonkey.co.uk
 * Website:        www.DirtyMonkey.co.uk
 */
#include <windows.h>
#include <time.h>
#include "resource.h"

#define ID_EDIT_SECS    1 /* seconds input          */
#define ID_EDIT_MINS    2 /* minutes input          */
#define ID_EDIT_LEFT    3 /* time left display      */
#define ID_EDIT_TIME    4 /* current status display */
#define ID_EDIT_STATUS  5 /* current time display   */
#define ID_BUTTON       6 /* Start!/Abort! button   */
#define ID_RAD_BUTTON_1 7 /* seconds radio button   */
#define ID_RAD_BUTTON_2 8 /* minutes radio button   */

/*  prototypes  */
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
int CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

/*  Make the class name into a global variable  */
char szClassName[] = "WindowsApp";
HINSTANCE global_instance;
NOTIFYICONDATA structNID;
HICON hMainIcon;
HMENU hPopMenu;
HWND hwndEdit1, hwndEdit2, hwndEdit3, 
     hwndEdit4, hwndEdit5, hwndButton,
     hwndRad1,  hwndRad2,  hwndForce;

int WINAPI WinMain(HINSTANCE hThisInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpszArgument,
                   int nFunsterStil)

{
    HWND hwnd;                                /* This is the handle for our window */
    MSG messages;                             /* Here messages to the application are saved */
    WNDCLASSEX wincl;                         /* Data structure for the windowclass */
    global_instance = hThisInstance;          /* now we can use this everywhere */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof(WNDCLASSEX);
    wincl.hIcon = LoadIcon(hThisInstance, MAKEINTRESOURCE(ID_MYICON));
    wincl.hIconSm = LoadIcon(hThisInstance, MAKEINTRESOURCE(ID_MYICON));
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
    wincl.lpszMenuName = MAKEINTRESOURCE(ID_MYMENU);
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    wincl.hbrBackground = (HBRUSH) (COLOR_3DFACE+1);

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx(&wincl)) return(0);

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx(
           0,                            /* Extended possibilites for variation */
           szClassName,                  /* Classname */
           "Stall Shutdown ver 1.0",     /* Title Text */
           WS_MINIMIZEBOX | WS_SYSMENU,  /* a standard window with no maximize button */
           CW_USEDEFAULT,                /* Windows decides the position */
           CW_USEDEFAULT,                /* where the window ends up on the screen */
           304,                          /* The programs width */
           235,                          /* and height in pixels */
           HWND_DESKTOP,                 /* The window is a child-window to desktop */
           NULL,                         /* No menu */
           hThisInstance,                /* Program Instance handler */
           NULL                          /* No Window Creation data */
           );

	hMainIcon = LoadIcon(hThisInstance,(LPCTSTR) MAKEINTRESOURCE(ID_MYICON)); 

	structNID.cbSize = sizeof(NOTIFYICONDATA);
	structNID.hWnd = (HWND) hwnd;
	structNID.uID = ID_MYICON;
	structNID.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
	strcpy(structNID.szTip, "Stall Shutdown - by Matt. Stevens");
	structNID.hIcon = hMainIcon;
	structNID.uCallbackMessage = WM_USER_SHELLICON;
	
    /* put icon into the systray */
	Shell_NotifyIcon(NIM_ADD, &structNID); 

    /* Make the window visible on the screen */
    ShowWindow(hwnd, nFunsterStil);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage(&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return(messages.wParam);
}


/*  This function is called by the Windows function DispatchMessage()  */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static int flag = 0;              /* the current state of the button           */
    static int seconds;               /* time used in the 'Time left' edit control */
    char countdown_buffer[10];        /* holds the string to send to countdown     */
    POINT lpClickPoint;               /* location of mouse over systray icon       */
    HFONT font, font2;                /* fonts for the controls                    */

    time_t lt = time(NULL);           /* stores the value returned by time()       */
    struct tm *ptr = localtime(&lt);  /* stores data and time information          */
    char ascii_time[28];

    switch (message)
    {
        /* deals with messages from the icon in the systray */
		case WM_USER_SHELLICON: 
			switch(LOWORD(lParam)) 
			{ 
				case WM_LBUTTONDBLCLK: /* open the window */
					ShowWindow(hwnd, SW_NORMAL); 
					return(1);
				case WM_RBUTTONDOWN: /* open the menu */
					GetCursorPos(&lpClickPoint);
					
					/* place the window/menu there if needed */
					hPopMenu = CreatePopupMenu();
					InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, ID_EXIT, "&Exit");

					SetForegroundWindow(hwnd);
					TrackPopupMenu(hPopMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN,
                                             lpClickPoint.x, lpClickPoint.y, 0, hwnd, NULL);
					SendMessage(hwnd, WM_NULL, 0, 0);
					return(1);
			} 
			break; 
        case WM_CREATE:
        {
            /* set timer up for the time control */
            SetTimer(hwnd, 1, 1000, NULL);

            CreateWindow("BUTTON", "Shutdown in...", WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
                         5,          /* X Position */
                         5,         /* Y Position */
                         165,      /* X Width    */
                         70,      /* Y Height   */
                         hwnd, (HMENU) 200, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            CreateWindow("BUTTON", "Time left", WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
                         175,        /* X Position */
                         5,         /* Y Position */
                         115,      /* X Width    */
                         70,      /* Y Height   */
                         hwnd, (HMENU) 201, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            CreateWindow("BUTTON", "Status", WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
                         5,          /* X Position */
                         80,        /* Y Position */
                         165,      /* X Width    */
                         45,      /* Y Height   */
                         hwnd, (HMENU) 202, ((LPCREATESTRUCT) lParam)->hInstance, NULL);
                         
            CreateWindow("BUTTON", "Current Time", WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
                         5,          /* X Position */
                         130,       /* Y Position */
                         165,      /* X Width    */
                         45,      /* Y Height   */
                         hwnd, (HMENU) 203, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            hwndEdit1 = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", 
                       WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | ES_NUMBER,
                       15,           /* X Position */
                       25,          /* Y Position */
                       60,         /* X Width    */
                       20,        /* Y Height   */
                       hwnd, (HMENU) ID_EDIT_SECS, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            hwndEdit2 = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", 
                       WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | ES_NUMBER,
                       100,          /* X Position */
                       25,          /* Y Position */
                       60,         /* X Width    */
                       20,        /* Y Height   */
                       hwnd, (HMENU) ID_EDIT_MINS, ((LPCREATESTRUCT) lParam)->hInstance, NULL);
                       
            hwndEdit3 = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", 
                       WS_CHILD | WS_VISIBLE | ES_CENTER | ES_AUTOHSCROLL | WS_TABSTOP,
                       185,          /* X Position */
                       25,          /* Y Position */
                       95,         /* X Width    */
                       20,        /* Y Height   */
                       hwnd, (HMENU) ID_EDIT_LEFT, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            hwndEdit4 = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", 
                       WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP,
                       15,           /* X Position */
                       98,          /* Y Position */
                       145,        /* X Width    */
                       20,        /* Y Height   */
                       hwnd, (HMENU) ID_EDIT_STATUS, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            hwndEdit5 = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", 
                       WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP,
                       15,           /* X Position */
                       148,         /* Y Position */
                       145,        /* X Width    */
                       20,        /* Y Height   */
                       hwnd, (HMENU) ID_EDIT_TIME, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            hwndForce = CreateWindow("BUTTON", "Force Shutdown",
                       WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
                       180,          /* X Position */
                       90,          /* Y Position */
                       105,        /* X Width    */
                       15,        /* Y Height   */
                       hwnd, (HMENU) 204, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            hwndButton = CreateWindow("BUTTON", "Start!", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                         180,        /* X Position */
                         120,       /* Y Position */
                         100,      /* X Width    */
                         50,      /* Y Height   */
                         hwnd, (HMENU) ID_BUTTON, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            CreateWindow("STATIC", "seconds", WS_CHILD | WS_VISIBLE,
                         215,        /* X Position */
                         50,        /* Y Position */
                         60,       /* X Width    */
                         20,      /* Y Height   */
                         hwnd, (HMENU) 205, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            hwndRad1 = CreateWindow("BUTTON", "seconds", WS_CHILD | WS_VISIBLE |  BS_AUTORADIOBUTTON,
                         15,         /* X Position */
                         50,        /* Y Position */
                         60,       /* X Width    */
                         18,      /* Y Height   */
                         hwnd, (HMENU) ID_RAD_BUTTON_1, ((LPCREATESTRUCT) lParam)->hInstance, NULL);
                         
            hwndRad2 = CreateWindow("BUTTON", "minutes", WS_CHILD | WS_VISIBLE |  BS_AUTORADIOBUTTON,
                         100,        /* X Position */
                         50,        /* Y Position */
                         60,       /* X Width    */
                         18,      /* Y Height   */
                         hwnd, (HMENU) ID_RAD_BUTTON_2, ((LPCREATESTRUCT) lParam)->hInstance, NULL);

            /* disable edit boxes that don't require user input */
            EnableWindow(hwndEdit3, FALSE);
            EnableWindow(hwndEdit4, FALSE);
            EnableWindow(hwndEdit5, FALSE);

            /* create a custom font for our controls */
            font = CreateFont(15, 0, 0, 0, 550, 0, 0, 0, 0, 0, 0, 0, 0, "Courier New");
            font2 = CreateFont(13, 0, 0, 0, 550, 0, 0, 0, 0, 0, 0, 0, 0, "Courier New");

            /* set the fonts for the controls */
            SendDlgItemMessage(hwnd, ID_RAD_BUTTON_1, WM_SETFONT, (WPARAM)font2, TRUE);
            SendDlgItemMessage(hwnd, ID_RAD_BUTTON_2, WM_SETFONT, (WPARAM)font2, TRUE);
            SendDlgItemMessage(hwnd, ID_EDIT_SECS, WM_SETFONT, (WPARAM)font, TRUE);
            SendDlgItemMessage(hwnd, ID_EDIT_MINS, WM_SETFONT, (WPARAM)font, TRUE);
            SendDlgItemMessage(hwnd, ID_EDIT_LEFT, WM_SETFONT, (WPARAM)font, TRUE);
            SendDlgItemMessage(hwnd, ID_EDIT_TIME, WM_SETFONT, (WPARAM)font, TRUE);
            SendDlgItemMessage(hwnd, ID_EDIT_STATUS, WM_SETFONT, (WPARAM)font, TRUE);
            SendDlgItemMessage(hwnd, 200, WM_SETFONT, (WPARAM)font, TRUE);
            SendDlgItemMessage(hwnd, 201, WM_SETFONT, (WPARAM)font, TRUE);
            SendDlgItemMessage(hwnd, 202, WM_SETFONT, (WPARAM)font, TRUE);
            SendDlgItemMessage(hwnd, 203, WM_SETFONT, (WPARAM)font, TRUE);
            SendDlgItemMessage(hwnd, 204, WM_SETFONT, (WPARAM)font2, TRUE);
            SendDlgItemMessage(hwnd, 205, WM_SETFONT, (WPARAM)font2, TRUE);

            /* formats time into a string */
            strftime(ascii_time, 28, "%c", ptr);

            /* control settings on startup */
            SetWindowText(hwndEdit5, ascii_time);
            SetWindowText(hwndEdit4, "Halted...");
            SetWindowText(hwndEdit1, "60");
            SetWindowText(hwndEdit2, "60");
            SetWindowText(hwndEdit3, "60");
            SendMessage(hwndRad1, BM_SETCHECK, TRUE, 0);
            EnableWindow(hwndEdit2, FALSE);
            
            break;     
        }
        /* we use two timers, one for the clock and one for the countdown */
        case WM_TIMER:
        {
            switch (wParam)
            {
                /* for updating the time edit control */
                case 1:
                {
                    lt = time(NULL);
                    ptr = localtime(&lt);

                    /* formats time into a string */
                    strftime(ascii_time, 28, "%c", ptr);

                    /* change the current time text */
                    SetWindowText(hwndEdit5, ascii_time);

                    break;
                }

                /* for the countdown */
                case 2:
                {
                    /* start button has been pressed so countdown commences */
                    if (flag == 1) {
                        seconds = GetDlgItemInt(hwnd, ID_EDIT_LEFT, 0, FALSE);

                        /* sound effects for the last 5 seconds */
                        if (seconds <= 6 && seconds != 0) {
                            PlaySound(MAKEINTRESOURCE(ID_BEEP), global_instance, SND_RESOURCE | SND_ASYNC);
                        }

                        switch (seconds)
                        {
                            case 11:    /* fall through */
                            case 10:    /* fall through */
                            case 9:     /* fall through */
                            case 8:     /* fall through */
                            case 7:     /* fall through */
                                SetWindowText(hwndEdit4, "Shutdown Soon!");
                                break;
                            case 6:
                                SetWindowText(hwndEdit4, "Five!");
                                break;
                            case 5:
                                SetWindowText(hwndEdit4, "Four!");
                                break;
                            case 4:
                                SetWindowText(hwndEdit4, "Three!");
                                break;
                            case 3:
                                SetWindowText(hwndEdit4, "Two!");
                                break;
                            case 2:
                                SetWindowText(hwndEdit4, "One!");
                                break;
                            case 1:
                                SetWindowText(hwndEdit4, "Shutdown Now!");
                                break;
                            default:
                                break;
                        }

                        if (seconds == 0) {
                            /* stops the shutdown code from being executed more than once */
	                        KillTimer(hwnd, 2);

                            /* begin: shutdown code */
       	                    TOKEN_PRIVILEGES tkp;
                            HANDLE hToken;

                            /* get the token for this process */
	                        OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
	                        LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
	                        tkp.PrivilegeCount = 1;

	                        /* set this privilege to enabled. otherwise it would disable it */
	                        tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	                        AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);

                            /* force shutdown button checked? */
                            if (SendMessage(hwndForce, BM_GETCHECK, 0, 0) == BST_CHECKED) {
	                            ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF, 0);
                            /* normal shutdown */
                            } else {
	                            ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF | EWX_FORCE, 0);
                            }
	                        /* end: shutdown code */
                        }

                        /* stops the counter from going below zero */
                        if (seconds != 0) seconds--;

                        sprintf(countdown_buffer, "%i", seconds);
                        SetWindowText(hwndEdit3, countdown_buffer);
                    }
                    break;
                }
            }
            break;
        }
        case WM_COMMAND:
            switch (LOWORD (wParam)) {
                case ID_RAD_BUTTON_1:
                {
                    EnableWindow(hwndEdit2, FALSE);
                    EnableWindow(hwndEdit1, TRUE);

                    break;
                }
                case ID_RAD_BUTTON_2:
                {
                    EnableWindow(hwndEdit1, FALSE);
                    EnableWindow(hwndEdit2, TRUE);

                    break;
                }

                /* when the 'Start!' button is pressed a lot of controls */
                /* are disabled and changed, this code deals with that   */
                case ID_BUTTON:
                {
                    /* changes the flag state */
                    flag ^= 1;

                    /* 'Start!' has been pressed */
                    if (flag) {
                        SetTimer(hwnd, 2, 1000, NULL);
                        SetWindowText(hwndButton, "Abort!");
                        SetWindowText(hwndEdit4, "Counting Down...");
                        EnableWindow(hwndEdit1, FALSE);
                        EnableWindow(hwndEdit2, FALSE);
                        EnableWindow(hwndRad1, FALSE);
                        EnableWindow(hwndRad2, FALSE);
                        EnableWindow(hwndForce, FALSE);

                        /* sets the countdown edit control to the right time  */
                        /* the first radio button is checked, so it's seconds */
                        if (SendMessage(hwndRad1, BM_GETCHECK, 0, 0) == BST_CHECKED) {
                            seconds = GetDlgItemInt(hwnd, ID_EDIT_SECS, 0, FALSE);
                            sprintf(countdown_buffer, "%i", seconds);
                            SetWindowText(hwndEdit3, countdown_buffer);
                        /* the second radio button is checked, so convert the */
                        /* minutes into seconds and put result in countdown   */
                        } else {
                            seconds = GetDlgItemInt(hwnd, ID_EDIT_MINS, 0, FALSE);
                            sprintf(countdown_buffer, "%i", seconds*60);
                            SetWindowText(hwndEdit3, countdown_buffer);
                        }
                    /* 'Abort!' has been pressed */
                    } else {
                        KillTimer(hwnd, 2);
                        SetWindowText(hwndButton, "Start!");
                        SetWindowText(hwndEdit4, "Halted...");
                        EnableWindow(hwndRad1, TRUE);
                        EnableWindow(hwndRad2, TRUE);
                        EnableWindow(hwndForce, TRUE);

                        /* stops both time entry fields from being activated */
                        if (SendMessage(hwndRad1, BM_GETCHECK, 0, 0) == BST_CHECKED) {
                            EnableWindow(hwndEdit1, TRUE);
                        } else {
                            EnableWindow(hwndEdit2, TRUE);
                        }
                    }

                    break;
                }
                case ID_EXIT:
                    /* send a quit message to close the program */
                    SendMessage(hwnd, WM_CLOSE, 0, 0);
                    break;
                case ID_ABOUT:
                    /* launch the about dialog box */
                    DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(ABOUT_DIALOG), hwnd, DlgProc);
                    break;
            }
            break;
        /* hide window on minimize */
		case WM_SYSCOMMAND: 
			if (SC_MINIMIZE == wParam) { 
				ShowWindow(hwnd, SW_HIDE);
				return(1);
			}
			break;
		case WM_CLOSE: 
			/* remove the icon from system tray */
			Shell_NotifyIcon(NIM_DELETE, &structNID);
			DestroyWindow(hwnd);
			PostQuitMessage(0);
			break;
        case WM_DESTROY:
            PostQuitMessage(0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc(hwnd, message, wParam, lParam);
    }

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

/* handles messages from the 'About' dialog box */
int CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_INITDIALOG:
            return(TRUE);
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {        
                case WM_DESTROY:
                case ID_ABOUT_OK:
                    EndDialog(hwnd, ID_ABOUT_OK);
                    break;
            }
            break;
        default:
            return(FALSE);
    }
    return(TRUE);
}