#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NON_CONFORMING_SWPRINTFS

#include <windows.h>
#include <tchar.h>

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT ("SymbolLink");
    HWND         hwnd;
    MSG          msg;
    WNDCLASSEX   wndclassex = {0};
    wndclassex.cbSize        = sizeof(WNDCLASSEX);
    wndclassex.style         = CS_HREDRAW | CS_VREDRAW;
    wndclassex.lpfnWndProc   = WndProc;
    wndclassex.cbClsExtra    = 0;
    wndclassex.cbWndExtra    = 0;
    wndclassex.hInstance     = hInstance;
    wndclassex.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
    wndclassex.hCursor       = LoadCursor (NULL, IDC_ARROW);
    wndclassex.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
    wndclassex.lpszMenuName  = NULL;
    wndclassex.lpszClassName = szAppName;
    wndclassex.hIconSm       = wndclassex.hIcon;
	
    if (!RegisterClassEx (&wndclassex))
    {
        MessageBox (NULL, TEXT ("RegisterClassEx failed!"), szAppName, MB_ICONERROR);
        return 0;
    }
    hwnd = CreateWindowEx (WS_EX_OVERLAPPEDWINDOW, 
		                  szAppName, 
        		          TEXT ("SymbolLink"),
                		  WS_OVERLAPPEDWINDOW,
		                  CW_USEDEFAULT, 
        		          CW_USEDEFAULT, 
                		  CW_USEDEFAULT, 
		                  CW_USEDEFAULT, 
        		          NULL, 
                		  NULL, 
		                  hInstance,
        		          NULL); 
						  
    //ShowWindow (hwnd, iCmdShow);
    //UpdateWindow (hwnd);
	
    while (GetMessage (&msg, NULL, 0, 0))
    {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
        {
            TCHAR *psz_cmmnd_line = GetCommandLine();
            if (psz_cmmnd_line == NULL)
            {
                MessageBox(hwnd, _T("Wrong parameters!"), _T("SymbolLink Error"), MB_OK);
                return -1;
            }

            int n_count = 0;
            WCHAR **psz_arg_list = CommandLineToArgvW(psz_cmmnd_line, &n_count);
            if (psz_arg_list == NULL)
            {
                MessageBox(hwnd, _T("Failed to parse parameters!"), _T("SymbolLink Error"), MB_OK);
                return -1;
            }

            if (n_count != 3 && n_count != 2)
            {
                MessageBox(hwnd, _T("Wrong parameters!\nUsage: symbollink [source] link"), _T("SymbolLink Error"), MB_OK);
                LocalFree(psz_arg_list);
                return -1;
            }


            // what type the source is
            DWORD dwAttr = GetFileAttributes(psz_arg_list[1]);
            if (dwAttr == INVALID_FILE_ATTRIBUTES)
            {
                int n_err_msg_len = 60 + _tcslen(psz_arg_list[1]);
                TCHAR *psz_err_msg = new TCHAR[n_err_msg_len];
                _stprintf(psz_err_msg, _T("Failed to get attributes\nfor %s"), psz_arg_list[1]);
                MessageBox(hwnd, psz_err_msg, _T("SymbolLink Error"), MB_OK);
                LocalFree(psz_arg_list);
                delete [] psz_err_msg;

                return -1;
            }

            if ((dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT)
            {
                // delete symbolic link
                WIN32_FIND_DATA wfd;
                ZeroMemory(&wfd, sizeof(WIN32_FIND_DATA));
                HANDLE hFile = FindFirstFile(psz_arg_list[1], &wfd);
                FindClose(hFile);

                if (wfd.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
                {
                    if ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
                    {
                        RemoveDirectory(psz_arg_list[1]);
                    }
                    else
                    {
                        DeleteFile(psz_arg_list[1]);
                    }
                }
            }
            else
            {
                //
                // create symbolic link
                //

                // get the link name from source
                TCHAR *psz_link_name = _tcsrchr(psz_arg_list[1], _T('\\'));
                if (psz_link_name == NULL)
                {
                    int n_err_msg_len = 60 + _tcslen(psz_arg_list[1]);
                    TCHAR *psz_err_msg = new TCHAR[n_err_msg_len];
                    _stprintf(psz_err_msg, _T("Failed to get name\nfrom %s"), psz_arg_list[1]);
                    MessageBox(hwnd, psz_err_msg, _T("SymbolLink Error"), MB_OK);
                    LocalFree(psz_arg_list);
                    delete [] psz_err_msg;
                    return -1;
                }
                // skip slash
                psz_link_name++;

                // append the link name to target/link path
                int n_link_path_len = _tcslen(psz_arg_list[2]) + _tcslen(psz_link_name) + 8;
                TCHAR *psz_link_path = new TCHAR[n_link_path_len];

                _stprintf(psz_link_path, _T("%s\\%s"), psz_arg_list[2], psz_link_name);

                DWORD dw_flag = ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) ? 0x1 : 0x0;
                if (!CreateSymbolicLink(psz_link_path, psz_arg_list[1], dw_flag))
                {
                    DWORD dwError = GetLastError();
                    TCHAR sz_error_buf[MAX_PATH * 10] = _T("");
                    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, sz_error_buf, MAX_PATH * 10, NULL);
                    int n_err_msg_len = 60 + _tcslen(psz_link_path) + _tcslen(psz_arg_list[1]) + _tcslen(sz_error_buf);
                    TCHAR *psz_err_msg = new TCHAR[n_err_msg_len];
                    _stprintf(psz_err_msg, _T("Failed to create symobl link\nfrom %s\nto %s\nCause: %s"), psz_arg_list[1], psz_link_path, sz_error_buf);
                    MessageBox(hwnd, psz_err_msg, _T("SymbolLink Error"), MB_OK);
                    delete [] psz_err_msg;

                    delete [] psz_link_path;

                    LocalFree(psz_arg_list);

                    return -1;
                }
                delete [] psz_link_path;
            }

            LocalFree(psz_arg_list);

            DestroyWindow(hwnd);
            //CreateSymbolicLink()
        }
        return (0);
		
    case WM_DESTROY:
        PostQuitMessage (0);
        return (0);
    }
    return DefWindowProc (hwnd, message, wParam, lParam);
}
