//
//
//

#define DBG_LEVEL 3
#include "Raym/Log.h"

#include <process.h>

#include "CommandRunner.h"

using namespace Raym;

namespace ry0
{
namespace iPTd
{

CommandRunner::CommandRunner()
{
    DebugLog2("CommandRunner::CommandRunner()");

    _state     = ST_IDLE;
    _command   = NULL;
    _arguments = NULL;
}

CommandRunner::~CommandRunner()
{
    stop();

    RELEASE(_command);
    RELEASE(_arguments);

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

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

CommandRunner *CommandRunner::init()
{
    DebugLog2("CommandRunner::init()");

    _state = ST_IDLE;

    return this;
}

CommandRunner *CommandRunner::retain()
{
    DebugLog2("CommandRunner::retain()");

    Object::retain();
    return this;
}

CommandRunner *CommandRunner::autorelease()
{
    DebugLog2("CommandRunner::autorelease()");

    Object::autorelease();
    return this;
}

void CommandRunner::setCommandPath(String *path)
{
    DebugLog2("CommandRunner::setCommandPath()");

    RELEASE(_command);
    if (path != NULL)
    {
        _command = path->retain();
    }
}

void CommandRunner::setArguments(Array *args)
{
    DebugLog2("CommandRunner::setArguments()");

    RELEASE(_arguments);
    if (args != NULL)
    {
        _arguments = args->retain();
    }
}

bool CommandRunner::readLine(String *line)
{
    return false;
}

void CommandRunner::write(String *line)
{
}

unsigned __stdcall CommandRunner_run(void *arg)
{
    ((CommandRunner *)arg)->run();
    return 0;
}

void CommandRunner::run()
{
    DebugLog2("CommandRunner::run()");

    AutoreleasePool *pool = AutoreleasePool::alloc()->init();

    // lock
    RaymLock(this);

    while ((_command != NULL) && (_state == ST_READY))
    {
        DebugLog3("pre processing");

        // パイプ生成
        Pipe *pipe = Pipe::alloc()->init();

        // タスク生成
        Task *task = Task::alloc()->init();
        task->setLaunchPath(_command);

        // 引数設定
        if (_arguments != NULL)
        {
            task->setArguments(_arguments);
        }

        // 標準出力設定
        task->setStandardOutput(pipe->fileHandleForWriting());
        task->setStandardError(pipe->fileHandleForWriting());

        // タスク実行
        task->launch();

        // 読み込み
        FileInputStream *fis = FileInputStream::fileInputStream(pipe->fileHandleForReading());

        // 状態変更
        _state = ST_RUN;

        // unlock
        RaymUnlock(this);

        bool done = false;
        while (!done)
        {
            // ループ処理
            AutoreleasePool *pool2 = AutoreleasePool::alloc()->init();

            // 1行読み込み
            bool done2 = readLine(fis->readLine());

            // lock
            RaymLock(this);

            // 終了チェック
            done = (done2 || (_state == ST_DONE));

            // unlock
            RaymUnlock(this);

            pool2->release();
        }

        DebugLog3("post processing");

        // タスク終了
        task->terminate();
        task->release();

        // パイプ破棄
        pipe->release();

        // lock
        RaymLock(this);
        break;
    }

    // 状態変更
    _state = ST_IDLE;

    // unlock
    RaymUnlock(this);

    pool->release();

    DebugLog2("CommandRunner::run() done.");
}

bool CommandRunner::start()
{
    DebugLog2("CommandRunner::start()");
    
    bool result = false;
    
    RaymLock(this);
    
    if (_state == ST_IDLE)
    {
        HANDLE h;
        unsigned int uiThreadId;
        
        h = (HANDLE)_beginthreadex(NULL, 0, CommandRunner_run, this, 0, &uiThreadId);
        if (h != NULL)
        {
            _state = ST_READY;

            RaymUnlock(this);

            bool done = false;
            while (!done)
            {
                bool needSleep = false;

                RaymLock(this);

                if (_state == ST_IDLE)
                {
                    done = true;
                }
                else if (_state == ST_RUN)
                {
                    done = true;
                    result = true;
                }
                else if (_state == ST_READY)
                {
                    needSleep = true;
                }
                RaymUnlock(this);

                if (needSleep)
                {
                    ::Sleep(100); // 100 ms
                }
            }

            RaymLock(this);
        }
    }

    RaymUnlock(this);

    return result;
}

void CommandRunner::stop()
{
    DebugLog2("CommandRunner::stop()");
    RaymLock(this);
    if (_state != ST_IDLE)
    {
        _state = ST_DONE;
    }
    RaymUnlock(this);
    bool done = false;
    while (!done)
    {
        RaymLock(this);
        done = (_state == ST_IDLE);
        RaymUnlock(this);
        if (!done)
        {
            ::Sleep(100);
        }
    }
    DebugLog2("CommandRunner::stop() done.");
}

bool CommandRunner::isRunning()
{
    bool result;
    RaymLock(this);
    result = (_state == ST_RUN);
    RaymUnlock(this);
    return result;
}

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

} // iPTd
} // ry0
