/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2014-2016. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */

#if defined(_WIN32)
#include <wx/msw/private.h> // for wxSetInstance
#endif

#include "wxe_impl.h"

// Until fixed in emulator
#ifndef _WIN32
extern "C" {
  extern void erts_thread_disable_fpe(void);
}
#endif

ErlDrvTid wxe_thread;

ErlDrvMutex *wxe_status_m;
ErlDrvCond  *wxe_status_c;

int wxe_status = WXE_NOT_INITIATED;

ErlDrvMutex * wxe_batch_locker_m;
ErlDrvCond  * wxe_batch_locker_c;
ErlDrvTermData  init_caller = 0;

#ifdef __DARWIN__
extern "C" {
  int erl_drv_stolen_main_thread_join(ErlDrvTid tid, void **respp);
  int erl_drv_steal_main_thread(char *name,
				ErlDrvTid *dtid,
				void* (*func)(void*),
				void* arg,
				ErlDrvThreadOpts *opts);
}
#endif

void *wxe_main_loop(void * );

/* ************************************************************
 *  START AND STOP of driver thread
 * ************************************************************/

int load_native_gui()
{
  return 1;
}

int start_native_gui(wxe_data *sd)
{
  int res;
  ErlDrvThreadOpts *opts = NULL;
  wxe_status_m = erl_drv_mutex_create((char *) "wxe_status_m");
  wxe_status_c = erl_drv_cond_create((char *)"wxe_status_c");

  wxe_batch_locker_m = erl_drv_mutex_create((char *)"wxe_batch_locker_m");
  wxe_batch_locker_c = erl_drv_cond_create((char *)"wxe_batch_locker_c");
  init_caller = driver_connected(sd->port_handle);

#ifdef __DARWIN__
  res = erl_drv_steal_main_thread((char *)"wxwidgets",
				  &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
#else
  opts = erl_drv_thread_opts_create((char *)"wx thread");
  opts->suggested_stack_size = 8192;
  res = erl_drv_thread_create((char *)"wxwidgets",
			      &wxe_thread,wxe_main_loop,(void *) sd->pdl,opts);
  erl_drv_thread_opts_destroy(opts);
#endif
  if(res == 0) {
    erl_drv_mutex_lock(wxe_status_m);
    for(;wxe_status == WXE_NOT_INITIATED;) {
      erl_drv_cond_wait(wxe_status_c, wxe_status_m);
    }
    erl_drv_mutex_unlock(wxe_status_m);
    return wxe_status;
  } else {
    wxString msg;
    msg.Printf(wxT("Erlang failed to create wxe-thread %d\r\n"), res);
    send_msg("error", &msg);
    return -1;
  }
}

void stop_native_gui(wxe_data *sd)
{
  if(wxe_status == WXE_INITIATED) {
    meta_command(WXE_SHUTDOWN, sd);
  }
#ifdef __DARWIN__
  erl_drv_stolen_main_thread_join(wxe_thread, NULL);
#else
  erl_drv_thread_join(wxe_thread, NULL);
#endif
  erl_drv_mutex_destroy(wxe_status_m);
  erl_drv_cond_destroy(wxe_status_c);
  erl_drv_mutex_destroy(wxe_batch_locker_m);
  erl_drv_cond_destroy(wxe_batch_locker_c);
}

void unload_native_gui()
{

}

/* ************************************************************
 *  wxWidgets Thread
 * ************************************************************/

void *wxe_main_loop(void *vpdl)
{
  int result;
  int  argc = 1;
  const wxChar temp[10] = L"Erlang";
  wxChar * argv[] = {(wxChar *)temp, NULL};
  ErlDrvPDL pdl = (ErlDrvPDL) vpdl;

  driver_pdl_inc_refc(pdl);

  // Disable floating point execption if they are on.
  // This should be done in emulator but it's not in yet.
#ifndef _WIN32
  erts_thread_disable_fpe();
#else
  // Setup that wxWidgets should look for cursors and icons in
  // this dll and not in werl.exe (which is the default)
  HMODULE WXEHandle = GetModuleHandle(_T("wxe_driver"));
  wxSetInstance((HINSTANCE) WXEHandle);
#endif

  wxe_ps_init();
  result = wxEntry(argc, argv);
  // fprintf(stderr, "WXWidgets quits main loop %d \r\n", result);
  if(result >= 0 && wxe_status == WXE_INITIATED) {
    /* We are done try to make a clean exit */
    wxe_status = WXE_EXITED;
    driver_pdl_dec_refc(pdl);
#ifndef __DARWIN__
    erl_drv_thread_exit(NULL);
#endif
    return NULL;
  } else {
    erl_drv_mutex_lock(wxe_status_m);
    wxe_status = WXE_ERROR;
    erl_drv_cond_signal(wxe_status_c);
    erl_drv_mutex_unlock(wxe_status_m);
    driver_pdl_dec_refc(pdl);
    return NULL;
  }
}
