/*
 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
 *
 * This software is dual-licensed to you under the MIT License (MIT) and
 * the Universal Permissive License (UPL). See the LICENSE file in the root
 * directory for license terms. You may choose either license, or both.
 */

#include <errno.h>
#include <stdio.h>
#include <windows.h>
#include <process.h>
#include "iotcs_port_system.h"
#include "util/util_memory.h"
#include "iotcs_port_thread.h"
#include "iotcs_port_mutex.h"
#include "iotcs_thread_private.h"

#include "log/log.h"
#define IOTCSP_MODULE_LOG_CHANNEL LOG_CHANNEL_PORT_THREAD
#include "log/log_template.h"

static void error_handler(int error, char* msg) {
    LOG_CRITS(msg);
    switch (error) {
        case 1: LOG_CRITS(" Type - Common");
            break;
        case WAIT_ABANDONED: LOG_CRITS(" Type - WAIT_ABANDONED");
            break;
        case WAIT_TIMEOUT: LOG_CRITS(" Type - WAIT_TIMEOUT");
            break;
        case WAIT_FAILED: LOG_CRITS(" Type - WAIT_FAILED");
            break;
        default: LOG_CRITS(" Type - UNKNOWN");
            break;
    }
}

iotcs_port_thread iotcs_port_thread_create(iotcs_port_thread_func func_ptr) {
    HANDLE hThread;

    hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func_ptr, NULL, 0, NULL);

    if (hThread == NULL) {
        error_handler(1, "Create thread failed");
        return (iotcs_port_thread) NULL;
    }

    return (iotcs_port_thread) hThread;
}

iotcs_port_thread iotcs_port_thread_create_small_stack(iotcs_port_thread_func func_ptr) {
    return iotcs_port_thread_create(func_ptr);
}

void iotcs_port_thread_join(iotcs_port_thread thread) {
    DWORD dwWaitResult;
    BOOL bCloseResult;
    dwWaitResult = WaitForSingleObject(thread, INFINITE);
    if (dwWaitResult != 0) {
        error_handler((int) dwWaitResult, "Thread join failed");
    }

    bCloseResult = CloseHandle(thread);
    if (bCloseResult == FALSE) {
        error_handler(1, "Thread close failed");
    }
}

iotcs_port_mutex iotcs_port_mutex_create(void) {
    HANDLE mutex;
    mutex = CreateMutex(0, 0, 0);
    if (mutex == NULL) {
        error_handler(1, "Create mutex failed");
    }
    return (iotcs_port_mutex) mutex;
}

void iotcs_port_mutex_destroy(iotcs_port_mutex mutex) {
    BOOL bCloseResult;
    bCloseResult = CloseHandle(mutex);
    if (bCloseResult == FALSE) {
        error_handler(1, "Destroy mutex failed");
    }
}

void iotcs_port_mutex_lock(iotcs_port_mutex mutex) {
    DWORD dwWaitResult;
    dwWaitResult = WaitForSingleObject(mutex, INFINITE);
    if (dwWaitResult != 0) {
        error_handler((int) dwWaitResult, "Lock mutex failed");
    }
}

void iotcs_port_mutex_unlock(iotcs_port_mutex mutex) {
    BOOL bReleaseResult;
    bReleaseResult = ReleaseMutex(mutex);
    if (bReleaseResult == FALSE) {
        error_handler((int) 1, "Unlock mutex failed");
    }
}

unsigned long iotcs_get_thread_id(void) {
    return (unsigned long) GetCurrentThreadId();
}