# NativeAPIEmulation.java

    /*
    ** Module   : NativeAPIEmulation.java
    ** Abstract : Native API emulation.
    **
    ** Copyright (c) 2018-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --------------------------------------Description----------------------------------------
    ** 001 HC  20181211 Created initial version
    ** 002 HC  20190127 Added more emulated Windows API functions.
    **     HC  20190206 Added limited emulation of getLocaleInfoA.
    **     OM  20190302 Fixed getParent().
    **     HC  20190302 Implemented emulation of GetWindowRect and GetClientRect Windows API
    **                  functions.
    **     HC  20190305 Implemented emulation of GetCursorPos and ScreenToClient Windows API
    **                  functions.
    **     HC  20190306 Implemented Windows API emulation for functions ShowScrollBar and
    **                  GetActiveWindow and limited emulation for GetSystemMetrics function.
    ** 003 EVL 20190629 Adding getUserNameA, LockWindowUpdate, IsWindow implementations.
    ** 004 EVL 20190708 Adding wsock32.dll native calls to get the computer name/IP.  Fix for
    **                  user32.LockWindowUpdate() unlocking issue.  Adding user32 call to
    **                  DisableProcessWindowsGhosting, stubbing.  Fixed native API names camel case
    **                  issues that prevent the methods to be properly integrated.  Added new paameter
    **                  to process for getLocaleInfoA(), changing error handling approach.
    **     HC  20190806 Implemented SetParent Windows native function and the system-level widget
    **                  hierarchy.
    ** 005 HC  20191023 Improved emulation of getSystemMetrics, getWindowRect and getClientRect Win32
    **                  functions.
    ** 006 IAS 20191140 Added ClientToScreen Windows native function emulation.
    **     HC  20191204 Improved emulation of setFocus Win32 system function. Plus related changes to
    **                  focus handling.
    **     HC  20191205 Fixed ShowScrollBar not showing/hiding scroll bars.
    ** 007 RFB 20200210 Add sleep emulation. Note that there may be a todo to handle the
    **                  InterruptedException.
    **     RFB 20200219 Added getComputerNameA emulation. Some questions arise with respect to what
    **                  name should be returned. We are returning LogicalTerminal.getHostName()
    ** 008 EVL 20200206 Fixed locked window issue whan we need to handle several locked windows.
    **     EVL 20200219 Adding getForegroundWindow() native API implementation for windows.user32.
    **                  Changed setFocus() implementation to force enclosing window activation.
    ** 009 RFB 20200304 Fixed some minor Javadoc warnings in sleep() method.
    ** 010 EVL 20200312 Changed getForegroundWindow() implementation.
    **     HC  20200331 Implemented support for OPEN-POPUP legacy method, which opens popup menu set
    **                  on the widget.
    **     EVL 20200409 Improved getForegroundWindow() considering getCurrentWindow() as fallback.
    **                  Adding implementation for emulated user32.IsWindowVisible() native API call.
    **                  Changing getAciveWindow() for several caseswhen the system is in migration
    **                  state from one active window to another.
    ** 011 GES 20200628 Added kernel32.GetCurrentProcessId().
    ** 012 EVL 20200630 Fixed method name issue for getCurrentProcessId().
    ** 013 RFB 20200724 Added getSysColor for finding the RGB from the theme color palatte.
    **     EVL 20210622 Fixed issue in client to screen coordinates transformation.
    **     EVL 20210701 Added new emulated call for user32.dll, WindowMove() function.  Fix for Javadoc typo.
    **     EVL 20210702 Added TODO explanation for inefficent code usage.
    **     EVL 20210705 Considering 0 or less windws width or height as implicit hiding command for WindowMove.
    **     VVT 20211203 Fixed: getAsyncKeyState: must return its value in bit 16. See #5850.
    **     VVT 20211203 Fixed: getAsyncKeyState: a comment added describing unimplemented features. See #5850.
    **     VVT 20211206 'state' replaced by 'modifiers' in code related to key modifiers. See #5850-26.
    **     VVT 20220318 user32.getKeyboardState() function added. See #6178-15.
    **     VVT 20220401 windows.user32.getKeyboardState() implementation fixed based on the
    **                  KeyReader.getKeyboardState().
    **     HC  20220928 Improved user32.SetParent to ignore parent assignment when parent and child equal.
    **     EVL 20221214 Adding implementation for set/get/loadCursor native functions.  Improved usage of the
    **                  LogicalTerminal instance from static context.
    **     EVL 20221215 Notes resolution, javadoc fixes for cursor related methods.
    **     EVL 20230208 Implementing GetScrollPos and SendMessageA native calls to simulate native based container
    **                  content scrolling.  Notes resolution.
    ** 014 EVL 20230216 Need to find closest frame when getting parent for widget with native like getParent()
    **                  usage.
    **     HC 20230221  Extended GetParent to allow window:hwnd as the argument.
    ** 015 HC  20230322 Improved user32.getParent to account for the non-client portion of window
    **                  widget.
    ** 016 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
    ** 017 EVL 20230711 Improved win32 SetFocus implementation.  The enclosing window is not activating if it is
    **                  already active.  Fixed the win32 SetParent implementation, for cases when child is
    **                  a WindowWidget.
    ** 018 SVL 20230803 Added postMessageA function.
    ** 019 AI  20231009 Added sendMessageA and setCursorPos.
    **     AI  20231010 Changed sendMessageA and postMessageA to call for applyMessageA.
    **     AI  20231012 Updated setCursorPos to emit a partial-implementation warning, as it can not physically
    **                  change the cursor position, only sets a temporal variable with the new cursor position.
    ** 020 EVL 20240626 Added GetKeyState() user32 emulation.  Implementation the same as for GetAsyncKeyState().
    ** 021 GBB 20240826 Moving LowLevelBuffer to osresource package.
    ** 022 CA  20241030 Added immutable character constant support.
    ** 023 SB  20250129 Modified the cursors list to be accessible.
    */

    /*
     ** This program is free software: you can redistribute it and/or modify
     ** it under the terms of the GNU Affero General Public License as
     ** published by the Free Software Foundation, either version 3 of the
     ** License, or (at your option) any later version.
     **
     ** This program 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 Affero General Public License for more details.
     **
     ** You may find a copy of the GNU Affero GPL version 3 at the following
     ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
     **
     ** Additional terms under GNU Affero GPL version 3 section 7:
     **
     **   Under Section 7 of the GNU Affero GPL version 3, the following additional
     **   terms apply to the works covered under the License.  These additional terms
     **   are non-permissive additional terms allowed under Section 7 of the GNU
     **   Affero GPL version 3 and may not be removed by you.
     **
     **   0. Attribution Requirement.
     **
     **     You must preserve all legal notices or author attributions in the covered
     **     work or Appropriate Legal Notices displayed by works containing the covered
     **     work.  You may not remove from the covered work any author or developer
     **     credit already included within the covered work.
     **
     **   1. No License To Use Trademarks.
     **
     **     This license does not grant any license or rights to use the trademarks
     **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
     **     of Golden Code Development Corporation. You are not authorized to use the
     **     name Golden Code, FWD, or the names of any author or contributor, for
     **     publicity purposes without written authorization.
     **
     **   2. No Misrepresentation of Affiliation.
     **
     **     You may not represent yourself as Golden Code Development Corporation or FWD.
     **
     **     You may not represent yourself for publicity purposes as associated with
     **     Golden Code Development Corporation, FWD, or any author or contributor to
     **     the covered work, without written authorization.
     **
     **   3. No Misrepresentation of Source or Origin.
     **
     **     You may not represent the covered work as solely your work.  All modified
     **     versions of the covered work must be marked in a reasonable way to make it
     **     clear that the modified work is not originating from Golden Code Development
     **     Corporation or FWD.  All modified versions must contain the notices of
     **     attribution required in this license.
     */

    package com.goldencode.p2j.util;

    import java.awt.event.*;
    import java.io.*;
    import java.lang.reflect.*;
    import java.nio.*;
    import java.util.*;

    import com.goldencode.p2j.library.*;
    import com.goldencode.p2j.main.*;
    import com.goldencode.p2j.ui.*;
    import com.goldencode.p2j.ui.client.*;
    import com.goldencode.p2j.ui.client.driver.*;
    import com.goldencode.p2j.util.logging.*;
    import com.goldencode.p2j.util.osresource.*;
    import com.goldencode.util.*;

    /**
     * The class emulates native API calls.
     */
    public class NativeAPIEmulation
    {
       /**
        * Logger.
        */
       private static CentralLogger LOG = CentralLogger.get(NativeAPIEmulation.class.getSimpleName());

       /**
        * The method returns {@code true} if the supplied os name, library and function are supported by the
        * class. In other words the class must declare function {@code func} in the declared classes
        * {@code os.lib}.
        *
        * @param   os
        *          The target operating system name.
        * @param   lib
        *          The target library name. May contain the file name extension.
        * @param   func
        *          The target function name.
        *
        * @return  {@code true} if the target function is supported, {@code false} otherwise.
        */
       public static boolean emulates(String os, String lib, String func)
       {
          os = removeStringLiteralOptions(os.toLowerCase().trim());

          Class osClass = null;
          for (Class clazz : NativeAPIEmulation.class.getDeclaredClasses())
          {
             if (clazz.getSimpleName().equals(os))
             {
                osClass = clazz;
             }
          }

          if (osClass == null)
          {
             return false;
          }

          lib = removeStringLiteralOptions(lib.toLowerCase().trim());
          lib = lib.replaceFirst("\\.(dll|exe|so|lib)$", "");

          Class libClass = null;
          for (Class clazz : osClass.getDeclaredClasses())
          {
             if (clazz.getSimpleName().equals(lib))
             {
                libClass = clazz;
             }
          }

          if (libClass == null)
          {
             return false;
          }

          // as function name, try the original and then first letter lower case
          func = removeStringLiteralOptions(func.trim());
          String[] funcs = new String[2];
          funcs[0] = func;
          funcs[1] = Character.toLowerCase(func.charAt(0)) + func.substring(1);

          for (Method m : libClass.getDeclaredMethods())
          {
             for (String f : funcs)
             {
                if (m.getName().equals(f))
                {
                   return true;
                }
             }
          }

          return false;
       }

       /**
        * Declares windows API symbols and supported libraries.
        */
       public static class windows
       {
          /** NULL constant used in the Windows API. */
          public static final int NULL = 0;

          /** TRUE constant */
          public static final int TRUE = 1;

          /** FALSE constant */
          public static final int FALSE = 0;

          /** win32 compatible constatnt for horizontal scrollbar. */
          public static final int SB_HORZ = 0;

          /** win32 compatible constatnt for vertical scrollbar. */
          public static final int SB_VERT = 1;

          /** win32 compatible constatnt for scrolling messages. */
          public static final int SB_THUMBPOSITION = 4;

          /** win32 compatible constatnt for horizontal scrolling message. */
          public static final int WM_HSCROLL = 276;

          /** win32 compatible constatnt for vertical scrolling message. */
          public static final int WM_VSCROLL = 277;

          /** Mask to extract lo word value from integer. */
          public static final int LO_WORD_MASK = 0x0000FFFF;

          /** Mask to extract hi word value from integer. */
          public static final int HI_WORD_MASK = 0xFFFF0000;

          /** win32 compatible constatnt for arrow cursor. */
          public static int IDC_ARROW       = 32512;

          /** win32 compatible constatnt for hourglass cursor. */
          public static int IDC_WAIT        = 32514;

          /** win32 compatible constatnt for small arrow and hourglass cursor. */
          public static int IDC_APPSTARTING = 32650;

          /** win32 compatible constatnt for crosshair cursor. */
          public static int IDC_CROSS       = 32515;

          /** win32 compatible constatnt for hand cursor. */
          public static int IDC_HAND        = 32649;

          /** win32 compatible constatnt for I-beam cursor. */
          public static int IDC_IBEAM       = 32513;

          /** win32 compatible constatnt for crossed circle cursor. */
          public static int IDC_NO          = 32648;

          /** win32 compatible constatnt for sizing cursor. */
          public static int IDC_SIZE        = 32640;

          /** win32 compatible constatnt for up arrow cursor. */
          public static int IDC_UPARROW     = 32516;

          /** win32 compatible constatnt for question mark cursor. */
          public static int IDC_HELP        = 32651;

          /** Windows cursors maping table. */
          public final static Map<integer, MousePointer> CURSORS;

          /** Init predefined mouse pointers. */
          static
          {
             HashMap<integer, MousePointer> cursors = new HashMap<integer, MousePointer>(16);
             cursors.put(new integer(IDC_ARROW), MousePointer.ARROW); // regular arrow
             cursors.put(new integer(IDC_WAIT), MousePointer.WAIT);   // wait or hourglass
             cursors.put(new integer(IDC_APPSTARTING), MousePointer.APPSTARTING);   // small arrow and hourglass
             cursors.put(new integer(IDC_CROSS), MousePointer.CROSS);   // crosshair
             cursors.put(new integer(IDC_HAND), MousePointer.GLOVE);   // hand
             cursors.put(new integer(IDC_IBEAM), MousePointer.IBEAM);   // I-beam
             cursors.put(new integer(IDC_NO), MousePointer.NO);   // crossed circle
             cursors.put(new integer(IDC_SIZE), MousePointer.SIZE);   // sizing
             cursors.put(new integer(IDC_UPARROW), MousePointer.UPARROW);   // vertical arrow
             cursors.put(new integer(IDC_HELP), MousePointer.HELP);   // question mark
             CURSORS = Collections.unmodifiableMap(cursors);
          }

          /**
           * Declares supported user32.dll functions.
           */
          public static class user32
          {
             /** The currently locked window. */
             private static GenericWidget wndLocked = null;

             /**
              * Implementation of GetKeyboardState call.
              *
              * PARTLY IMPLEMENTED: only several key codes are supported.
              *
              * @param state
              *        the 256-byte byte array
              */
             public static void getKeyboardState(final memptr state)
             {
                LogicalTerminal.getInstance();
                final int[] keyboardState = LogicalTerminal.getKeyboardState();
                if (keyboardState != null)
                {
                   for (int keyCode : keyboardState)
                   {
                      switch (keyCode)
                      {
                         case KeyEvent.VK_LEFT:
                            state.setLong(0x80, 38);
                            break;
                         case KeyEvent.VK_UP:
                            state.setLong(0x8000, 38);
                            break;
                         case KeyEvent.VK_RIGHT:
                            state.setLong(0x800000, 38);
                            break;
                         case KeyEvent.VK_DOWN:
                            state.setLong(0x80000000, 38);
                            break;
                         default:
                            // TODO implement other keycodes if and when required
                      }
                   }
                }
             }

             /**
              * Returns keyboard state. Currently only Windows virtual keys VK_ALT, VK_CONTROL and VK_SHIFT are
              * supported.
              *
              * The value returned is a 16-bit integer with the bit 16 or
              * bit 0 set if the virtual key was pressed.
              *
              * Otherwise, 0 is returned.
              *
              * UNIMPLEMENTED:
              *
              * The call should not be idempotent: if this method was
              * called the first time after the key was pressed, the key
              * state is returned in the bit 16, otherwise the key state
              * is returned in the bit 0. Until this feature is
              * implemented, the bit 16 is always used to return the key
              * state.
              *
              * @param   winVKey
              *          Windows virtual key. Must be one of WindowsKey.VK_ALT, WindowsKey.VK_CONTROL
              *          or WindowsKey.VK_SHIFT.
              *
              * @return  see above.
              */
             public static integer getKeyState(integer winVKey)
             {
                if (winVKey == null || winVKey.isUnknown())
                {
                   return new integer(NULL);
                }

                final int modifiers = KeyReader.getLastKeyModifiers();

                final boolean result;
                switch (winVKey.intValue())
                {
                   case WindowsKey.VK_ALT:
                      result = KeyCode.isAlt(modifiers);
                      break;
                   case WindowsKey.VK_CONTROL:
                      result = KeyCode.isCtrl(modifiers);
                      break;
                   case WindowsKey.VK_SHIFT:
                      result = KeyCode.isShift(modifiers);
                      break;
                   default:
                      LOG.severe("Invalid winKey: " + winVKey);
                      result = false;
                }

                return new integer(result ? 32768 : 0);
             }

             /**
              * Returns keyboard state. Currently only Windows virtual keys VK_ALT, VK_CONTROL and VK_SHIFT are
              * supported.
              *
              * Note that the method doesn't behave truly asynchronously, it reads the key state from the last
              * key event.
              *
              * The value returned is a 16-bit integer with the bit 16 or
              * bit 0 set if the virtual key was pressed.
              *
              * Otherwise, 0 is returned.
              *
              * UNIMPLEMENTED:
              *
              * The call should not be idempotent: if this method was
              * called the first time after the key was pressed, the key
              * state is returned in the bit 16, otherwise the key state
              * is returned in the bit 0. Until this feature is
              * implemented, the bit 16 is always used to return the key
              * state.
              *
              * @param   winVKey
              *          Windows virtual key. Must be one of WindowsKey.VK_ALT, WindowsKey.VK_CONTROL
              *          or WindowsKey.VK_SHIFT.
              *
              * @return  see above.
              */
             public static integer getAsyncKeyState(integer winVKey)
             {
                return getKeyState(winVKey);
             }

             /**
              * Returns the id of the widget in focus.
              *
              * @return  see above.
              */
             public static integer getFocus()
             {
                handle h = LogicalTerminal.focus();
                if (h.isUnknown())
                {
                   return new integer(NULL);
                }

                GenericWidget w = (GenericWidget) h.getResource();
                return w.getHWND();
             }

             /**
              * Emulates <code>PostMessageA</code> Win32 function by posting an event to the inner FWD OS event
              * queue.
              *
              * @param    hWnd
              *           Integer handle value of the window containing the widget (same as widget ID).
              * @param    msg
              *           Message to be posted.
              * @param    wParam
              *           Additional message-specific information.
              * @param    lParam
              *           Additional message-specific information.
              */
             public static void postMessageA(integer hWnd, integer msg, integer wParam, integer lParam)
             {
                ClientExports ce = LogicalTerminal.getClient();
                if (hWnd.isUnknown() || msg.isUnknown() || wParam.isUnknown() || lParam.isUnknown())
                {
                   NativeInvoker.genInputUnknownErr(true);
                   return;
                }

                ce.applyMessageA(hWnd.intValue(), msg.intValue(), wParam.intValue(), lParam.intValue(), false);
             }

             /**
              * Sets focus to the requested widget hwnd. The method interprets the hwnd value as widget id.
              *
              * @param   hwnd
              *          Widget id.
              */
             public static void setFocus(integer hwnd)
             {
                if (hwnd == null || hwnd.isUnknown())
                {
                   return;
                }

                int id = hwnd.intValue();
                WindowWidget wnd = LogicalTerminal.getWindowByNonClientId(id);
                if (wnd != null)
                {
                   id = wnd.getId();
                }

                // try to identify if the requested widget ID is already in active window
                // activate by default
                boolean forceActivation = true;
                GenericWidget<?> widget = LogicalTerminal.getWidgetForId(id);
                if (widget != null)
                {
                   handle hTarget = widget.getWindow();
                   if (hTarget != null && !hTarget.isUnknown())
                   {
                      CommonWindow winTarget = hTarget.unwrapWindow();
                      handle hActive = LogicalTerminal.activeWindow();
                      if (hActive != null && !hActive.isUnknown())
                      {
                         CommonWindow winActive = hActive.unwrapWindow();
                         // do not activate target window if it is already active
                         // in this case we should preserve existed Z-order of the windows
                         forceActivation = winTarget != winActive;
                      }
                   }
                   else
                   {
                      forceActivation = false;
                   }
                }
                else
                {
                   // nothing to be focused
                   return;
                }

                ClientExports ce = LogicalTerminal.getClient();
                ce.setFocus(id, forceActivation);
             }

             /**
              * Returns parent widget id of the supplied widget native handle. The method interprets
              * the hwnd value as widget id.
              *
              * @param   hwnd
              *          Widget id.
              * @return  see above.
              */
             public static integer getParent(integer hwnd)
             {
                if (hwnd == null || hwnd.isUnknown())
                {
                   return new integer(NULL);
                }

                GenericWidget w = LogicalTerminal.getWidgetForId(hwnd.intValue());
                if (w == null)
                {
                   return new integer(NULL);
                }

                if (w instanceof WindowWidget)
                {
                   // this is for the case when window:hwnd is passed to GetParent;
                   // window:hwnd will represent the Win32 client window of the legacy top-level legacy window
                   // and the result of GetParent will be the actual top-level window, i.e. the Win32 non-client
                   // window of the top-level legacy window;
                   return new integer(((WindowConfig) w.config()).nonClientWindowId);
                }

                CommonWidget parent = w.getParent();

                // for field group we need to find closest parent frame
                if (parent instanceof FieldGroup)
                {
                   parent = parent.getParent();
                }

                return parent == null ? hwnd : parent.getHWND();
             }

             /**
              * Changes the parent window of the specified child window.
              *
              * @param   hWndChild
              *          A handle to the child window.
              * @param   hWndNewParent
              *          A handle to the new parent window.
              *
              * @return  If the function succeeds, the return value is a handle to the previous parent window.
              *          If the function fails, the return value is NULL.
              */
             public static integer setParent(integer hWndChild, integer hWndNewParent)
             {
                if (!BaseDataType.isAllKnown(hWndChild, hWndNewParent))
                {
                   return new integer(NULL);
                }

                // user32 SetParent seems to always ignore same handle values
                if (hWndChild.longValue() == hWndNewParent.longValue())
                {
                   return new integer(NULL);
                }

                GenericWidget parent = LogicalTerminal.getWidgetForId(hWndNewParent.intValue());
                if (!(parent instanceof FrameWidget))
                {
                   UnimplementedFeature.partial("Only frame parents are supported for SetParent");
                   return new integer(NULL);
                }

                int childId = hWndChild.intValue();
                GenericWidget child = LogicalTerminal.getWidgetForId(childId);

                // try to search in non-client window registry next if child is a window widget
                if (child == null)
                {
                   child = LogicalTerminal.getWindowByNonClientId(childId);
                }

                if (child == null || !(child instanceof BaseEntity))
                {
                   return new integer(NULL);
                }

                ((BaseEntity) child).setSystemParent(parent);

                GenericFrame oldParent = child.getFrame();
                return oldParent != null ? new integer(oldParent.getFrameWidget().getId()) : new integer(NULL);
             }

             /**
              * Loads the specified cursor resource.
              *
              * @param   instance
              *          A handle to an instance of the module whose executable file contains the cursor to be
              *          loaded.
              *
              * @param   cursorName
              *          The name of the cursor resource to be loaded. Alternatively, this parameter can
              *          consist of the resource identifier in the low-order word and zero in the high-order word.
              *
              * @return  If the function succeeds, the return value is the handle to the newly loaded cursor. If
              *          the function fails, the return value is NULL. To get extended error information, call
              *          GetLastError.
              */
             public static integer loadCursorA(integer instance, integer cursorName)
             {
                return cursorName != null && !cursorName.isUnknown() &&
                       CURSORS.containsKey(cursorName) ? cursorName : new integer(NULL);
             }

             /**
              * Retrieves a handle to the current cursor.
              *
              * @return  The return value is the handle to the current cursor. If there is no cursor, the
              *          return value is NULL.
              */
             public static integer getCursor()
             {
                return LogicalTerminal.getCursor();
             }

             /**
              * Sets the cursor shape.
              *
              * @param   cursor
              *          A handle to the cursor. The cursor must have been created by the CreateCursor function
              *          or loaded by the LoadCursor or LoadImage function. If this parameter is NULL, the
              *          cursor is removed from the screen.
              *
              * @return  The return value is the handle to the previous cursor, if there was one. If there was
              *          no previous cursor, the return value is NULL.
              */
             public static integer setCursor(integer cursor)
             {
                // save previous cursor
                integer prevCursor = new integer(getCursor().intValue());
                // for valid incoming cursor change the shape
                if (cursor != null && !cursor.isUnknown())
                {
                   // call the client side to change the cursor
                   LogicalTerminal.setCursor(cursor, CURSORS.get(cursor));
                }

                return prevCursor;
             }

             /**
              * Retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are
              * given in screen coordinates that are relative to the upper-left corner of the screen.
              *
              * @param    hwnd
              *           A handle to the window.
              * @param    lprect
              *           A pointer to a RECT structure that receives the screen coordinates of the upper-left
              *           and lower-right corners of the window.
              *
              * @return   If the function succeeds, the return value is nonzero. If the function fails, the
              *           return value is zero.
              */
             public static integer getWindowRect(integer hwnd, int64 lprect)
             {
                if (!BaseDataType.isAllKnown(hwnd, lprect))
                {
                   return new integer(0);
                }

                GenericWidget w = getWidget(hwnd.intValue());
                if (w == null)
                {
                   return new integer(0);
                }

                int x;
                int y;
                int width;
                int height;

                if (w instanceof FrameWidget && ((FrameConfig) w.config()).dialog)
                {
                   FrameConfig fc = (FrameConfig) w.config();
                   x = fc.dialogX;
                   y = fc.dialogY;
                   width = w.getWidthPixels().intValue();
                   height = w.getHeightPixels().intValue();
                }
                else
                {
                   handle wnd = w.getWindow();
                   if (!BaseDataType.isAllKnown(wnd))
                   {
                      return new integer(0);
                   }

                   WindowWidget window = (WindowWidget) wnd.getResource();
                   x = window.getX().intValue();
                   y = window.getY().intValue();
                   width = window.getWindowWidthPixels().intValue();
                   height = window.getWindowHeightPixels().intValue();
                }

                LowLevelBuffer mem = memptr.getMemoryManager();
                byte[] data = mem.read(lprect.longValue(), 0, 16);
                if (data == null)
                {
                   return new integer(0);
                }

                IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
                buf.put(0, x);
                buf.put(1, y);
                buf.put(2, x + width);
                buf.put(3, y + height);
                mem.write(lprect.longValue(), 0, data);

                return new integer(1);
             }

             /**
              * Retrieves the coordinates of a window's client area. The client coordinates specify the
              * upper-left and lower-right corners of the client area. Because client coordinates are relative
              * to the upper-left corner of a window's client area, the coordinates of the upper-left corner
              * are (0,0).
              *
              * @param    hwnd
              *           A handle to the window.
              * @param    lprect
              *           A pointer to a RECT structure that receives the screen coordinates of the upper-left
              *           and lower-right corners of the window.
              *
              * @return   If the function succeeds, the return value is nonzero. If the function fails, the
              *           return value is zero.
              */
             public static integer getClientRect(integer hwnd, int64 lprect)
             {
                if (!BaseDataType.isAllKnown(hwnd, lprect))
                {
                   return new integer(0);
                }

                GenericWidget w = getWidget(hwnd.intValue());
                if (w == null)
                {
                   return new integer(0);
                }

                LowLevelBuffer mem = memptr.getMemoryManager();
                byte[] data = mem.read(lprect.longValue(), 0, 16);
                if (data == null)
                {
                   return new integer(0);
                }

                int width;
                int height;

                if (w instanceof FrameWidget && ((FrameConfig) w.config()).dialog)
                {
                   width = w.getWidthPixels().intValue();
                   height = w.getHeightPixels().intValue();
                }
                else
                {
                   handle wnd = w.getWindow();
                   if (!BaseDataType.isAllKnown(wnd))
                   {
                      return new integer(0);
                   }

                   WindowWidget window = (WindowWidget) wnd.getResource();
                   width = window.getWidthPixels().intValue();
                   height = window.getHeightPixels().intValue();
                }

                IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
                buf.put(0, 0);
                buf.put(1, 0);
                buf.put(2, width);
                buf.put(3, height);
                mem.write(lprect.longValue(), 0, data);

                return new integer(1);
             }

             /**
              * Implements a substitute functionality for Win32 SetCursorPos. Sets a temporal variable for the
              * new mouse position coordinates, as it can not physically move the cursor, position which is
              * later invalidated.
              *
              * @param   xPos
              *          X coordinate.
              * @param   yPos
              *          Y coordinate.
              *
              * @return  Returns nonzero if successful or zero otherwise.
              */
             public static integer setCursorPos(integer xPos, integer yPos)
             {
                UnimplementedFeature.partial("setCursorPos is not implemented to physically move the cursor.");
                LogicalTerminal.getClient().setCursorPos(new NativePoint(xPos.intValue(), yPos.intValue()));
                return new integer(1);
             }

             /**
              * Retrieves the position of the mouse cursor, in screen coordinates.
              *
              * @param   lpPoint
              *          A pointer to a POINT structure that receives the screen coordinates of the cursor.
              *
              * @return  Returns nonzero if successful or zero otherwise.
              */
             public static integer getCursorPos(memptr lpPoint)
             {
                return getCursorPos(lpPoint.getPointerValue());
             }

             /**
              * Retrieves the position of the mouse cursor, in screen coordinates.
              *
              * @param   lpPoint
              *          A pointer to a POINT structure that receives the screen coordinates of the cursor.
              *
              * @return  Returns nonzero if successful or zero otherwise.
              */
             public static integer getCursorPos(int64 lpPoint)
             {
                if (!BaseDataType.isAllKnown(lpPoint))
                {
                   return new integer(0);
                }

                LowLevelBuffer mem = memptr.getMemoryManager();
                byte[] data = mem.read(lpPoint.longValue(), 0, 16);
                if (data == null)
                {
                   return new integer(0);
                }

                NativePoint pos = LogicalTerminal.getClient().getMousePosition();

                IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
                buf.put(0, pos.x);
                buf.put(1, pos.y);
                mem.write(lpPoint.longValue(), 0, data);

                return new integer(1);
             }

             /**
              * The ClientToScreen function converts the client-area coordinates of a specified point
              * to screen coordinates.
              *
              * @param   hWnd
              *          A handle to the window whose client area will be used for the conversion.
              * @param   lpPoint
              *          A pointer to a POINT structure that specifies the client coordinates to be
              *          converted.
              *
              * @return If the function succeeds, the return value is nonzero. If the function fails,
              *         the return value is zero.
              */
             public static integer clientToScreen(integer hWnd, memptr lpPoint)
             {
                return clientToScreen(hWnd, lpPoint.getPointerValue());
             }

             /**
              * The ClientToScreen function converts the client-area coordinates of a specified point
              * to screen coordinates.
              *
              * @param   hWnd
              *          A handle to the window whose client area will be used for the conversion.
              * @param   lpPoint
              *          A pointer to a POINT structure that specifies the client coordinates to be
              *          converted.
              *
              * @return If the function succeeds, the return value is nonzero. If the function fails,
              *         the return value is zero.
              */
             public static integer clientToScreen(integer hWnd, int64 lpPoint)
             {
                if (!BaseDataType.isAllKnown(hWnd, lpPoint))
                {
                   return new integer(0);
                }

                CommonWidget w = getWidget(hWnd.intValue());
                if (w == null)
                {
                   return new integer(0);
                }

                LowLevelBuffer mem = memptr.getMemoryManager();
                byte[] data = mem.read(lpPoint.longValue(), 0, 16);
                if (data == null)
                {
                   return new integer(0);
                }

                IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
                NativePoint loc = new NativePoint(buf.get(), buf.get());
                NativePoint zero = new NativePoint(0, 0);
                zero = LogicalTerminal.getClient().screenToClient(w.getId(), zero);
                // the zero point coordinates are always negative here, need to invert
                loc = loc.translate(-zero.x, -zero.y);
                buf.put(0, loc.x);
                buf.put(1, loc.y);
                mem.write(lpPoint.longValue(), 0, data);

                return new integer(1);
             }
             /**
              * The ScreenToClient function converts the screen coordinates of a specified point on the screen
              * to client-area coordinates.
              *
              * @param   hWnd
              *          A handle to the window whose client area will be used for the conversion.
              * @param   lpPoint
              *          A pointer to a POINT structure that specifies the screen coordinates to be converted.
              *
              * @return If the function succeeds, the return value is nonzero. If the function fails, the
              *         return value is zero.
              */
             public static integer screenToClient(integer hWnd, memptr lpPoint)
             {
                return screenToClient(hWnd, lpPoint.getPointerValue());
             }

             /**
              * The ScreenToClient function converts the screen coordinates of a specified point on the screen
              * to client-area coordinates.
              *
              * @param   hWnd
              *          A handle to the window whose client area will be used for the conversion.
              * @param   lpPoint
              *          A pointer to a POINT structure that specifies the screen coordinates to be converted.
              *
              * @return If the function succeeds, the return value is nonzero. If the function fails, the
              *         return value is zero.
              */
             public static integer screenToClient(integer hWnd, int64 lpPoint)
             {
                if (!BaseDataType.isAllKnown(hWnd, lpPoint))
                {
                   return new integer(0);
                }

                CommonWidget w = getWidget(hWnd.intValue());
                if (w == null)
                {
                   return new integer(0);
                }

                LowLevelBuffer mem = memptr.getMemoryManager();
                byte[] data = mem.read(lpPoint.longValue(), 0, 16);
                if (data == null)
                {
                   return new integer(0);
                }

                IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
                NativePoint loc = new NativePoint(buf.get(), buf.get());
                loc = LogicalTerminal.getClient().screenToClient(w.getId(), loc);
                buf.put(0, loc.x);
                buf.put(1, loc.y);
                mem.write(lpPoint.longValue(), 0, data);

                return new integer(1);
             }

             /**
              * The ShowScrollBar function shows or hides the specified scroll bar.
              *
              * @param   hWnd
              *          Handle to a scroll bar control or a window with a standard scroll bar, depending on
              *          the value of the wBar parameter.
              * @param   wBar
              *          Specifies the scroll bar(s) to be shown or hidden.
              * @param   bShow
              *
              * @return If the function succeeds, the return value is nonzero. If the function fails,
              *         the return value is zero.
              */
             public static integer showScrollBar(integer hWnd, integer wBar, integer bShow)
             {
                if (!BaseDataType.isAllKnown(hWnd, wBar, bShow))
                {
                   return new integer(0);
                }

                GenericWidget w = getWidget(hWnd.intValue());
                if (!(w instanceof FrameWidget))
                {
                   return new integer(0);
                }

                FrameConfig c = ((FrameWidget) w).config();
                c.scrollbarFlags |= 1;
                byte bitsToSet = 0;
                switch (wBar.intValue())
                {
                   // SB_HORZ
                   case 0:
                      bitsToSet |= 2;
                      break;
                   // SB_VERT
                   case 1:
                      bitsToSet |= 4;
                      break;
                   // SB_BOTH
                   case 3:
                      bitsToSet |= (2 | 4);
                      break;
                   default:
                      throw new RuntimeException("Unsupported wBar value given to ShowScrollBar [" +
                                                 wBar.intValue() + "]");
                }

                if (bShow.intValue() == 0)
                {
                   // FALSE
                   c.scrollbarFlags &= ~bitsToSet;
                }
                else
                {
                   // TRUE
                   c.scrollbarFlags |= bitsToSet;
                }

                w.pushWidgetAttr("scrollbarFlags", c.scrollbarFlags);
                return new integer(1);
             }

             /**
              * Retrieves the window handle to the active window attached to the calling thread's message queue.
              *
              * @return  The return value is the handle to the active window attached to the calling thread's
              *          message queue. Otherwise, the return value is NULL.
              */
             public static integer getActiveWindow()
             {
                handle win = LogicalTerminal.activeWindow();
                if (!win.isUnknown())
                {
                   int winId = ((GenericWidget) win.getResource()).getId();
                   // this is special case when getActiveWindow() is calling in intermediate state
                   // during activation status is on the way to moving from one window to another
                   if (WindowManager.getActiveDriverWindow() == null &&
                       WindowManager.getCurrentWindow() == null &&
                       WindowManager.getFocusWindow() == null)
                   {
                      handle winCurr = LogicalTerminal.currentWindow();
                      if (!winCurr.isUnknown())
                      {
                         winId = ((GenericWidget) winCurr.getResource()).getId();
                      }
                   }
                   return new integer(winId);
                }
                else
                {
                   return new integer(NULL);
                }
             }

             /**
              * Retrieves the window handle to the foreground window attached to the calling thread's
              * message queue.
              *
              * @return  The return value is the handle to the foreground window attached to the
              *          calling thread's message queue. Otherwise, the return value is NULL.
              */
             public static integer getForegroundWindow()
             {
                TopLevelWindow<?> tlw = WindowManager.getActiveDriverWindow();
                if (tlw != null)
                {
                   return new integer(tlw.getId().asInt());
                }
                else
                {
                   // use current window as fallback for foreground window calculation
                   handle winCurr = LogicalTerminal.currentWindow();
                   if (!winCurr.isUnknown())
                   {
                      return new integer(((GenericWidget) winCurr.getResource()).getId());
                   }
                   else
                   {
                      return new integer(NULL);
                   }
                }
             }

           /**
              * Retrieves the specified system metric or system configuration setting.
              *
              * @param   nIndex
              *          The system metric or configuration setting to be retrieved.
              *
              * @return  If the function succeeds, the return value is the requested system metric or
              *          configuration setting. If the function fails, the return value is 0.
              */
             public static integer getSystemMetrics(integer nIndex)
             {
                if (nIndex.isUnknown())
                {
                   return new integer(0);
                }

                switch (nIndex.intValue())
                {
                   // SM_CXVSCROLL
                   case 2:
                      return new integer(LogicalTerminal.getClientMetrics().scrollbarWidth);
                   // SM_XVIRTUALSCREEN
                   // SM_YVIRTUALSCREEN
                   case 76:
                   case 77:
                      // assume top-left origin of virtual screen is at 0,0
                      return new integer(0);
                   // SM_CXVIRTUALSCREEN
                   case 78:
                      ClientMetrics cm = LogicalTerminal.getClientMetrics();
                      return new integer(cm.virtualScreenSize.width);
                   // SM_CYVIRTUALSCREEN
                   case 79:
                      cm = LogicalTerminal.getClientMetrics();
                      return new integer(cm.virtualScreenSize.height);

                   default:
                      LOG.severe("Unsupported client metrics index [" + nIndex.intValue() + "]");
                      return new integer(0);
                }
             }

             /**
              * Gets the current scrolling position for given scrollbar type.
              *
              * @param    hwnd
              *           A handle of the window to check.  This is a widget ID in terms of FWD.
              * @param    nBar
              *           Type of the scrollbar to check, horizontal (SB_HORZ or 0) or vertical (SB_VERT or 1).
              *
              * @return   The value of the current scroll position.
              */
             public static integer getScrollPos(integer hwnd, integer nBar)
             {
                if (!BaseDataType.isAllKnown(hwnd, nBar))
                {
                   // no-op
                   LOG.fine("Invalid input parameters for win32 native GetScrollPos() call.");
                   return new integer();
                }

                GenericWidget w = LogicalTerminal.getWidgetForId(hwnd.intValue());
                boolean isHorizontal = nBar.intValue() == SB_HORZ;

                return w instanceof PaneEntity ? isHorizontal
                                                  ? ((PaneEntity)w).getHScrollPosition()
                                                  : ((PaneEntity)w).getVScrollPosition()
                                               : new integer(0);
             }

             /**
              * Implements the replacement functionality for native Windows messages.
              *
              * @param    hwnd
              *           A handle of the window as a source of the message.  This is a widget ID in terms of FWD.
              * @param    uMsg
              *           The message identifier.
              * @param    wParam
              *           The first parameter of the native Windows message.
              * @param    lParam
              *           The second parameter of the native Windows message.
              */
             public static void sendMessageA(integer hwnd,
                                             integer uMsg,
                                             integer wParam,
                                             integer lParam)
             {
                if (!BaseDataType.isAllKnown(hwnd, uMsg, wParam, lParam))
                {
                   LOG.fine("Invalid input parameters for win32 native SendMessageA() call.");
                   return;
                }

                GenericWidget w = LogicalTerminal.getWidgetForId(hwnd.intValue());
                int msgType = uMsg.intValue();

                switch (msgType)
                {
                   case WM_HSCROLL:
                   case WM_VSCROLL:
                   {
                      if (w instanceof PaneEntity)
                      {
                         // extract scroll values from message options
                         int fullValue = wParam.intValue();
                         int loValue = fullValue & LO_WORD_MASK;
                         int hiValue = (fullValue & HI_WORD_MASK) >> 16;

                         if (loValue == SB_THUMBPOSITION)
                         {
                            if (msgType == WM_HSCROLL)
                            {
                               ((PaneEntity)w).setHScrollPosition(new integer(hiValue));
                            }
                            else
                            {
                               ((PaneEntity)w).setVScrollPosition(new integer(hiValue));
                            }
                         }
                         else
                         {
                            UnimplementedFeature.unsupported("Scroll type [" + loValue +
                                     "] for native win32 " +
                                     (msgType == WM_HSCROLL ? "WM_HSCROLL" : "WM_VSCROLL" ) + " message.");
                         }
                      }
                      else
                      {
                         UnimplementedFeature.unsupported("Target type [" + w.type() +
                                  "] for native win32" +
                                  (msgType == WM_HSCROLL ? "WM_HSCROLL" : "WM_VSCROLL" ) + " message.");
                      }
                      break;
                   }
                   case MsgBlasterPeer.WM_RBUTTONDOWN:
                   case MsgBlasterPeer.WM_RBUTTONUP:
                   {
                      ClientExports ce = LogicalTerminal.getClient();
                      if (hwnd.isUnknown() || uMsg.isUnknown() || wParam.isUnknown() || lParam.isUnknown())
                      {
                         NativeInvoker.genInputUnknownErr(true);
                         return;
                      }

                      ce.applyMessageA(hwnd.intValue(), uMsg.intValue(), wParam.intValue(), lParam.intValue(), true);
                      break;
                   }
                   default:
                      UnimplementedFeature.unsupported("Native win32 message type: [" + uMsg.intValue() + "]");
                }

                return;
             }

             /**
              * Checks if the window for the given handle is already realized.
              *
              * @param    hwnd
              *           A handle of the window to check.
              *
              * @return   If the function succeeds, the return value is nonzero. If the function fails, the
              *           return value is zero.
              */
             public static integer isWindow(integer hwnd)
             {
                if (hwnd == null || hwnd.isUnknown())
                {
                   // window does not exist
                   return new integer(FALSE);
                }
                else
                {
                   GenericWidget w = getWidget(hwnd.intValue());
                   if (w == null)
                   {
                      return new integer(FALSE);
                   }
                   else
                   {
                      return new integer(w._isRealized() ? TRUE : FALSE);
                   }
                }
             }

             /**
              * Requests window lock for the given window handle.
              *
              * @param    hwnd
              *           A handle to the window to lock.
              *
              * @return   If the function succeeds, the return value is nonzero. If the function fails, the
              *           return value is zero.
              */
             public static integer lockWindowUpdate(integer hwnd)
             {
                if (hwnd == null || hwnd.isUnknown() || hwnd.intValue() == 0)
                {
                   if (wndLocked != null)
                   {
                      // unlock previously locked window
                      wndLocked.setDisableRedraw(false);
                      wndLocked = null;
                   }
                   else
                   {
                      // nothing to unlock, operation failed
                      return new integer(FALSE);
                   }
                }
                else
                {
                   GenericWidget w = getWidget(hwnd.intValue());
                   if (w == null)
                   {
                      return new integer(FALSE);
                   }

                   // lock the given window
                   if (wndLocked == null)
                   {
                      w.setDisableRedraw(true);
                      wndLocked = w;
                   }
                   else
                   {
                      // some window already locked, only single is possible
                      return new integer(FALSE);
                   }
                }

                return new integer(TRUE);
             }

             /**
              * Should disable the ability to minimize, move, or close the main window of an
              * application that is not responding.
              */
             public static void disableProcessWindowsGhosting()
             {
                // Nothing to do in FWD at this time, may be modalize current window
             }

             /**
              * Checks if the window for the given handle is visible or not.
              *
              * @param    hwnd
              *           A handle of the window to check.
              *
              * @return   If the window is visible the return value is <code>1</code>. Otherwise it
              *           is <code>0</code>.
              */
             public static integer isWindowVisible(integer hwnd)
             {
                if (hwnd == null || hwnd.isUnknown())
                {
                   // window does not exist
                   return new integer(FALSE);
                }
                else
                {
                   GenericWidget w = getWidget(hwnd.intValue());
                   if (w == null)
                   {
                      return new integer(FALSE);
                   }
                   else
                   {
                      return new integer(w._isVisible() ? TRUE : FALSE);
                   }
                }
             }

             /**
              * Retrieves the current color of the specified display element.
              *
              * @param    nIndex
              *           The display element whose color is to be retrieved.
              *
              * @return   RGB value of the requested display element
              */
             public static integer getSysColor(integer nIndex)
             {
                if (nIndex.isUnknown())
                {
                   return new integer(0);
                }

                Map<Integer, ColorRgb> palette = LogicalTerminal.getThemeSystemPalette();
                ColorRgb clr = null;

                switch (nIndex.intValue())
                {
                   // COLOR_3DFACE
                   case 15:
                      clr = palette.get(SysColor.COLOR_3DFACE);
                      break;
                   // COLOR_3DSHADOW
                   case 16:
                      clr = palette.get(SysColor.COLOR_3DSHADOW);
                      break;
                   // COLOR_3DHIGHLIGHT (20 matches with COLOR_BTNHIGHLIGHT)
                   case 20:
                      clr = palette.get(SysColor.COLOR_BTNHIGHLIGHT);
                      break;

                   default:
                      LOG.severe("Unsupported display element index [" + nIndex.intValue() + "]");
                }
                return new integer((clr != null) ? clr.getRgb() : 0);
             }

             /**
              * Moves the window to the specified position.
              *
              * @param    hWnd
              *           A handle of the window to move.
              * @param    x
              *           A new X position.
              * @param    y
              *           A new Y position.
              * @param    w
              *           A new window width.
              * @param    h
              *           A new window height.
              * @param    bRepaint
              *           Flag indicating if we need to repaint window after move.
              *
              * @return   <code>1</code> if success, <code>0</code> otherwise.
              */
             public static integer moveWindow(integer hWnd, integer x, integer y, integer w, integer h,
                                              integer bRepaint)
             {
                if (!BaseDataType.isAllKnown(hWnd, x, y, w, h, bRepaint))
                {
                   return new integer(FALSE);
                }

                GenericWidget wndWid = getWidget(hWnd.intValue());
                if (wndWid == null)
                {
                   return new integer(FALSE);
                }

                if (w.intValue() <= 0 || h.intValue() <= 0)
                {
                   // implicit window hide
                   wndWid.hide();
                }
                else
                {
                   // TODO: The calls below are not optimal because generates 4 server-client calls with repaint
                   //       on each. However making single call requires a lot of rework in BaseEntity class or
                   //       copy-paste many lines from there which is not the best as well. Considering this is
                   //       not a frequently used method it should not be a pefrormance bottleneck for.
                   //       application. But we need to keep this in mind for case when it could becom an issue
                   //       to get back and provide better solution.
                   wndWid.setX(x);
                   wndWid.setY(y);
                   wndWid.setWidthPixels(w);
                   wndWid.setHeightPixels(h);
                }

                // the moved window will be repainted automatically so we can ignore bRepaint option now

                return new integer(TRUE);
             }

             /**
              * Resolves a widget based on the supplied hwnd value.
              *
              * @param   hwnd
              *          A hwnd value.
              *
              * @return  the widget or {@code null}.
              */
             private static GenericWidget getWidget(int hwnd)
             {
                GenericWidget w = LogicalTerminal.getWidgetForId(hwnd);
                if (w == null)
                {
                   w = LogicalTerminal.getWindowByNonClientId(hwnd);
                }

                return w;
             }
          }

          /**
           * Declares supported kernel32.dll functions.
           */
          public static class kernel32
          {
             /** Error number read by {@link #getLastError} */
             private static int errorno = 0;

             /**
              * Retrieves the calling thread's last-error code value.
              *
              * @return  The return value is the calling thread's last-error code.
              */
             public static integer getLastError()
             {
                return new integer(errorno);
             }

             /**
              * Returns the language identifier for the user UI language for the current user.
              *
              * @return  Returns the language identifier for the user UI language for the current user.
              */
             public static integer getUserDefaultUILanguage()
             {
                // TODO: this will require a corresponding configuration entry in the directory
                return new integer(0);
             }

             /**
              * Retrieves a string from the specified section in an initialization file.
              *
              * @param   appName
              *          The name of the section containing the key name.
              * @param   keyName
              *          The name of the key whose associated string is to be retrieved.
              * @param   defaultValue
              *          A default string.
              * @param   returnedString
              *          The retrieved string.
              * @param   size
              *          The size of the buffer pointed to by the returnedString parameter, in characters.
              * @param   fileName
              *          The name of the initialization file.
              *
              * @return  The return value is the number of characters copied to the buffer, not including the
              *          terminating null character.
              */
             public static integer getPrivateProfileStringA(character appName,
                                                            character keyName,
                                                            character defaultValue,
                                                            character returnedString,
                                                            integer   size,
                                                            character fileName)
             {
                errorno = 0;

                if (!BaseDataType.isAllKnown(appName, keyName, returnedString, size, fileName))
                {
                   return new integer(0);
                }

                String javaFileName = fileName.toStringMessage().replace('\\', File.pathSeparatorChar);
                IniFile ini = new IniFile(javaFileName);
                try
                {
                   ini.load();

                   String def = defaultValue == null || defaultValue.isUnknown() ?
                   null : defaultValue.toStringMessage();

                   returnedString.assign(ini.getString(appName.toStringMessage(),
                                                       keyName.toStringMessage(),
                                                       def));
                   return new integer(size.intValue() - 1);
                }
                catch (IOException e)
                {
                   errorno = 2;
                   return new integer(0);
                }
             }

             /**
              * Obtain the process-id (pid) of the current client process.  This returns the client JVM pid
              * (it does NOT provide the pid of the server JVM).
              *
              * @return    The current client process pid or -1 on any error.
              */
             public static integer getCurrentProcessId()
             {
                ClientParameters parms = StandardServer.getClientParameters();

                return new integer((parms == null) ? -1 : parms.pid);
             }

             /**
              * Retrieves information about a locale specified by identifier.
              *
              * @param    Locale
              *           Locale identifier for which to retrieve information.
              * @param    LCType
              *           The locale information to retrieve.
              * @param    lpLCData
              *           Pointer to a buffer in which this function retrieves the requested locale information.
              *           This pointer is not used if cchData is set to 0.
              * @param    cchData
              *           Size, in TCHAR values, of the data buffer indicated by lpLCData.
              *
              * @return   Returns the number of characters retrieved in the locale data buffer if successful
              *           and cchData is a nonzero value.
              */
             public static integer getLocaleInfoA(integer Locale,
                                                  integer LCType,
                                                  character lpLCData,
                                                  integer cchData)
             {
                // TODO: put the returned locale info in directory
                switch (LCType.intValue())
                {
                   case 12:
                      // LOCALE_SLIST - getting list items separator char
                      lpLCData.assign(",");
                      break;
                   case 31:
                      // short date format
                      lpLCData.assign("yyyy/MM/dd/");
                      break;
                   case 32:
                      // long date format
                      lpLCData.assign("dddd MMMM yyyy");
                      break;
                   default:
                      LOG.severe("Unsupported locale info type " + LCType.toStringMessage() + " .");
                }

                return new integer(0);
             }

             /**
              * Sleep for the duration passed in.
              *
              * @param   millis
              *          Number of milliseconds to sleep.
              */
             public static void sleep(int64 millis)
             {
                long unsigned_value = millis.getValue() & 0xFFFFFFFF;
                try
                {
                   Thread.sleep(unsigned_value);
                }
                catch (InterruptedException ie)
                {
                   // TODO: It is not clear that os-sleep can be interrupted. With 4GL the ON STOP
                   //       gets handled, but only *after* the sleep duration completes.
                   //       This was tested with testcase uast/windows_emulation/kernel32_sleep.p
                   //       Note also that on Windows, Control+Break is received as a STOP, but
                   //       Control+C is handled differently. The testcase jumps to "Done." and exits.
                   //       Handling of the InterruptedException may still be required, if we can see
                   //       the behavior when signalling the kernel32:sleep directly.
                }
             }

             /**
              * Retrieves the name of the computer associated with the current thread.
              *
              * @param   lpBuffer
              *          A pointer to the buffer to receive the user's computer name.
              * @param   pcbBuffer
              *          On input, this variable specifies the size of the lpBuffer buffer, in TCHARs. On
              *          output, the variable receives the number of TCHARs copied to the buffer, including
              *          the terminating null character.
              *
              * @return If the function succeeds, the return value is a nonzero value, and the variable pointed
              *         to by lpnSize contains the number of TCHARs copied to the buffer specified by lpBuffer,
              *         including the terminating null character.
              *         If the function fails, the return value is zero. To get extended error information,
              *         call GetLastError.
              */
             public static int getComputerNameA(character lpBuffer, integer pcbBuffer)
             {
                int res = FALSE;
                errorno = 0; // ERROR_BUFFER_OVERFLOW (-2147024785 or 0x8007006F) if name is too long.
                String currentHostName = LogicalTerminal.getHostName();
                if (currentHostName != null && !currentHostName.isEmpty())
                {
                   lpBuffer.assign(currentHostName);
                   pcbBuffer.assign(currentHostName.length() + 1);
                   res = TRUE;
                }
                else
                {
                   lpBuffer.assign(character.EMPTY_STRING);
                   pcbBuffer.assign(0);
                }
                // TODO: It may not be entirely accurate to just return the IP address, as
                //       LogicalTerminal.getHostName() does. However, the complexities of
                //       whether the client interface is a browser, or swing, and where to actually
                //       resolve the name are being left to an actual requirement specification.
                //       Implementation will ultimately be left to the client-side terminals.
                return res;
             }
          }

          /**
           * Declares supported advapi32.dll functions.
           */
          public static class advapi32
          {
             /**
              * Retrieves the name of the user associated with the current thread.
              *
              * @param   lpBuffer
              *          A pointer to the buffer to receive the user's logon name.
              * @param   pcbBuffer
              *          On input, this variable specifies the size of the lpBuffer buffer, in TCHARs. On
              *          output, the variable receives the number of TCHARs copied to the buffer, including
              *          the terminating null character.
              *
              * @return If the function succeeds, the return value is a nonzero value, and the variable pointed
              *         to by lpnSize contains the number of TCHARs copied to the buffer specified by lpBuffer,
              *         including the terminating null character.
              *         If the function fails, the return value is zero. To get extended error information,
              *         call GetLastError.
              */
             public static int getUserNameA(character lpBuffer, integer pcbBuffer)
             {
                int res = FALSE;
                String currentUserName = LogicalTerminal.getUserName();
                if (currentUserName != null && !currentUserName.isEmpty())
                {
                   lpBuffer.assign(currentUserName);
                   pcbBuffer.assign(currentUserName.length() + 1);
                   res = TRUE;
                }
                else
                {
                   lpBuffer.assign(character.EMPTY_STRING);
                   pcbBuffer.assign(0);
                }

                return res;
             }
          }

          /**
           * Declares supported wsock32.dll functions.
           */
          public static class wsock32
          {
             /**
              * Prepares the socket library to get inet info.
              *
              * @param   versionReq
              *          A pointer to the version buffer, not used.
              * @param   wsaDataPointer
              *          The pointer to input data buffer, not used.
              *
              * @return If the function succeeds, the return value is a nonzero value, if the function
              *         fails, the return value is zero. To get extended error information, call
              *         GetLastError.
              */
             public static int wSAStartup(integer versionReq, int64 wsaDataPointer)
             {
                // nothing to do here
                return TRUE;
             }

             /**
              * Finalizes the socket library to use.
              *
              * @return If the function succeeds, the return value is a nonzero value, if the function
              *         fails, the return value is zero. To get extended error information, call
              *         GetLastError.
              */
             public static int wSACleanup()
             {
                // nothing to do here
                return TRUE;
             }

             /**
              * Gets the name of the host the client is currently running.
              *
              * @param   hostName
              *          A pointer to the buffer to receive the client side hostname.
              * @param   nbLength
              *          On input, this variable specifies the size of the hostName buffer, in TCHARs.
              *          Currently the value is ignoring.
              *
              * @return If the function succeeds, the return value is a nonzero value, and the variable
              *         pointed to by nbLength contains the number of TCHARs copied to the buffer
              *         specified by hostName, including the terminating null character.
              *         If the function fails, the return value is zero. To get extended error
              *         information, call GetLastError.
              */
             public static int gethostname(character hostName, int64 nbLength)
             {
                int res = FALSE;

                // get the computer name from the client side
                String hName = LogicalTerminal.getHostName();
                if (hName != null && !hName.isEmpty())
                {
                   res = TRUE;
                   hostName.assign(new character(hName));
                }

                return res;
             }

             /**
              * Gets the host name of the host the client is currently running.
              *
              * @param   hostName
              *          A character based hostname.
              *
              * @return If the function succeeds, the return value is a memptr object containing
              *         hostent data structure with the host address. To get extended error
              *         information, call GetLastError.
              */
             public static int64 gethostbyname(character hostName)
             {
                return new int64(LogicalTerminal.getHostByName(hostName.getValue()));
             }

             /**
              * Gets the host name of the host the client is currently running.
              *
              * @param   addrInputStruct
              *          Structure containing digital representation of the Internet address.
              *
              * @return If the function succeeds, the return value is a memptr object containing
              *         IP address in format "0-255.0-255.0-255.0-255". To get extended error
              *         information, call GetLastError.
              */
             public static memptr inet_ntoa(int64 addrInputStruct)
             {
                // get the 32-bit integer address value
                int ipAddr = addrInputStruct.intValue();

                // convert big-endian integer address value to
                StringBuilder sb = new StringBuilder();
                sb.append((byte)((ipAddr & 0xFF000000) >> 24));
                sb.append(".");
                sb.append((byte)((ipAddr & 0x00FF0000) >> 16));
                sb.append(".");
                sb.append((byte)((ipAddr & 0x0000FF00) >> 8));
                sb.append(".");
                sb.append((byte)(ipAddr & 0x000000FF));

                // pack the String into memptr to return
                return new memptr(new character(sb.toString()));
             }
          }
       }

       /**
        * Removes all options from the supplied 4GL string literal. That is any options bayond the colon char
        * ':' will be removed together with the colon.
        *
        * @param   text
        *          4GL string literal.
        *
        * @return  text with removed 4GL string literal options.
        */
       private static String removeStringLiteralOptions(String text)
       {
          int i = text.indexOf(':');
          if (i >= 0)
          {
             return text.substring(0, i);
          }

          return text;
       }
    }