//
//
//

#define DBG_LEVEL 0
#include <Raym/Log.h>
#include <Raym/Raym.h>

namespace Raym
{

Task::Task()
{
    DebugLog2("Task::Task()");

    _launched = FALSE;
    _launchPath = NULL;
    _arguments = NULL;
    _standardInput = NULL;
    _standardOutput = NULL;
    _standardError = NULL;

    memset(&_pi, 0, sizeof(_pi));
}

Task::~Task()
{
    RELEASE(_launchPath);
    RELEASE(_arguments);
    RELEASE(_standardInput);
    RELEASE(_standardOutput);
    RELEASE(_standardError);

    DebugLog2("Task::~Task()");
}

Task *Task::alloc()
{
    return new Task();
}

Task *Task::init()
{
    return this;
}

void Task::setLaunchPath(String *launchPath)
{
    DebugLog2("Task::setLaunchPath()");

    EnterCriticalSection(&_cs);
    if (!_launched)
    {
        RELEASE(_launchPath);
        _launchPath = (String *)launchPath->retain();
    }
    LeaveCriticalSection(&_cs);
}

void Task::setArguments(Array *arguments)
{
    DebugLog2("Task::setArguments()");

    EnterCriticalSection(&_cs);
    if (!_launched)
    {
        RELEASE(_arguments);
        _arguments = (Array *)arguments->retain();
    }
    LeaveCriticalSection(&_cs);
}

void Task::setStandardInput(FileHandle *standardInput)
{
    DebugLog2("Task::setStandardInput()");

    EnterCriticalSection(&_cs);
    if (!_launched)
    {
        RELEASE(_standardInput);
        _standardInput = (FileHandle *)standardInput->retain();
    }
    LeaveCriticalSection(&_cs);
}

void Task::setStandardOutput(FileHandle *standardOutput)
{
    DebugLog2("Task::setStandardOutput()");

    EnterCriticalSection(&_cs);
    if (!_launched)
    {
        RELEASE(_standardOutput);
        _standardOutput = (FileHandle *)standardOutput->retain();
    }
    LeaveCriticalSection(&_cs);
}

void Task::setStandardError(FileHandle *standardError)
{
    DebugLog2("Task::setStandardError()");

    EnterCriticalSection(&_cs);
    if (!_launched)
    {
        RELEASE(_standardError);
        _standardError = (FileHandle *)standardError->retain();
    }
    LeaveCriticalSection(&_cs);
}

void Task::launch()
{
    DebugLog2("Task::launch()");

    EnterCriticalSection(&_cs);
    if (!_launched)
    {
        String *cmd = _launchPath;
        if ((_arguments != NULL) && (_arguments->count() > 0))
        {
            for (uint i = 0; i < _arguments->count(); ++i)
            {
                cmd = cmd->stringByAppendingString(" ");
                cmd = cmd->stringByAppendingString((String *)_arguments->objectAtIndex(i));
            }
        }
        DebugLog2("cmd line: %s", cmd->cString());


        TCHAR *tcmd = (TCHAR *)malloc(sizeof(TCHAR) * (cmd->length() + 1));
        if (cmd != NULL)
        {
            errno_t e;
            size_t returnValue;
            e = mbstowcs_s(&returnValue, tcmd, sizeof(TCHAR) * (cmd->length() + 1), cmd->cString(), _TRUNCATE);
            if (e == 0)
            {
                STARTUPINFO si;
                DWORD timeout = 100;

                memset(&si, 0, sizeof(si));
                si.cb = sizeof(si);
                si.dwFlags = STARTF_USESTDHANDLES;
                si.wShowWindow = SW_HIDE;

                if (_standardInput != NULL)
                {
                    si.hStdInput = _standardInput->handle();
                    timeout = 1000;
                }
                if (_standardOutput != NULL)
                {
                    si.hStdOutput = _standardOutput->handle();
                }
                if (_standardError != NULL)
                {
                    si.hStdError = _standardError->handle();
                }

                if (CreateProcess(NULL, tcmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &_pi))
                {
                    DebugLog2("CreateProcess() success.");
                    WaitForInputIdle(_pi.hProcess, timeout);
                    WaitForSingleObject(_pi.hProcess, timeout);
                    _launched = TRUE;
                }
            }

            free(tcmd);
        }
    }
    LeaveCriticalSection(&_cs);
}

void Task::terminate()
{
    DebugLog2("Task::terminate()");

    EnterCriticalSection(&_cs);
    if (_launched)
    {
        TerminateProcess(_pi.hProcess, 0);

        CloseHandle(_pi.hThread);
        CloseHandle(_pi.hProcess);

        memset(&_pi, 0, sizeof(_pi));

        _launched = FALSE;
    }
    LeaveCriticalSection(&_cs);
}

void Task::waitUntilExit()
{
    DebugLog2("Task::waitUntilExit()");

    bool launched;
    EnterCriticalSection(&_cs);
    launched = _launched;
    LeaveCriticalSection(&_cs);

    if (launched)
    {
        while (true)
        {
            if (WaitForSingleObject(_pi.hProcess, INFINITE) == WAIT_OBJECT_0)
            {
                break;
            }
        }

        EnterCriticalSection(&_cs);
        _launched = FALSE;
        LeaveCriticalSection(&_cs);
    }

    DebugLog2("Task::waitUntilExit() done.");
}

const char *Task::className()
{
    return "Task";
}

} // Raym
