/*
 * IMM32 library
 *
 * Copyright 1998 Patrik Stridvall
 * Copyright 2002, 2003 CodeWeavers, Aric Stewart
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winerror.h"
#include "wine/debug.h"
#include "imm.h"
#include "winnls.h"

WINE_DEFAULT_DEBUG_CHANNEL(imm);

#define FROM_IME 0xcafe1337

static void (*pX11DRV_ForceXIMReset)(HWND);

typedef struct tagInputContextData
{
        LPBYTE          CompositionString;
        LPBYTE          CompositionReadingString;
        LPBYTE          ResultString;
        LPBYTE          ResultReadingString;
        DWORD           dwCompStringSize;   /* buffer size */
        DWORD           dwCompStringLength; /* string length (in bytes) */
        DWORD           dwCompReadStringSize;
        DWORD           dwResultStringSize;
        DWORD           dwResultReadStringSize;
        HWND            hwnd;
        BOOL            bOpen;
        BOOL            bInternalState;
        BOOL            bRead;
        LOGFONTW        font;
        HFONT           textfont;
        COMPOSITIONFORM CompForm;
} InputContextData;

static InputContextData *root_context = NULL;
static HWND hwndDefault = NULL;
static HANDLE hImeInst;
static const WCHAR WC_IMECLASSNAME[] = {'I','M','E',0};

/* MSIME messages */
static UINT WM_MSIME_SERVICE;
static UINT WM_MSIME_RECONVERTOPTIONS;
static UINT WM_MSIME_MOUSE;
static UINT WM_MSIME_RECONVERTREQUEST;
static UINT WM_MSIME_RECONVERT;
static UINT WM_MSIME_QUERYPOSITION;
static UINT WM_MSIME_DOCUMENTFEED;

/*
 * prototypes
 */
static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
                                          LPARAM lParam);
static void UpdateDataInDefaultIMEWindow(HWND hwnd);
static void ImmInternalPostIMEMessage(UINT, WPARAM, LPARAM);
static void ImmInternalSetOpenStatus(BOOL fOpen);

static VOID IMM_PostResult(InputContextData *data)
{
    unsigned int i;
    TRACE("Posting result as IME_CHAR\n");

    for (i = 0; i < data->dwResultStringSize / sizeof (WCHAR); i++)
        ImmInternalPostIMEMessage (WM_IME_CHAR, ((WCHAR*)data->ResultString)[i],
                     1);

    /* clear the buffer */
    if (data->dwResultStringSize)
        HeapFree(GetProcessHeap(),0,data->ResultString);
    data->dwResultStringSize = 0;
    data->ResultString = NULL;
}

static void IMM_Register(void)
{
    WNDCLASSW wndClass;
    ZeroMemory(&wndClass, sizeof(WNDCLASSW));
    wndClass.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = (WNDPROC) IME_WindowProc;
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hImeInst;
    wndClass.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW);
    wndClass.hIcon = NULL;
    wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW +1);
    wndClass.lpszMenuName   = 0;
    wndClass.lpszClassName = WC_IMECLASSNAME;
    RegisterClassW(&wndClass);
}

static void IMM_Unregister(void)
{
    UnregisterClassW(WC_IMECLASSNAME, NULL);
}

static void IMM_RegisterMessages(void)
{
    WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService");
    WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions");
    WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation");
    WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest");
    WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert");
    WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition");
    WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed");
}


BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved)
{
    HMODULE x11drv;

    TRACE("%p, %lx, %p\n",hInstDLL,fdwReason,lpReserved);
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls(hInstDLL);
            hImeInst = hInstDLL;
            IMM_RegisterMessages();
            x11drv = GetModuleHandleA("x11drv.dll");
            if (x11drv) pX11DRV_ForceXIMReset = (void *)GetProcAddress( x11drv, "ForceXIMReset");
            break;
        case DLL_PROCESS_DETACH:
            if (hwndDefault)
            {
                DestroyWindow(hwndDefault);
                hwndDefault = 0;
            }
            IMM_Unregister();
            break;
    }
    return TRUE;
}

/* for posting messages as the IME */
static void ImmInternalPostIMEMessage(UINT msg, WPARAM wParam, LPARAM lParam)
{
    HWND target = GetFocus();
    if (!target)
       PostMessageW(root_context->hwnd,msg,wParam,lParam);
    else 
       PostMessageW(target, msg, wParam, lParam);
}


static void ImmInternalSetOpenStatus(BOOL fOpen)
{
    TRACE("Setting internal state to %s\n",(fOpen)?"OPEN":"CLOSED");

   root_context->bOpen = fOpen;
   root_context->bInternalState = fOpen;

   if (fOpen == FALSE)
   {
        ShowWindow(hwndDefault,SW_HIDE);

        if (root_context->dwCompStringSize)
            HeapFree(GetProcessHeap(),0,root_context->CompositionString);
        if (root_context->dwCompReadStringSize)
            HeapFree(GetProcessHeap(),0,root_context->CompositionReadingString);
        if (root_context->dwResultStringSize)
            HeapFree(GetProcessHeap(),0,root_context->ResultString);
        if (root_context->dwResultReadStringSize)
            HeapFree(GetProcessHeap(),0,root_context->ResultReadingString);
        root_context->dwCompStringSize = 0;
        root_context->dwCompStringLength = 0;
        root_context->CompositionString = NULL;
        root_context->dwCompReadStringSize = 0;
        root_context->CompositionReadingString = NULL;
        root_context->dwResultStringSize = 0;
        root_context->ResultString = NULL;
        root_context->dwResultReadStringSize = 0;
        root_context->ResultReadingString = NULL;
    }
    else
        ShowWindow(hwndDefault, SW_SHOWNOACTIVATE);

   SendMessageW(root_context->hwnd, WM_IME_NOTIFY, IMN_SETOPENSTATUS, 0);
}


/***********************************************************************
 *		ImmAssociateContext (IMM32.@)
 */
HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
{
    InputContextData *data = (InputContextData*)hIMC;

    WARN("(%p, %p): semi-stub\n",hWnd,hIMC);

    if (!data)
        return FALSE;

    /*
     * WINE SPECIFIC! MAY CONFLICT
     * associate the root context we have an XIM created
     */
    if (hWnd == 0x000)
    {
        root_context = (InputContextData*)hIMC;
    }

    /*
     * If already associated just return
     */
    if (data->hwnd == hWnd)
        return (HIMC)data;

    if (IsWindow(data->hwnd))
    {
        /*
         * Post a message that your context is switching
         */
        SendMessageW(data->hwnd, WM_IME_SETCONTEXT, FALSE, ISC_SHOWUIALL);
    }

    data->hwnd = hWnd;

    if (IsWindow(data->hwnd))
    {
        /*
         * Post a message that your context is switching
         */
        SendMessageW(data->hwnd, WM_IME_SETCONTEXT, TRUE, ISC_SHOWUIALL);
    }

    /*
     * TODO: We need to keep track of the old context associated
     * with a window and return it for now we will return NULL;
     */
    return (HIMC)NULL;
}

/***********************************************************************
 *              ImmAssociateContextEx (IMM32.@)
 */
BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
{
    FIXME("(%p, %p, %ld): stub\n", hWnd, hIMC, dwFlags);
    return FALSE;
}

/***********************************************************************
 *		ImmConfigureIMEA (IMM32.@)
 */
