/*
 * Test program to try unicode file names.
 *
 * Copyright (C) 1998-2019 Toni Ronkko
 * This file is part of dirent.  Dirent may be freely distributed
 * under the MIT license.  For all details and documentation, see
 * https://github.com/tronkko/dirent
 */

/* Silence warning about fopen being insecure */
#define _CRT_SECURE_NO_WARNINGS

#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <time.h>
#include <locale.h>

#undef NDEBUG
#include <assert.h>


int
main(
    int argc, char *argv[])
{
#ifdef WIN32
    wchar_t wpath[MAX_PATH+1];
    char path[MAX_PATH+1];
    DWORD i, j, k, x;
    BOOL ok;
    HANDLE fh;
    _WDIR *wdir;
    struct _wdirent *wentry;
    DIR *dir;
    struct dirent *entry;
    char buffer[100];
    FILE *fp;
    int counter = 0;

    (void) argc;
    (void) argv;


    /* Initialize random number generator */
    srand (((int) time (NULL)) * 257 + ((int) GetCurrentProcessId ()));

    /* Set current locale */
    if (argc > 1) {
        printf ("Locale %s\n", argv[1]); 
        setlocale (LC_ALL, argv[1]);
    } else {
        setlocale (LC_ALL, "");
    }


    /****** CREATE FILE WITH UNICODE FILE NAME ******/

    /* Get path to temporary directory (wide-character and ascii) */
    i = GetTempPathW (MAX_PATH, wpath);
    assert (i > 0);
    j = GetTempPathA (MAX_PATH, path);
    assert (j > 0);

    /* Append random directory name */
    for (k = 0; k < 10; k++) {
        char c;

        /* Generate random character */
        c = "abcdefghijklmnopqrstuvwxyz"[rand() % 26];

        /* Append character to paths */
        assert (i < MAX_PATH  &&  j < MAX_PATH);
        wpath[i++] = c;
        path[j++] = c;
    }

    /* Terminate paths */
    assert (i < MAX_PATH  &&  j < MAX_PATH);
    wpath[i] = '\0';
    path[j] = '\0';

    /* Remember the end of directory name */
    k = i;

    /* Create directory using unicode */
    ok = CreateDirectoryW (wpath, NULL);
    if (!ok) {
        DWORD e = GetLastError ();
        wprintf (L"Cannot create directory %ls (code %u)\n", wpath, e);
        abort ();
    }

    /* Overwrite zero terminator with path separator */
    assert (i < MAX_PATH  &&  j < MAX_PATH);
    wpath[i++] = '\\';

    /* Append a few unicode characters */
    assert (i < MAX_PATH);
    wpath[i++] = 0x6d4b;
    assert (i < MAX_PATH);
    wpath[i++] = 0x8bd5;

    /* Terminate string */
    assert (i < MAX_PATH);
    wpath[i] = '\0';

    /* Create file with unicode */
    fh = CreateFileW(
        wpath,
        /* Access */ GENERIC_READ | GENERIC_WRITE,
        /* Share mode */ 0,
        /* Security attributes */ NULL,
        /* Creation disposition */ CREATE_NEW,
        /* Attributes */ FILE_ATTRIBUTE_NORMAL,
        /* Template files */ NULL
    );
    assert (fh != INVALID_HANDLE_VALUE);

    /* Write some data to file */
    ok = WriteFile(
        /* File handle */ fh,
        /* Pointer to data */ "hep\n",
        /* Number of bytes to write */ 4,
        /* Number of bytes written */ NULL,
        /* Overlapped */ NULL
    );
    assert (ok);

    /* Close file */
    ok = CloseHandle (fh);
    assert (ok);


    /****** MAKE SURE THAT UNICODE FILE NAME CAN BE READ BY _WREADDIR ******/

    /* Zero terminate wide-character path and open directory stream */
    wpath[k] = '\0';
    wdir = _wopendir (wpath);
    if (wdir == NULL) {
        wprintf (L"Cannot open directory %ls\n", wpath);
        abort ();
    }

    /* Read through entries */
    counter = 0;
    while ((wentry = _wreaddir (wdir)) != NULL) {

        /* Skip pseudo directories */
        if (wcscmp (wentry->d_name, L".") == 0) {
            continue;
        }
        if (wcscmp (wentry->d_name, L"..") == 0) {
            continue;
        }

        /* Found a file */
        counter++;
        assert (wentry->d_type == DT_REG);

        /* Append file name to path */
        i = k;
        assert (i < MAX_PATH);
        wpath[i++] = '\\';
        x = 0;
        while (wentry->d_name[x] != '\0') {
            assert (i < MAX_PATH);
            wpath[i++] = wentry->d_name[x++];
        }
        assert (i < MAX_PATH);
        wpath[i] = '\0';

        /* Open file for read */
        fh = CreateFileW(
            wpath,
            /* Access */ GENERIC_READ,
            /* Share mode */ 0,
            /* Security attributes */ NULL,
            /* Creation disposition */ OPEN_EXISTING,
            /* Attributes */ FILE_ATTRIBUTE_NORMAL,
            /* Template files */ NULL
        );
        assert (fh != INVALID_HANDLE_VALUE);

        /* Read data from file */
        ok = ReadFile(
            /* File handle */ fh,
            /* Output buffer */ buffer,
            /* Maximum number of bytes to read */ sizeof (buffer) - 1,
            /* Number of bytes actually read */ &x,
            /* Overlapped */ NULL
        );
        assert (ok);

        /* Make sure that we got the file contents right */
        assert (x == 4);
        assert (buffer[0] == 'h');
        assert (buffer[1] == 'e');
        assert (buffer[2] == 'p');
        assert (buffer[3] == '\n');

        /* Close file */
        ok = CloseHandle (fh);
        assert (ok);

    }
    assert (counter == 1);

    /* Close directory */
    _wclosedir (wdir);


    /****** MAKE SURE THAT UNICODE FILE NAME CAN BE READ BY READDIR ******/

    /* Zero terminate ascii path and open directory stream */
    k = j;
    path[k] = '\0';
    dir = opendir (path);
    if (dir == NULL) {
        fprintf (stderr, "Cannot open directory %s\n", path);
        abort ();
    }

    /* Read through entries */
    counter = 0;
    while ((entry = readdir (dir)) != NULL) {

        /* Skip pseudo directories */
        if (strcmp (entry->d_name, ".") == 0) {
            continue;
        }
        if (strcmp (entry->d_name, "..") == 0) {
            continue;
        }

        /* Found a file */
        counter++;
        assert (entry->d_type == DT_REG);

        /* Append file name to path */
        j = k;
        assert (j < MAX_PATH);
        path[j++] = '\\';
        x = 0;
        while (entry->d_name[x] != '\0') {
            assert (j < MAX_PATH);
            path[j++] = entry->d_name[x++];
        }
        assert (j < MAX_PATH);
        path[j] = '\0';

        /* Open file for read */
        fp = fopen (path, "r");
        if (!fp) {
            fprintf (stderr, "Cannot open file %s\n", path);
            abort ();
        }

        /* Read data from file */
        if (fgets (buffer, sizeof (buffer), fp) == NULL) {
            fprintf (stderr, "Cannot read file %s\n", path);
            abort ();
        }

        /* Make sure that we got the file contents right */
        assert (buffer[0] == 'h');
        assert (buffer[1] == 'e');
        assert (buffer[2] == 'p');
        assert (buffer[3] == '\n');
        assert (buffer[4] == '\0');

        /* Close file */
        fclose (fp);

    }
    assert (counter == 1);

    /* Close directory */
    closedir (dir);


    /****** CREATE FILE WITH UTF-8 ******/

    /* Append UTF-8 file name (åäö.txt) to path */
    j = k;
    path[j++] = '\\';
    path[j++] = 0xc3;
    path[j++] = 0xa5;
    path[j++] = 0xc3;
    path[j++] = 0xa4;
    path[j++] = 0xc3;
    path[j++] = 0xb6;
    path[j++] = 0x2e;
    path[j++] = 0x74;
    path[j++] = 0x78;
    path[j++] = 0x74;
    assert (j < MAX_PATH);
    path[j] = '\0';

    /*
     * Create file.
     *
     * Be ware that the code below creates a different file depending on the
     * current locale!  For example, if the current locale is
     * english_us.65001, then the file name will be "åäö.txt" (7 characters).
     * However, if the current locale is english_us.1252, then the file name
     * will be "ÃċÃĊÃ¶.txt" (10 characters).
     */
    printf ("Creating %s\n", path);
    fp = fopen (path, "w");
    if (!fp) {
        fprintf (stderr, "Cannot open file %s\n", path);
        abort ();
    }
    fputs ("hep\n", fp);
    fclose (fp);

    /* Open directory again */
    path[k] = '\0';
    dir = opendir (path);
    if (dir == NULL) {
        fprintf (stderr, "Cannot open directory %s\n", path);
        abort ();
    }

    /* Read through entries */
    counter = 0;
    while ((entry = readdir (dir)) != NULL) {

        /* Skip pseudo directories */
        if (strcmp (entry->d_name, ".") == 0) {
            continue;
        }
        if (strcmp (entry->d_name, "..") == 0) {
            continue;
        }

        /* Found a file */
        counter++;
        assert (entry->d_type == DT_REG);

        /* Append file name to path */
        j = k;
        assert (j < MAX_PATH);
        path[j++] = '\\';
        x = 0;
        while (entry->d_name[x] != '\0') {
            assert (j < MAX_PATH);
            path[j++] = entry->d_name[x++];
        }
        assert (j < MAX_PATH);
        path[j] = '\0';

        /* Print file name for debugging */
        printf ("Opening \"%s\" hex ", path + k + 1);
        x = 0;
        while (entry->d_name[x] != '\0') {
            printf ("0x%02x ", (unsigned) (entry->d_name[x++] & 0xff));
        }
        printf ("\n");

        /* Open file for read */
        fp = fopen (path, "r");
        if (!fp) {
            fprintf (stderr, "Cannot open file %s\n", path);
            abort ();
        }

        /* Read data from file */
        if (fgets (buffer, sizeof (buffer), fp) == NULL) {
            fprintf (stderr, "Cannot read file %s\n", path);
            abort ();
        }

        /* Make sure that we got the file contents right */
        assert (buffer[0] == 'h');
        assert (buffer[1] == 'e');
        assert (buffer[2] == 'p');
        assert (buffer[3] == '\n');
        assert (buffer[4] == '\0');

        /* Close file */
        fclose (fp);

    }
    assert (counter == 2);

    /* Close directory */
    closedir (dir);

#else

    /* Linux */
    (void) argc;
    (void) argv;

#endif
    return EXIT_SUCCESS;
}
