/* GDK - The GIMP Drawing Kit
 * Copyright (C) 2019 Руслан Ижбулатов <lrn1986@gmail.com>
 *
 * 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 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, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#define COBJMACROS
#include <msctf.h>

#include <gdk/gdk.h>

#include "gdkprivate-win32.h"

struct _GdkWin32ALPNSink
{
  ITfActiveLanguageProfileNotifySink itf_alpn_sink;

  gint ref_count;
};

typedef struct _GdkWin32ALPNSink GdkWin32ALPNSink;

static GdkWin32ALPNSink *actlangchangenotify = NULL;
static ITfSource *itf_source = NULL;
static DWORD actlangchangenotify_id = 0;

static ULONG STDMETHODCALLTYPE
alpn_sink_addref (ITfActiveLanguageProfileNotifySink *This)
{
  GdkWin32ALPNSink *alpn_sink = (GdkWin32ALPNSink *) This;
  int ref_count = ++alpn_sink->ref_count;

  return ref_count;
}

static HRESULT STDMETHODCALLTYPE
alpn_sink_queryinterface (ITfActiveLanguageProfileNotifySink *This,
                          REFIID                              riid,
                          LPVOID                             *ppvObject)
{
  *ppvObject = NULL;

  if (IsEqualGUID (riid, &IID_IUnknown))
    {
      ITfActiveLanguageProfileNotifySink_AddRef (This);
      *ppvObject = This;
      return S_OK;
    }

  if (IsEqualGUID (riid, &IID_ITfActiveLanguageProfileNotifySink))
    {
      ITfActiveLanguageProfileNotifySink_AddRef (This);
      *ppvObject = This;
      return S_OK;
    }

  return E_NOINTERFACE;
}

static ULONG STDMETHODCALLTYPE
alpn_sink_release (ITfActiveLanguageProfileNotifySink *This)
{
  GdkWin32ALPNSink *alpn_sink = (GdkWin32ALPNSink *) This;
  int ref_count = --alpn_sink->ref_count;

  if (ref_count == 0)
    {
      g_free (This);
    }

  return ref_count;
}

static HRESULT STDMETHODCALLTYPE
alpn_sink_on_activated (ITfActiveLanguageProfileNotifySink *This,
                        REFCLSID                            clsid,
                        REFGUID                             guidProfile,
                        BOOL                                fActivated)
{
  _gdk_input_locale_is_ime = fActivated;
  return S_OK;
}

static ITfActiveLanguageProfileNotifySinkVtbl alpn_sink_vtbl = {
  alpn_sink_queryinterface,
  alpn_sink_addref,
  alpn_sink_release,
  alpn_sink_on_activated,
};

static GdkWin32ALPNSink *
alpn_sink_new ()
{
  GdkWin32ALPNSink *result;

  result = g_new0 (GdkWin32ALPNSink, 1);
  result->itf_alpn_sink.lpVtbl = &alpn_sink_vtbl;
  result->ref_count = 0;

  ITfActiveLanguageProfileNotifySink_AddRef (&result->itf_alpn_sink);

  return result;
}


void
_gdk_win32_lang_notification_init ()
{
  HRESULT hr;
  ITfThreadMgr *itf_threadmgr;

  CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);

  if (actlangchangenotify != NULL)
    return;

  hr = CoCreateInstance (&CLSID_TF_ThreadMgr,
                         NULL,
                         CLSCTX_INPROC_SERVER,
                         &IID_ITfThreadMgr,
                         (LPVOID *) &itf_threadmgr);

  if (!SUCCEEDED (hr))
    return;

  hr = ITfThreadMgr_QueryInterface (itf_threadmgr, &IID_ITfSource, (VOID **) &itf_source);
  ITfThreadMgr_Release (itf_threadmgr);

  if (!SUCCEEDED (hr))
    return;

  actlangchangenotify = alpn_sink_new ();

  hr = ITfSource_AdviseSink (itf_source,
                             &IID_ITfActiveLanguageProfileNotifySink,
                             (IUnknown *) actlangchangenotify,
                             &actlangchangenotify_id);

  if (!SUCCEEDED (hr))
    {
      ITfActiveLanguageProfileNotifySink_Release (&actlangchangenotify->itf_alpn_sink);
      actlangchangenotify = NULL;
      ITfSource_Release (itf_source);
      itf_source = NULL;
    }
}

void
_gdk_win32_lang_notification_exit ()
{
  if (actlangchangenotify != NULL && itf_source != NULL)
    {
      ITfSource_UnadviseSink (itf_source, actlangchangenotify_id);
      ITfSource_Release (itf_source);
      ITfActiveLanguageProfileNotifySink_Release (&actlangchangenotify->itf_alpn_sink);
    }

  CoUninitialize ();
}