BOOL WINAPI ImmConfigureIMEA(
  HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData)
{
  FIXME("(%p, %p, %ld, %p): stub\n",
    hKL, hWnd, dwMode, lpData
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmConfigureIMEW (IMM32.@)
 */
BOOL WINAPI ImmConfigureIMEW(
  HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData)
{
  FIXME("(%p, %p, %ld, %p): stub\n",
    hKL, hWnd, dwMode, lpData
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmCreateContext (IMM32.@)
 */
HIMC WINAPI ImmCreateContext(void)
{
    InputContextData *new_context;

    new_context = HeapAlloc(GetProcessHeap(),0,sizeof(InputContextData));
    ZeroMemory(new_context,sizeof(InputContextData));

    return (HIMC)new_context;
}

/***********************************************************************
 *		ImmDestroyContext (IMM32.@)
 */
BOOL WINAPI ImmDestroyContext(HIMC hIMC)
{
    InputContextData *data = (InputContextData*)hIMC;

    TRACE("Destroying %p\n",hIMC);

    if (hIMC)
    {
        if (data->dwCompStringSize)
            HeapFree(GetProcessHeap(),0,data->CompositionString);
        if (data->dwCompReadStringSize)
            HeapFree(GetProcessHeap(),0,data->CompositionReadingString);
        if (data->dwResultStringSize)
            HeapFree(GetProcessHeap(),0,data->ResultString);
        if (data->dwResultReadStringSize)
            HeapFree(GetProcessHeap(),0,data->ResultReadingString);

        if (data->textfont)
        {
            DeleteObject(data->textfont);
            data->textfont = NULL;
        }

        HeapFree(GetProcessHeap(),0,data);
    }
    return TRUE;
}

/***********************************************************************
 *		ImmDisableIME (IMM32.@)
 */
BOOL WINAPI ImmDisableIME(DWORD idThread)
{
    FIXME("(%ld): stub\n", idThread);
    return TRUE;
}

/***********************************************************************
 *		ImmEnumRegisterWordA (IMM32.@)
 */
UINT WINAPI ImmEnumRegisterWordA(
  HKL hKL, REGISTERWORDENUMPROCA lpfnEnumProc,
  LPCSTR lpszReading, DWORD dwStyle,
  LPCSTR lpszRegister, LPVOID lpData)
{
  FIXME("(%p, %p, %s, %ld, %s, %p): stub\n",
    hKL, lpfnEnumProc,
    debugstr_a(lpszReading), dwStyle,
    debugstr_a(lpszRegister), lpData
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmEnumRegisterWordW (IMM32.@)
 */
UINT WINAPI ImmEnumRegisterWordW(
  HKL hKL, REGISTERWORDENUMPROCW lpfnEnumProc,
  LPCWSTR lpszReading, DWORD dwStyle,
  LPCWSTR lpszRegister, LPVOID lpData)
{
  FIXME("(%p, %p, %s, %ld, %s, %p): stub\n",
    hKL, lpfnEnumProc,
    debugstr_w(lpszReading), dwStyle,
    debugstr_w(lpszRegister), lpData
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmEscapeA (IMM32.@)
 */
LRESULT WINAPI ImmEscapeA(
  HKL hKL, HIMC hIMC,
  UINT uEscape, LPVOID lpData)
{
  FIXME("(%p, %p, %d, %p): stub\n",
    hKL, hIMC, uEscape, lpData
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmEscapeW (IMM32.@)
 */
LRESULT WINAPI ImmEscapeW(
  HKL hKL, HIMC hIMC,
  UINT uEscape, LPVOID lpData)
{
  FIXME("(%p, %p, %d, %p): stub\n",
    hKL, hIMC, uEscape, lpData
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetCandidateListA (IMM32.@)
 */
DWORD WINAPI ImmGetCandidateListA(
  HIMC hIMC, DWORD deIndex,
  LPCANDIDATELIST lpCandList, DWORD dwBufLen)
{
  FIXME("(%p, %ld, %p, %ld): stub\n",
    hIMC, deIndex,
    lpCandList, dwBufLen
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetCandidateListCountA (IMM32.@)
 */
DWORD WINAPI ImmGetCandidateListCountA(
  HIMC hIMC, LPDWORD lpdwListCount)
{
  FIXME("(%p, %p): stub\n", hIMC, lpdwListCount);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetCandidateListCountW (IMM32.@)
 */
DWORD WINAPI ImmGetCandidateListCountW(
  HIMC hIMC, LPDWORD lpdwListCount)
{
  FIXME("(%p, %p): stub\n", hIMC, lpdwListCount);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetCandidateListW (IMM32.@)
 */
DWORD WINAPI ImmGetCandidateListW(
  HIMC hIMC, DWORD deIndex,
  LPCANDIDATELIST lpCandList, DWORD dwBufLen)
{
  FIXME("(%p, %ld, %p, %ld): stub\n",
    hIMC, deIndex,
    lpCandList, dwBufLen
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetCandidateWindow (IMM32.@)
 */
BOOL WINAPI ImmGetCandidateWindow(
  HIMC hIMC, DWORD dwBufLen, LPCANDIDATEFORM lpCandidate)
{
  FIXME("(%p, %ld, %p): stub\n", hIMC, dwBufLen, lpCandidate);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmGetCompositionFontA (IMM32.@)
 */
BOOL WINAPI ImmGetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf)
{
  FIXME("(%p, %p): stub\n", hIMC, lplf);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmGetCompositionFontW (IMM32.@)
 */
BOOL WINAPI ImmGetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf)
{
  FIXME("(%p, %p): stub\n", hIMC, lplf);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmGetCompositionStringA (IMM32.@)
 */
LONG WINAPI ImmGetCompositionStringA(
  HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
{
    CHAR *buf;
    LONG rc = 0;
    InputContextData *data = (InputContextData*)hIMC;

    TRACE("(%p, 0x%lx, %p, %ld)\n", hIMC, dwIndex, lpBuf, dwBufLen);

    if (!data)
       return FALSE;

    if (dwIndex == GCS_RESULTSTR)
    {
        TRACE("GSC_RESULTSTR %p %li\n",data->ResultString,
                                    data->dwResultStringSize);

        buf = HeapAlloc( GetProcessHeap(), 0, data->dwResultStringSize * 3 );
        rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)data->ResultString,
                                 data->dwResultStringSize / sizeof(WCHAR), buf,
                                 data->dwResultStringSize * 3, NULL, NULL);
        if (dwBufLen >= rc)
            memcpy(lpBuf,buf,rc);

        data->bRead = TRUE;
        HeapFree( GetProcessHeap(), 0, buf );
    }
    else if (dwIndex == GCS_COMPSTR)
    {
         TRACE("GSC_COMPSTR %p %li\n",data->CompositionString,
                                     data->dwCompStringLength/ sizeof(WCHAR));

        buf = HeapAlloc( GetProcessHeap(), 0, data->dwCompStringLength * 3 );
        rc = WideCharToMultiByte(CP_ACP, 0,(LPWSTR)data->CompositionString,
                                 data->dwCompStringLength/ sizeof(WCHAR), buf,
                                 data->dwCompStringLength* 3, NULL, NULL);
        if (dwBufLen >= rc)
            memcpy(lpBuf,buf,rc);
        HeapFree( GetProcessHeap(), 0, buf );
    }
    else if (dwIndex == GCS_COMPATTR)
    {
        TRACE("GSC_COMPATTR %p %li\n",data->CompositionString,
                                    data->dwCompStringLength/ sizeof(WCHAR));

        rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)data->CompositionString,
                                 data->dwCompStringLength/ sizeof(WCHAR), NULL,
                                 0, NULL, NULL);
 
        if (dwBufLen >= rc)
        {
            int i=0;
            for (i = 0;  i < rc; i++)
                ((LPBYTE)lpBuf)[i] = ATTR_INPUT;
    }
    }
    else if (dwIndex == GCS_COMPCLAUSE)
    {
        TRACE("GSC_COMPCLAUSE %p %li\n",data->CompositionString,
                                    data->dwCompStringLength/ sizeof(WCHAR));
 
        rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)data->CompositionString,
                                 data->dwCompStringLength/ sizeof(WCHAR), NULL,
                                 0, NULL, NULL);

        if (dwBufLen >= sizeof(DWORD)*2)
        {
            ((LPDWORD)lpBuf)[0] = 0;
            ((LPDWORD)lpBuf)[1] = rc;
        }
        rc = sizeof(DWORD)*2;
    }
    else
    {
        FIXME("Unhandled index 0x%lx\n",dwIndex);
    }

    return rc;
}

/***********************************************************************
 *		ImmGetCompositionStringW (IMM32.@)
 */
LONG WINAPI ImmGetCompositionStringW(
  HIMC hIMC, DWORD dwIndex,
  LPVOID lpBuf, DWORD dwBufLen)
{
  LONG rc = 0;
  InputContextData *data = (InputContextData*)hIMC;

  TRACE("(%p, 0x%lx, %p, %ld)\n",
    hIMC, dwIndex, lpBuf, dwBufLen
  );

    if (!data)
       return FALSE;

    if (dwIndex == GCS_RESULTSTR)
    {
        data->bRead = TRUE;

        if (dwBufLen >= data->dwResultStringSize)
            memcpy(lpBuf,data->ResultString,data->dwResultStringSize);
        
        rc =  data->dwResultStringSize;
    }
    else if (dwIndex == GCS_RESULTREADSTR)
    {
        if (dwBufLen >= data->dwResultReadStringSize)
            memcpy(lpBuf,data->ResultReadingString,
                    data->dwResultReadStringSize);
        
        rc = data->dwResultReadStringSize;
    }   
    else if (dwIndex == GCS_COMPSTR)
    {
        if (dwBufLen >= data->dwCompStringLength)
            memcpy(lpBuf,data->CompositionString,data->dwCompStringLength);

        rc = data->dwCompStringLength;
    }
    else if (dwIndex == GCS_COMPATTR)
    {
        unsigned int len = data->dwCompStringLength;
        
        if (dwBufLen >= len)
        {
            unsigned int i=0;
            for (i = 0;  i < len; i++)
                ((LPBYTE)lpBuf)[i] = ATTR_INPUT;
        }

        rc = len;
    }
    else if (dwIndex == GCS_COMPCLAUSE)
    {
        if (dwBufLen >= sizeof(DWORD)*2)
        {
            ((LPDWORD)lpBuf)[0] = 0;
            ((LPDWORD)lpBuf)[1] = data->dwCompStringLength/sizeof(WCHAR);
        }
        rc = sizeof(DWORD)*2;
    }
    else if (dwIndex == GCS_COMPREADSTR)
    {
        if (dwBufLen >= data->dwCompReadStringSize)
            memcpy(lpBuf,data->CompositionReadingString,
                    data->dwCompReadStringSize);
        
        rc = data->dwCompReadStringSize;
    }   
    else
    {
        FIXME("Unhandled index 0x%lx\n",dwIndex);
    }   

    return rc;
}

/***********************************************************************
 *		ImmGetCompositionWindow (IMM32.@)
 */
BOOL WINAPI ImmGetCompositionWindow(HIMC hIMC, LPCOMPOSITIONFORM lpCompForm)
{
    InputContextData *data = (InputContextData*)hIMC;

    TRACE("(%p, %p)\n", hIMC, lpCompForm);

    if (!data)
        return FALSE;

    memcpy(lpCompForm,&(data->CompForm),sizeof(COMPOSITIONFORM));
    return 1;
}

/***********************************************************************
 *		ImmGetContext (IMM32.@)
 *
 */
HIMC WINAPI ImmGetContext(HWND hWnd)
{
    FIXME("(%p): stub\n", hWnd);

    if (!root_context)
        return NULL;

    root_context->hwnd = hWnd;
    return (HIMC)root_context;
}

/***********************************************************************
 *		ImmGetConversionListA (IMM32.@)
 */
DWORD WINAPI ImmGetConversionListA(
  HKL hKL, HIMC hIMC,
  LPCSTR pSrc, LPCANDIDATELIST lpDst,
  DWORD dwBufLen, UINT uFlag)
{
  FIXME("(%p, %p, %s, %p, %ld, %d): stub\n",
    hKL, hIMC, debugstr_a(pSrc), lpDst, dwBufLen, uFlag
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetConversionListW (IMM32.@)
 */
DWORD WINAPI ImmGetConversionListW(
  HKL hKL, HIMC hIMC,
  LPCWSTR pSrc, LPCANDIDATELIST lpDst,
  DWORD dwBufLen, UINT uFlag)
{
  FIXME("(%p, %p, %s, %p, %ld, %d): stub\n",
    hKL, hIMC, debugstr_w(pSrc), lpDst, dwBufLen, uFlag
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetConversionStatus (IMM32.@)
 */
BOOL WINAPI ImmGetConversionStatus(
  HIMC hIMC, LPDWORD lpfdwConversion, LPDWORD lpfdwSentence)
{
    TRACE("(%p, %p, %p): best guess\n", hIMC, lpfdwConversion, lpfdwSentence);
    if (lpfdwConversion)
        *lpfdwConversion = IME_CMODE_NATIVE;
    if (lpfdwSentence)
        *lpfdwSentence = IME_SMODE_NONE;
    return TRUE;
}

/***********************************************************************
 *		ImmGetDefaultIMEWnd (IMM32.@)
 */
HWND WINAPI ImmGetDefaultIMEWnd(HWND hWnd)
{
  FIXME("(%p - %p %p ): semi-stub\n", hWnd,hwndDefault, root_context);

  if (hwndDefault == NULL)
  {
        static const WCHAR the_name[] = {'I','M','E','\0'};

        IMM_Register();
        hwndDefault = CreateWindowExW( WS_EX_CLIENTEDGE, WC_IMECLASSNAME,
                the_name, WS_POPUPWINDOW|WS_CAPTION, 0, 0, 120, 55, 0, 0,
                hImeInst, 0);

        TRACE("Default created (0x%x)\n",(INT)hwndDefault);
  }

  return (HWND)hwndDefault;
}

/***********************************************************************
 *		ImmGetDescriptionA (IMM32.@)
 */
UINT WINAPI ImmGetDescriptionA(
  HKL hKL, LPSTR lpszDescription, UINT uBufLen)
{
  WCHAR *buf;
  DWORD len;

  TRACE("%p %p %d\n", hKL, lpszDescription, uBufLen);

  /* find out how many characters in the unicode buffer */
  len = ImmGetDescriptionW( hKL, NULL, 0 );

  /* allocate a buffer of that size */
  buf = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof (WCHAR) );
  if( !buf )
  return 0;

  /* fetch the unicode buffer */
  len = ImmGetDescriptionW( hKL, buf, len + 1 );

  /* convert it back to ASCII */
  len = WideCharToMultiByte( CP_ACP, 0, buf, len + 1,
                             lpszDescription, uBufLen, NULL, NULL );

  HeapFree( GetProcessHeap(), 0, buf );

  return len;
}

/***********************************************************************
 *		ImmGetDescriptionW (IMM32.@)
 */
UINT WINAPI ImmGetDescriptionW(HKL hKL, LPWSTR lpszDescription, UINT uBufLen)
{
  static const WCHAR name[] = { 'W','i','n','e',' ','X','I','M',0 };

  FIXME("(%p, %p, %d): semi stub\n", hKL, lpszDescription, uBufLen);

  if (!uBufLen) return lstrlenW( name );
  lstrcpynW( lpszDescription, name, uBufLen );
  return lstrlenW( lpszDescription );
}

/***********************************************************************
 *		ImmGetGuideLineA (IMM32.@)
 */
DWORD WINAPI ImmGetGuideLineA(
  HIMC hIMC, DWORD dwIndex, LPSTR lpBuf, DWORD dwBufLen)
{
  FIXME("(%p, %ld, %s, %ld): stub\n",
    hIMC, dwIndex, debugstr_a(lpBuf), dwBufLen
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetGuideLineW (IMM32.@)
 */
DWORD WINAPI ImmGetGuideLineW(HIMC hIMC, DWORD dwIndex, LPWSTR lpBuf, DWORD dwBufLen)
{
  FIXME("(%p, %ld, %s, %ld): stub\n",
    hIMC, dwIndex, debugstr_w(lpBuf), dwBufLen
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetIMEFileNameA (IMM32.@)
 */
UINT WINAPI ImmGetIMEFileNameA(
  HKL hKL, LPSTR lpszFileName, UINT uBufLen)
{
  FIXME("(%p, %p, %d): stub\n", hKL, lpszFileName, uBufLen);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetIMEFileNameW (IMM32.@)
 */
UINT WINAPI ImmGetIMEFileNameW(
  HKL hKL, LPWSTR lpszFileName, UINT uBufLen)
{
  FIXME("(%p, %p, %d): stub\n", hKL, lpszFileName, uBufLen);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetOpenStatus (IMM32.@)
 */
BOOL WINAPI ImmGetOpenStatus(HIMC hIMC)
{
  InputContextData *data = (InputContextData*)hIMC;

    if (!data)
        return FALSE;
  FIXME("(%p): semi-stub\n", hIMC);

  return data->bOpen;
}

/***********************************************************************
 *		ImmGetProperty (IMM32.@)
 */
DWORD WINAPI ImmGetProperty(HKL hKL, DWORD fdwIndex)
{
    DWORD rc = 0;
    TRACE("(%p, %ld)\n", hKL, fdwIndex);

    switch (fdwIndex)
    {
        case IGP_PROPERTY:
            TRACE("(%s)\n", "IGP_PROPERTY");
            rc = IME_PROP_UNICODE | IME_PROP_AT_CARET;
            break;
        case IGP_CONVERSION:
            FIXME("(%s)\n", "IGP_CONVERSION");
            rc = IME_CMODE_NATIVE;
            break;
        case IGP_SENTENCE:
            FIXME("%s)\n", "IGP_SENTENCE");
            rc = IME_SMODE_AUTOMATIC;
            break;
        case IGP_SETCOMPSTR:
            TRACE("(%s)\n", "IGP_SETCOMPSTR");
            rc = 0;
            break;
        case IGP_SELECT:
            TRACE("(%s)\n", "IGP_SELECT");
            rc = SELECT_CAP_CONVERSION | SELECT_CAP_SENTENCE;
            break;
        case IGP_GETIMEVERSION:
            TRACE("(%s)\n", "IGP_GETIMEVERSION");
            rc = IMEVER_0400;
            break;
        case IGP_UI:
            TRACE("(%s)\n", "IGP_UI");
            rc = 0;
            break;
        default:
            rc = 0;
    }
    return rc;
}

/***********************************************************************
 *		ImmGetRegisterWordStyleA (IMM32.@)
 */
UINT WINAPI ImmGetRegisterWordStyleA(
  HKL hKL, UINT nItem, LPSTYLEBUFA lpStyleBuf)
{
  FIXME("(%p, %d, %p): stub\n", hKL, nItem, lpStyleBuf);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetRegisterWordStyleW (IMM32.@)
 */
UINT WINAPI ImmGetRegisterWordStyleW(
  HKL hKL, UINT nItem, LPSTYLEBUFW lpStyleBuf)
{
  FIXME("(%p, %d, %p): stub\n", hKL, nItem, lpStyleBuf);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return 0;
}

/***********************************************************************
 *		ImmGetStatusWindowPos (IMM32.@)
 */
BOOL WINAPI ImmGetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos)
{
  FIXME("(%p, %p): stub\n", hIMC, lpptPos);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmGetVirtualKey (IMM32.@)
 */
UINT WINAPI ImmGetVirtualKey(HWND hWnd)
{
  OSVERSIONINFOA version;
  FIXME("(%p): stub\n", hWnd);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  GetVersionExA( &version );
  switch(version.dwPlatformId)
  {
  case VER_PLATFORM_WIN32_WINDOWS:
      return VK_PROCESSKEY;
  case VER_PLATFORM_WIN32_NT:
      return 0;
  default:
      FIXME("%ld not supported\n",version.dwPlatformId);
      return VK_PROCESSKEY;
  }
}

/***********************************************************************
 *		ImmInstallIMEA (IMM32.@)
 */
HKL WINAPI ImmInstallIMEA(
  LPCSTR lpszIMEFileName, LPCSTR lpszLayoutText)
{
  FIXME("(%s, %s): stub\n",
    debugstr_a(lpszIMEFileName), debugstr_a(lpszLayoutText)
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return NULL;
}

/***********************************************************************
 *		ImmInstallIMEW (IMM32.@)
 */
HKL WINAPI ImmInstallIMEW(
  LPCWSTR lpszIMEFileName, LPCWSTR lpszLayoutText)
{
  FIXME("(%s, %s): stub\n",
    debugstr_w(lpszIMEFileName), debugstr_w(lpszLayoutText)
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return NULL;
}

/***********************************************************************
 *		ImmIsIME (IMM32.@)
 */
BOOL WINAPI ImmIsIME(HKL hKL)
{
  FIXME("(%p): semi-stub\n", hKL);
  /*
   * Dead key locales will return TRUE here when they should not
   * There is probably a more proper way to check this.
   */
  return (root_context != NULL);
}

/***********************************************************************
 *		ImmIsUIMessageA (IMM32.@)
 */
BOOL WINAPI ImmIsUIMessageA(
  HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
{
    BOOL rc = FALSE;

    TRACE("(%p, %x, %d, %ld)\n", hWndIME, msg, wParam, lParam);
    if ((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
        (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP) ||
        (msg == WM_MSIME_SERVICE) ||
        (msg == WM_MSIME_RECONVERTOPTIONS) ||
        (msg == WM_MSIME_MOUSE) ||
        (msg == WM_MSIME_RECONVERTREQUEST) ||
        (msg == WM_MSIME_RECONVERT) ||
        (msg == WM_MSIME_QUERYPOSITION) ||
        (msg == WM_MSIME_DOCUMENTFEED))

    {
        if (!hwndDefault)
            ImmGetDefaultIMEWnd(NULL);

        if (hWndIME == NULL)
            PostMessageA(hwndDefault, msg, wParam, lParam);

        rc = TRUE;
    }
    return rc;
}

/***********************************************************************
 *		ImmIsUIMessageW (IMM32.@)
 */
BOOL WINAPI ImmIsUIMessageW(
  HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
{
    BOOL rc = FALSE;
    TRACE("(%p, %d, %d, %ld): stub\n", hWndIME, msg, wParam, lParam);
    if ((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
        (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP) ||
        (msg == WM_MSIME_SERVICE) ||
        (msg == WM_MSIME_RECONVERTOPTIONS) ||
        (msg == WM_MSIME_MOUSE) ||
        (msg == WM_MSIME_RECONVERTREQUEST) ||
        (msg == WM_MSIME_RECONVERT) ||
        (msg == WM_MSIME_QUERYPOSITION) ||
        (msg == WM_MSIME_DOCUMENTFEED))
        rc = TRUE;
    return rc;
}

/***********************************************************************
 *		ImmNotifyIME (IMM32.@)
 */
BOOL WINAPI ImmNotifyIME(
  HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
{
    BOOL rc = FALSE;
  FIXME("(%p, %ld, %ld, %ld): stub\n",
        hIMC, dwAction, dwIndex, dwValue);

    if (!root_context)
        return rc;

    switch(dwAction)
    {
        case NI_CHANGECANDIDATELIST:
            FIXME("%s\n","NI_CHANGECANDIDATELIST");
            break;
        case NI_CLOSECANDIDATE:
            FIXME("%s\n","NI_CLOSECANDIDATE");
            break;
        case NI_COMPOSITIONSTR:
            switch (dwIndex)
            {
                case CPS_CANCEL:
                    TRACE("%s - %s\n","NI_COMPOSITIONSTR","CPS_CANCEL");
                    if (pX11DRV_ForceXIMReset)
                        pX11DRV_ForceXIMReset(root_context->hwnd);
                    if (root_context->dwCompStringSize)
                    {
                        HeapFree(GetProcessHeap(),0,
                                 root_context->CompositionString);
                        root_context->dwCompStringSize = 0;
                        root_context->dwCompStringLength = 0;
                        root_context->CompositionString = NULL;
                        ImmInternalPostIMEMessage(WM_IME_COMPOSITION, 0,
                                                  GCS_COMPSTR);
                    }
                    rc = TRUE;
                    break;
                case CPS_COMPLETE:
                    TRACE("%s - %s\n","NI_COMPOSITIONSTR","CPS_COMPLETE");
                    if (hIMC != (HIMC)FROM_IME && pX11DRV_ForceXIMReset)
                        pX11DRV_ForceXIMReset(root_context->hwnd);

                    if (root_context->dwResultStringSize)
                    {
                        HeapFree(GetProcessHeap(),0,root_context->ResultString);
                        root_context->dwResultStringSize = 0;
                        root_context->ResultString = NULL;
                    }
                    if (root_context->dwCompStringLength)
                    {
                        root_context->ResultString = HeapAlloc(
                        GetProcessHeap(), 0, root_context->dwCompStringLength);
                        root_context->dwResultStringSize =
                                        root_context->dwCompStringLength;

                        memcpy(root_context->ResultString,
                               root_context->CompositionString,
                               root_context->dwCompStringLength);

                        HeapFree(GetProcessHeap(),0,
                                 root_context->CompositionString);

                        root_context->dwCompStringSize = 0;
                        root_context->dwCompStringLength = 0;
                        root_context->CompositionString = NULL;
                        root_context->bRead = FALSE;

                        ImmInternalPostIMEMessage(WM_IME_COMPOSITION, 0,
                                                  GCS_COMPSTR);

                        ImmInternalPostIMEMessage(WM_IME_COMPOSITION,
                                            root_context->ResultString[0],
                                            GCS_RESULTSTR|GCS_RESULTCLAUSE);
                    }
                    break;
                case CPS_CONVERT:
                    FIXME("%s - %s\n","NI_COMPOSITIONSTR","CPS_CONVERT");
                    break;
                case CPS_REVERT:
                    FIXME("%s - %s\n","NI_COMPOSITIONSTR","CPS_REVERT");
                    break;
                default:
                    ERR("%s - %s (%li)\n","NI_COMPOSITIONSTR","UNKNOWN",dwIndex);
                    break;
            }
            break;
        case NI_IMEMENUSELECTED:
            FIXME("%s\n", "NI_IMEMENUSELECTED");
            break;
        case NI_OPENCANDIDATE:
            FIXME("%s\n", "NI_OPENCANDIDATE");
            break;
        case NI_SELECTCANDIDATESTR:
            FIXME("%s\n", "NI_SELECTCANDIDATESTR");
            break;
        case NI_SETCANDIDATE_PAGESIZE:
            FIXME("%s\n", "NI_SETCANDIDATE_PAGESIZE");
            break;
        case NI_SETCANDIDATE_PAGESTART:
            FIXME("%s\n", "NI_SETCANDIDATE_PAGESTART");
            break;
        default:
            ERR("Unknown\n");
    }
  
    return rc;
}

/***********************************************************************
 *		ImmRegisterWordA (IMM32.@)
 */
BOOL WINAPI ImmRegisterWordA(
  HKL hKL, LPCSTR lpszReading, DWORD dwStyle, LPCSTR lpszRegister)
{
  FIXME("(%p, %s, %ld, %s): stub\n",
    hKL, debugstr_a(lpszReading), dwStyle, debugstr_a(lpszRegister)
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmRegisterWordW (IMM32.@)
 */
BOOL WINAPI ImmRegisterWordW(
  HKL hKL, LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszRegister)
{
  FIXME("(%p, %s, %ld, %s): stub\n",
    hKL, debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister)
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmReleaseContext (IMM32.@)
 */
BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
{
  FIXME("(%p, %p): stub\n", hWnd, hIMC);

    return TRUE;
}

/***********************************************************************
 *		ImmSetCandidateWindow (IMM32.@)
 */
BOOL WINAPI ImmSetCandidateWindow(
  HIMC hIMC, LPCANDIDATEFORM lpCandidate)
{
  FIXME("(%p, %p): stub\n", hIMC, lpCandidate);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmSetCompositionFontA (IMM32.@)
 */
BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf)
{
    InputContextData *data = (InputContextData*)hIMC;
    TRACE("(%p, %p)\n", hIMC, lplf);

    if (!data)
        return FALSE;

    memcpy(&data->font,lplf,sizeof(LOGFONTA));
    MultiByteToWideChar(CP_ACP, 0, lplf->lfFaceName, -1, data->font.lfFaceName,
                        LF_FACESIZE);

    SendMessageW(root_context->hwnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONFONT, 0);

    if (data->textfont)
    {
        DeleteObject(data->textfont);
        data->textfont = NULL;
    }

    data->textfont = CreateFontIndirectW(&data->font); 
    return TRUE;
}

/***********************************************************************
 *		ImmSetCompositionFontW (IMM32.@)
 */
BOOL WINAPI ImmSetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf)
{
    InputContextData *data = (InputContextData*)hIMC;
    TRACE("(%p, %p)\n", hIMC, lplf);

    if (!data)
        return FALSE;

    memcpy(&data->font,lplf,sizeof(LOGFONTW));
    SendMessageW(root_context->hwnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONFONT, 0);

    if (data->textfont)
    {
        DeleteObject(data->textfont);
        data->textfont = NULL;
    }
    data->textfont = CreateFontIndirectW(&data->font); 
    return TRUE;
}

/***********************************************************************
 *		ImmSetCompositionStringA (IMM32.@)
 */
BOOL WINAPI ImmSetCompositionStringA(
  HIMC hIMC, DWORD dwIndex,
  LPCVOID lpComp, DWORD dwCompLen,
  LPCVOID lpRead, DWORD dwReadLen)
{
    DWORD comp_len;
    DWORD read_len;
    WCHAR *CompBuffer = NULL;
    WCHAR *ReadBuffer = NULL;
    BOOL rc;

    TRACE("(%p, %ld, %p, %ld, %p, %ld): stub\n",
            hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);

    comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0);
    if (comp_len)
    {
        CompBuffer = (WCHAR*)HeapAlloc(GetProcessHeap(),0,comp_len * sizeof(WCHAR));
        MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len);
    }

    read_len = MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, NULL, 0);
    if (read_len)
    {
        ReadBuffer = (WCHAR*)HeapAlloc(GetProcessHeap(),0,read_len * sizeof(WCHAR));
        MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len);
    }

    rc =  ImmSetCompositionStringW(hIMC, dwIndex, CompBuffer, comp_len,
                                   ReadBuffer, read_len);

    HeapFree(GetProcessHeap(), 0, CompBuffer);
    HeapFree(GetProcessHeap(), 0, ReadBuffer);

    return rc;
}

/***********************************************************************
 *		ImmSetCompositionStringW (IMM32.@)
 */
BOOL WINAPI ImmSetCompositionStringW(
	HIMC hIMC, DWORD dwIndex,
	LPCVOID lpComp, DWORD dwCompLen,
	LPCVOID lpRead, DWORD dwReadLen)
{
     DWORD flags = 0;
     WCHAR wParam  = 0;

     TRACE("(%p, %ld, %p, %ld, %p, %ld): stub\n",
             hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);


     if (hIMC != (HIMC)FROM_IME)
         FIXME("PROBLEM: This only sets the wine level string\n");

     /*
      * Explanation:
      *  this sets the composition string in the imm32.dll level
      *  of the composition buffer. we cannot manipulate the xim level
      *  buffer, which means that once the xim level buffer changes again
      *  any call to this function from the application will be lost
      */

     if (lpRead && dwReadLen)
         FIXME("Reading string unimplemented\n");

     /*
      * app operating this api to also receive the message from xim
      */

    if (dwIndex == SCS_SETSTR)
    {
         flags = GCS_COMPSTR;

         if (root_context->dwCompStringLength)
             HeapFree(GetProcessHeap(),0,root_context->CompositionString);

         root_context->dwCompStringLength = dwCompLen;
         root_context->dwCompStringSize = dwCompLen;

         if (dwCompLen && lpComp)
         {
             root_context->CompositionString = HeapAlloc(GetProcessHeap(), 0,
                                                     dwCompLen);
             memcpy(root_context->CompositionString,lpComp,dwCompLen);

             wParam = ((const WCHAR*)lpComp)[0];
             flags |= GCS_COMPCLAUSE | GCS_COMPATTR;
         }
         else
             root_context->CompositionString = NULL;

    }

     UpdateDataInDefaultIMEWindow(hwndDefault);

     ImmInternalPostIMEMessage(WM_IME_COMPOSITION, wParam, flags);

     return TRUE;
}

/***********************************************************************
 *		ImmSetCompositionWindow (IMM32.@)
 */
BOOL WINAPI ImmSetCompositionWindow(
  HIMC hIMC, LPCOMPOSITIONFORM lpCompForm)
{
    BOOL reshow = FALSE;
    InputContextData *data = (InputContextData*)hIMC;

    TRACE("(%p, %p)\n", hIMC, lpCompForm);
    TRACE("\t%lx, (%li,%li), (%li,%li - %li,%li)\n",lpCompForm->dwStyle,
          lpCompForm->ptCurrentPos.x, lpCompForm->ptCurrentPos.y, lpCompForm->rcArea.top,
          lpCompForm->rcArea.left, lpCompForm->rcArea.bottom, lpCompForm->rcArea.right);

    if (!data)
        return FALSE;

    memcpy(&data->CompForm,lpCompForm,sizeof(COMPOSITIONFORM));

    if (IsWindowVisible(hwndDefault))
    {
        reshow = TRUE;
        ShowWindow(hwndDefault,SW_HIDE);
    }

    FIXME("STUB\n");

    if (reshow)
        ShowWindow(hwndDefault,SW_SHOWNOACTIVATE);

    SendMessageW(root_context->hwnd, WM_IME_NOTIFY,IMN_SETCOMPOSITIONWINDOW, 0);
    return TRUE;
}

/***********************************************************************
 *		ImmSetConversionStatus (IMM32.@)
 */
BOOL WINAPI ImmSetConversionStatus(
  HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence)
{
  FIXME("(%p, %ld, %ld): stub\n",
    hIMC, fdwConversion, fdwSentence
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmSetOpenStatus (IMM32.@)
 */
BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen)
{
    InputContextData *data = (InputContextData*)hIMC;
    FIXME("Semi-Stub\n");

    if (hIMC == (HIMC)FROM_IME)
    {
        if (fOpen)
            ImmInternalPostIMEMessage(WM_IME_STARTCOMPOSITION, 0, 0);

        ImmInternalSetOpenStatus(fOpen);

        if (!fOpen)
            ImmInternalPostIMEMessage(WM_IME_ENDCOMPOSITION, 0, 0);

        return TRUE;
    }

    if (!data)
        return FALSE;

    if (fOpen != data->bInternalState)
    {
        if (fOpen == FALSE && pX11DRV_ForceXIMReset)
            pX11DRV_ForceXIMReset(data->hwnd);

        if (fOpen == FALSE)
            ImmInternalPostIMEMessage(WM_IME_ENDCOMPOSITION,0,0);
        else
            ImmInternalPostIMEMessage(WM_IME_STARTCOMPOSITION,0,0);

        ImmInternalSetOpenStatus(fOpen);
        ImmInternalSetOpenStatus(!fOpen);

        if (data->bOpen == FALSE)
            ImmInternalPostIMEMessage(WM_IME_ENDCOMPOSITION,0,0);
        else
            ImmInternalPostIMEMessage(WM_IME_STARTCOMPOSITION,0,0);

        return FALSE;
    }
    return TRUE;
}

/***********************************************************************
 *		ImmSetStatusWindowPos (IMM32.@)
 */
BOOL WINAPI ImmSetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos)
{
  FIXME("(%p, %p): stub\n", hIMC, lpptPos);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmSimulateHotKey (IMM32.@)
 */
BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID)
{
  FIXME("(%p, %ld): stub\n", hWnd, dwHotKeyID);
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmUnregisterWordA (IMM32.@)
 */
BOOL WINAPI ImmUnregisterWordA(
  HKL hKL, LPCSTR lpszReading, DWORD dwStyle, LPCSTR lpszUnregister)
{
  FIXME("(%p, %s, %ld, %s): stub\n",
    hKL, debugstr_a(lpszReading), dwStyle, debugstr_a(lpszUnregister)
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}

/***********************************************************************
 *		ImmUnregisterWordW (IMM32.@)
 */
BOOL WINAPI ImmUnregisterWordW(
  HKL hKL, LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszUnregister)
{
  FIXME("(%p, %s, %ld, %s): stub\n",
    hKL, debugstr_w(lpszReading), dwStyle, debugstr_w(lpszUnregister)
  );
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  return FALSE;
}


/*****
 * Internal functions to help with IME window management
 */
static void PaintDefaultIMEWnd(HWND hwnd)
{
    PAINTSTRUCT ps;
    RECT rect;
    HDC hdc = BeginPaint(hwnd,&ps);
    GetClientRect(hwnd,&rect);

    if (root_context->dwCompStringLength && root_context->CompositionString)
    {
        SIZE size;
        POINT pt;
        HFONT oldfont = NULL;

        if (root_context->textfont)
            oldfont = SelectObject(hdc,root_context->textfont);

        TextOutW(hdc, 0,0,(LPWSTR)root_context->CompositionString,
                 root_context->dwCompStringLength / sizeof(WCHAR));

        GetTextExtentPoint32W(hdc, (LPWSTR)root_context->CompositionString,
                              root_context->dwCompStringLength / sizeof(WCHAR),
                              &size);
        pt.x = size.cx;
        pt.y = size.cy;
        LPtoDP(hdc,&pt,1);
        rect.left = pt.x;

        if (oldfont)
            SelectObject(hdc,oldfont);
    }
    FillRect(hdc,&rect, (HBRUSH) (COLOR_WINDOW+1));
    EndPaint(hwnd,&ps);
}

static void UpdateDataInDefaultIMEWindow(HWND hwnd)
{
    RedrawWindow(hwnd,NULL,NULL,RDW_ERASENOW|RDW_INVALIDATE);
}

/*
 * The window proc for the default IME window
 */
static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam,
                                          LPARAM lParam)
{
    LRESULT rc = 0;

    TRACE("Incoming Message 0x%x  (0x%08x, 0x%08x)\n", msg, (UINT)wParam,
           (UINT)lParam);

    switch(msg)
    {
        case WM_PAINT:
            PaintDefaultIMEWnd(hwnd);
            return FALSE;

        case WM_NCCREATE:
            return TRUE;

        case WM_CREATE:
            SetWindowTextA(hwnd,"Wine Ime Active");
            return TRUE;

        case WM_SETFOCUS:
            if (wParam)
                SetFocus((HWND)wParam);
            else
                FIXME("Received focus, should never have focus\n");
            break;
        case WM_IME_COMPOSITION:
            TRACE("IME message %s, 0x%x, 0x%x (%i)\n",
                    "WM_IME_COMPOSITION", (UINT)wParam, (UINT)lParam,
                     root_context->bRead);
            if ((lParam & GCS_RESULTSTR) && (!root_context->bRead))
                    IMM_PostResult(root_context);
            else
                 UpdateDataInDefaultIMEWindow(hwnd);
            break;
        case WM_IME_STARTCOMPOSITION:
            TRACE("IME message %s, 0x%x, 0x%x\n",
                    "WM_IME_STARTCOMPOSITION", (UINT)wParam, (UINT)lParam);
            root_context->hwnd = GetFocus();
            ShowWindow(hwndDefault,SW_SHOWNOACTIVATE);
            break;
        case WM_IME_ENDCOMPOSITION:
            TRACE("IME message %s, 0x%x, 0x%x\n",
                    "WM_IME_ENDCOMPOSITION", (UINT)wParam, (UINT)lParam);
            ShowWindow(hwndDefault,SW_HIDE);
            break;
        case WM_IME_SELECT:
            TRACE("IME message %s, 0x%x, 0x%x\n","WM_IME_SELECT",
                (UINT)wParam, (UINT)lParam);
            break;
        case WM_IME_CONTROL:
            TRACE("IME message %s, 0x%x, 0x%x\n","WM_IME_CONTROL",
                (UINT)wParam, (UINT)lParam);
            rc = 1; 
            break;
        case WM_IME_NOTIFY:
            TRACE("!! IME NOTIFY\n");
            break;
       default:
            TRACE("Non-standard message 0x%x\n",msg);
    }
    /* check the MSIME messages */
    if (msg == WM_MSIME_SERVICE)
    {
            TRACE("IME message %s, 0x%x, 0x%x\n","WM_MSIME_SERVICE",
                (UINT)wParam, (UINT)lParam);
            rc = FALSE;
    }
    else if (msg == WM_MSIME_RECONVERTOPTIONS)
    {
            TRACE("IME message %s, 0x%x, 0x%x\n","WM_MSIME_RECONVERTOPTIONS",
                (UINT)wParam, (UINT)lParam);
    }
    else if (msg == WM_MSIME_MOUSE)
    {
            TRACE("IME message %s, 0x%x, 0x%x\n","WM_MSIME_MOUSE",
                (UINT)wParam, (UINT)lParam);
    }
    else if (msg == WM_MSIME_RECONVERTREQUEST)
    {
            TRACE("IME message %s, 0x%x, 0x%x\n","WM_MSIME_RECONVERTREQUEST",
                (UINT)wParam, (UINT)lParam);
    }
    else if (msg == WM_MSIME_RECONVERT)
    {
            TRACE("IME message %s, 0x%x, 0x%x\n","WM_MSIME_RECONVERT",
                (UINT)wParam, (UINT)lParam);
    }
    else if (msg == WM_MSIME_QUERYPOSITION)
    {
            TRACE("IME message %s, 0x%x, 0x%x\n","WM_MSIME_QUERYPOSITION",
                (UINT)wParam, (UINT)lParam);
    }
    else if (msg == WM_MSIME_DOCUMENTFEED)
    {
            TRACE("IME message %s, 0x%x, 0x%x\n","WM_MSIME_DOCUMENTFEED",
                (UINT)wParam, (UINT)lParam);
    }
    /* DefWndProc if not an IME message */
    else if (!rc && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
                      (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP)))
        rc = DefWindowProcW(hwnd,msg,wParam,lParam);

    return rc;
}
