/* (C) 2003-2007 Willem Jan Hengeveld <itsme@xs4all.nl>
 * Web: http://www.xs4all.nl/~itsme/
 *      http://wiki.xda-developers.com/
 *
 * $Id$
 */
#include <windows.h>
#include <Tlhelp32.h>
#include "dllmain.h"
#include "streammultiplex.h"
#include <algorithm>

// !!! I am mixing up handles and processid's quite often.
//     on WinCE this is not a problem, but this needs to be
//     fixed before porting to another platform.

// todo:
//    * merge docread and sdread and p300read
//    * add options to specify 
//        - ioctl read blocksize ( how large blocks are read at once. )
//        - sectorsize ( for converting offset -> sector nr )
//        - ITReadDisk blocksize ( how large blocks are transfered over rapi at once )
//        - file blocksize ( how large blocks are read/written at once )
//    * getmodulelist: always returns empty - fix this
//
#include "ItsUtils.h"
#include "dllversion.h"

#include "cever_deps.h"
#include "kernelmisc.h"

#include "cenk.h"

#include "debug.h"
#include "klog.h"
#include "FileFunctions.h"

//DONE: add new api for 'startstream', to avoid the problem
//of waiting for a stream from a pre-stream version
//.. or call it twice, once with, once without stream
ITSUTILS_API HRESULT STDAPICALLTYPE ITGetVersion(
        DWORD cbInput, GetVersionParams*pbInput,
        DWORD *pcbOutput, void **ppbOutput,
        IRAPIStream *pStream)
{
    *pcbOutput=0;
    *ppbOutput=NULL;

    klogmsg(L"ITGetVersion\n");
    if (cbInput>=sizeof(DWORD) && pbInput) {
        klogmsg(L"changing logtype to %d\n", pbInput->logtype);
        save_config(pbInput);
        load_config();
    }
    else {
        klogmsg(L"not changing logtype: pb=%08lx  cb=%08lx\n", pbInput, cbInput);
#ifdef LOCKED_DLL_CFG
        klogmsg(L"cfg= %08lx\n", g_cfg);
#endif
    }
    return ITSDLL_VERSION;
}

ITSUTILS_API HRESULT STDAPICALLTYPE ITStartStream(
        DWORD cbInput, void*pbInput,
        DWORD *pcbOutput, void **ppbOutput,
        IRAPIStream *pStream)
{
    *pcbOutput=0;
    *ppbOutput=NULL;
    if (pStream==NULL)
        return 0;

    //debug("started stream handler\n");

    try {
    // NOTE: the stream handler must run in the same thread as the stream was created.
    streammx_ptr stream= streammx_ptr(new streammx(pStream));

    stream->sendversion(ITSDLL_VERSION);

    while (!stream->eof())
        stream->service();
    }
    catch(const char*msg) {
        debug("ITSUTILS EXCEPTION msg: %s\n", msg);
    }
    catch(...)
    {
        debug("ITSUTILS EXCEPTION\n");
    }

    debug("terminated stream handler\n");

    return 0;
}


ITSUTILS_API HRESULT STDAPICALLTYPE ITShellExecute(
        DWORD cbInput, ShellExecuteParams *pbInput,
        DWORD *pcbOutput, BYTE **ppbOutput,
        IRAPIStream *pStream)
{
    // todo
    return 0;
}

ITSUTILS_API HRESULT STDAPICALLTYPE ITSearchMemory(
		DWORD cbInput, SearchMemoryParams *pbInput,
		DWORD *pcbOutput, SearchMemoryResult **ppbOutput,
		IRAPIStream *pStream)
{
    KernelMode _km;

    // todo - finish this

    return 0;
}







ITSUTILS_API HRESULT STDAPICALLTYPE ITReboot(
        DWORD cbInput, BYTE *pbInput,
        DWORD *pcbOutput, BYTE **ppbOutput,
        IRAPIStream *pStream)
{
    *pcbOutput= 0;
    *ppbOutput= NULL;

    KernelIoControl(IOCTL_HAL_REBOOT, NULL, 0, NULL, 0, NULL);

    return 0;
}
ITSUTILS_API HRESULT STDAPICALLTYPE ITColdboot(
        DWORD cbInput, BYTE *pbInput,
        DWORD *pcbOutput, BYTE **ppbOutput,
        IRAPIStream *pStream)
{
    *pcbOutput= 0;
    *ppbOutput= NULL;

    // this is htc-startrek specific
    KernelIoControl(0x1012594, NULL, 0, NULL, 0, NULL);
    keybd_event(0x81, 0, 0, 0);
    keybd_event(0x81, 0, 2, 0);
    return 0;
}


ITSUTILS_API HRESULT STDAPICALLTYPE ITCallAddress(
        DWORD cbInput, CallAddressParams *pbInput,
        DWORD *pcbOutput, BYTE **ppbOutput,
        IRAPIStream *pStream)
{
    *pcbOutput= 0;
    *ppbOutput= NULL;

    typedef void (*calltarget_fn)();

    calltarget_fn  calltarget= (calltarget_fn)pbInput->dwAddress;

    KernelMode _km;

    // todo: if (pbInput->hProcess==INVALID_HANDLE_VALUE) -> call physical address
    // if (pbInput->hProcess==NULL) -> call virtual address
    // else call to address in process space
    calltarget();


    return 0;
}

ITSUTILS_API HRESULT STDAPICALLTYPE ITRegistryFlush(
        DWORD cbInput, BYTE *pbInput,
        DWORD *pcbOutput, BYTE **ppbOutput,
        IRAPIStream *pStream)
{
    *pcbOutput= 0;
    *ppbOutput= NULL;

    LONG res;
    res= RegFlushKey(HKEY_CLASSES_ROOT);
    if (res) error("RegFlushKey(HKEY_CLASSES_ROOT)\n");
    res= RegFlushKey(HKEY_CURRENT_USER);
    if (res) error("RegFlushKey(HKEY_CURRENT_USER)\n");
    res= RegFlushKey(HKEY_LOCAL_MACHINE);
    if (res) error("RegFlushKey(HKEY_LOCAL_MACHINE)\n");
    res= RegFlushKey(HKEY_USERS);
    if (res) error("RegFlushKey(HKEY_USERS)\n");

    return 0;
}


#include "smsfunc.h"
ITSUTILS_API HRESULT STDAPICALLTYPE ITSendSms(
        DWORD cbInput, SendSmsParams *pbInput,
        DWORD *pcbOutput, SendSmsResult **ppbOutput,
        IRAPIStream *pStream)
{
    smsfunc sms;
    if (!sms.load())
        return GetLastError();
    if (!sms.send(pbInput->msg, pbInput->number))
        return GetLastError();
    *pcbOutput= 0;
    *ppbOutput= NULL;
    return 0;
}

#include "rilfunc.h"
ITSUTILS_API HRESULT STDAPICALLTYPE ITDialNumber(
        DWORD cbInput, DialNumberParams *pbInput,
        DWORD *pcbOutput, DialNumberResult **ppbOutput,
        IRAPIStream *pStream)
{
    rilfunc ril;
    if (!ril.load())
        return GetLastError();
    if (!ril.dial(pbInput->number))
        return GetLastError();

    *pcbOutput= 0;
    *ppbOutput= NULL;
    return 0;
}



