#include "ProcessServer.h"
#include "ProcessManager.h"

#include <monapi.h>
#include <monapi/messages.h>
#include <monapi/Assert.h>

using namespace MonAPI;

static monapi_cmemoryinfo* commonParams = NULL;
static HList<ProcessInfo> infos;
static HList<dword> receivers;

void initCommonParameters()
{
    commonParams = monapi_cmemoryinfo_new();
    if (!monapi_cmemoryinfo_create(commonParams, sizeof(CommonParameters), 1)) exit(1);
}

ProcessInfo getProcessInfo(dword tid)
{
    int size = infos.size();
    for (int i = 0; i < size; i++)
    {
        if (infos[i].tid == tid) return infos[i];
    }

    return ProcessInfo();
}

void addProcessInfo(const CString& name)
{
    syscall_set_ps_dump();
    PsInfo info;

    while (syscall_read_ps_dump(&info) == 0)
    {
        if (name != info.name) continue;

        ProcessInfo pi = getProcessInfo(info.tid);
        if (pi.tid != THREAD_UNKNOWN) continue;

        pi.tid  = info.tid;
        pi.name = info.name;
        infos.add(pi);
    }
}


void registerStdout(dword tid, dword stdout)
{
    if (tid == THREAD_UNKNOWN) return;

    MessageInfo msg;
    MessageInfo reply;
    Message::create(&msg, MSG_STDOUT_REGIST_TO_SERVER, tid, stdout);

    if (Message::sendReceiveA(&reply, monapi_get_server_thread_id(ID_FILE_SERVER), &msg))
    {
        /* error, but nothing to do */
        ASSERT(!"stdout Error");
    }
}

void addProcessInfo(dword tid, dword parent, const CString& path)
{
    ProcessInfo pi = getProcessInfo(tid);
    if (pi.tid != THREAD_UNKNOWN) return;

    syscall_set_ps_dump();
    PsInfo info, found;
    found.tid = THREAD_UNKNOWN;

    while (syscall_read_ps_dump(&info) == 0)
    {
        if (found.tid == THREAD_UNKNOWN && info.tid == tid) found = info;
    }
    if (found.tid == THREAD_UNKNOWN) return;

    pi.tid    = tid;
    pi.parent = parent;
    pi.name   = found.name;
    pi.path   = path;
    infos.add(pi);
    notifyProcessChanged(MSG_PROCESS_CREATED, tid, parent, path);
}

dword addProcessInfo(dword parent, const CString& name, const CString& path)
{
    dword ret = THREAD_UNKNOWN;
    syscall_set_ps_dump();
    PsInfo info;

    while (syscall_read_ps_dump(&info) == 0)
    {
        if (ret != THREAD_UNKNOWN || name != info.name) continue;

        ProcessInfo pi = getProcessInfo(info.tid);
        if (pi.tid != THREAD_UNKNOWN) continue;

        pi.tid    = info.tid;
        pi.parent = parent;
        pi.name   = name;
        pi.path   = path;
        infos.add(pi);
        ret = info.tid;
    }
    return ret;
}

void addProcessInfo(dword tid, dword parent, const CString& name, const CString& path)
{
    ProcessInfo pi(tid, parent, name, path);
    infos.add(pi);
}

void removeProcessInfo(dword tid)
{
    int size = infos.size();
    for (int i = 0; i < size; i++)
    {
        if (infos[i].tid != tid) continue;

        infos.removeAt(i);
        notifyProcessChanged(MSG_PROCESS_TERMINATED, tid);
        return;
    }
}

static void registerReceiver(dword tid)
{
    int size = receivers.size();
    for (int i = 0; i < size; i++)
    {
        if (receivers[i] == tid) return;
    }
    receivers.add(tid);
}

static void unregisterReceiver(dword tid)
{
    int size = receivers.size();
    for (int i = 0; i < size; i++)
    {
        if (receivers[i] != tid) continue;

        receivers.removeAt(i);
        return;
    }
}

void notifyProcessChanged(dword header, dword tid, dword parent /*= 0*/, const CString& path /*= NULL*/)
{
    int i = 0;
    while (i < receivers.size())
    {
        if (Message::send(receivers[i], header, tid, parent, 0, path) == 0)
        {
            i++;
        }
        else
        {
            printf("%s: can not connect to %d\n", SVR, receivers[i]);
            removeProcessInfo(receivers[i]);
            receivers.removeAt(i);
        }
    }
}

bool processHandler(MessageInfo* msg)
{
    switch (msg->header)
    {
        case MSG_REGISTER_TO_SERVER:
            registerReceiver(msg->arg1);
            Message::reply(msg);
            break;
        case MSG_UNREGISTER_FROM_SERVER:
            unregisterReceiver(msg->arg1);
            Message::reply(msg);
            break;
        case MSG_PROCESS_GET_PROCESS_INFO:
        {
            ProcessInfo pi = getProcessInfo(msg->from);
            Message::reply(msg, pi.parent, 0, pi.path);
            break;
        }
        case MSG_PROCESS_CREATED:
            addProcessInfo(msg->arg1, msg->arg2, msg->str);
            break;
        case MSG_PROCESS_TERMINATED:
            removeProcessInfo(msg->arg1);
            break;
        case MSG_PROCESS_GET_COMMON_PARAMS:
            Message::reply(msg, commonParams->Handle);
            break;
        default:
            return false;
    }
    return true;
}
