/****************************************************************************
 * apps/graphics/nuttx/pdckbd.c
 *
 *   Copyright (C) 2017 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <fcntl.h>

#include "pdcnuttx.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/* Currently only this bits are used.  Others are simply masked out. */

#define DJOY_BITSET (DJOY_UP_BIT | DJOY_DOWN_BIT | DJOY_LEFT_BIT | \
                     DJOY_RIGHT_BIT | DJOY_BUTTON_SELECT_BIT)

/****************************************************************************
 * Public Data
 ****************************************************************************/

/* Key modifiers.  The OR of any of:
 *
 *   PDC_KEY_MODIFIER_SHIFT;
 *   PDC_KEY_MODIFIER_CONTROL;
 *   PDC_KEY_MODIFIER_ALT;
 *   PDC_KEY_MODIFIER_NUMLOCK;
 */

unsigned long pdc_key_modifiers;

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: PDC_djoy_sample
 *
 * Description:
 *   Keyboard/mouse event check, called from wgetch(). Returns true if
 *   there's an event ready to process. This function must be non-blocking.
 *
 ****************************************************************************/

#ifdef CONFIG_PDCURSES_DJOYSTICK
static djoy_buttonset_t PDC_djoy_sample(FAR struct pdc_fbstate_s *fbstate)
{
  djoy_buttonset_t sample;
  ssize_t nread;

  PDC_LOG(("PDC_check_key() - called: DJoystick\n"));

  nread = read(fbstate->djfd, &sample, sizeof(djoy_buttonset_t));
  if (nread < 0)
    {
      PDC_LOG(("ERROR: read() failed: %d\n", errno));
      return 0;
    }
  else if (nread != sizeof(djoy_buttonset_t))
    {
      PDC_LOG(("ERROR: read() unexpected size: %ld vs %d\n",
              (long)nread, sizeof(djoy_buttonset_t)));
      return 0;
    }

  return sample & DJOY_BITSET;
}
#endif

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: PDC_check_key
 *
 * Description:
 *   Keyboard/mouse event check, called from wgetch(). Returns true if
 *   there's an event ready to process. This function must be non-blocking.
 *
 ****************************************************************************/

bool PDC_check_key(void)
{
#if defined(CONFIG_PDCURSES_KEYBOARD)
#  warning Missing logic
  return false;

#elif defined(CONFIG_PDCURSES_MOUSE)
#  warning Missing logic
  return false;

#elif defined(CONFIG_PDCURSES_DJOYSTICK)
  {
    FAR struct pdc_fbscreen_s *fbscreen = (FAR struct pdc_fbscreen_s *)SP;
    FAR struct pdc_fbstate_s *fbstate;
    djoy_buttonset_t newset;

    PDC_LOG(("PDC_check_key() - called: DJoystick\n"));

    DEBUGASSERT(fbscreen != NULL);
    fbstate = &fbscreen->fbstate;

    /* Sample the discrete joystick bits and return true of any of them
     * are now in a different state.
     */

    newset = PDC_djoy_sample(fbstate);
    return (fbstate->djlast ^ newset) != 0;
  }

#else
  return false;
#endif
}

/****************************************************************************
 * Name: PDC_get_key
 *
 * Description:
 *   Get the next available key, or mouse event (indicated by a return of
 *   KEY_MOUSE), and remove it from the OS' input queue, if applicable. This
 *   function is called from wgetch().  This function may be blocking, and
 *   traditionally is; but it need not be.  If a valid key or mouse event
 *   cannot be returned, for any reason, this function returns -1. Valid keys
 *   are those that fall within the appropriate character set, or are in the
 *   list of special keys found in curses.h (KEY_MIN through KEY_MAX). When
 *   returning a special key code, this routine must also set SP->key_code to
 *   true; otherwise it must set it to false. If SP->return_key_modifiers is
 *   true, this function may return modifier keys (shift, control, alt),
 *   pressed alone, as special key codes; if SP->return_key_modifiers is
 *   false, it must not. If modifier keys are returned, it should only happen
 *   if no other keys were pressed in the meantime; i.e., the return should
 *   happen on key up. But if this is not possible, it may return the
 *   modifier keys on key down (if and only if SP->return_key_modifiers is
 *   true).
 *
 ****************************************************************************/

int PDC_get_key(void)
{
#if defined(CONFIG_PDCURSES_KEYBOARD)
#  warning Missing logic
  return false;

#elif defined(CONFIG_PDCURSES_MOUSE)
#  warning Missing logic
  return false;

#elif defined(CONFIG_PDCURSES_DJOYSTICK)
  {
    FAR struct pdc_fbscreen_s *fbscreen = (FAR struct pdc_fbscreen_s *)SP;
    FAR struct pdc_fbstate_s *fbstate;
    djoy_buttonset_t sample;
    djoy_buttonset_t newbits;

    PDC_LOG(("PDC_get_key() - called: DJoystick\n"));

    DEBUGASSERT(fbscreen != NULL);
    fbstate = &fbscreen->fbstate;

    /* Sample the discrete joystick bits, get the bits that have changed
     * state, then update the settings for the next time we get here.
     */

    sample          = PDC_djoy_sample(fbstate);
    newbits         = sample & (sample ^ fbstate->djlast);
    fbstate->djlast = sample;

    if (newbits == 0)
      {
        /* Nothing has changed... we should:
         *
         * 1) Check for repeat keys.  Up, Down, Left and Right should repeat
         *    at a most rate if held down.
         * 2) Otherwise, block, polling for a change in button state.  But,
         *    apparently, we can just return ERR in this case.
         *
         *    The joystick driver also supports a notification via signal
         *    when a button change occurs.  This could be an option to poll().
         */

        return ERR;
      }

    /* Return a key code for any button that has just been pressed (button
     * releases are not reported)
     */

    if ((newbits & DJOY_BUTTON_SELECT_BIT) != 0)
      {
        return '\n';
      }
    else if ((newbits & DJOY_UP_BIT) != 0)
      {
        return KEY_UP;
      }
    else if ((newbits & DJOY_DOWN_BIT) != 0)
      {
        return KEY_DOWN;
      }
    else if ((newbits & DJOY_LEFT_BIT) != 0)
      {
        return KEY_LEFT;
      }
    else /* if ((newbits & DJOY_RIGHT_BIT) != 0) */
      {
        return KEY_RIGHT;
      }

    return ERR;
  }

#else
  return false;
#endif
}

/****************************************************************************
 * Name: PDC_get_input_fd
 *
 * Description:
 *   PDC_get_input_fd() returns the file descriptor that PDCurses reads its
 *   input from.  It can be used for select().
 *
 ****************************************************************************/

unsigned long PDC_get_input_fd(void)
{
#ifdef CONFIG_PDCURSES_HAVE_INPUT
  FAR struct pdc_fbscreen_s *fbscreen = (FAR struct pdc_fbscreen_s *)SP;
  FAR struct pdc_fbstate_s *fbstate;

  PDC_LOG(("PDC_get_input_fd() - called\n"));

  DEBUGASSERT(fbscreen != NULL);
  fbstate = &fbscreen->fbstate;

#if defined(CONFIG_PDCURSES_KEYBOARD)
#  warning Missing logic
  return -1;

#elif defined(CONFIG_PDCURSES_MOUSE)
#  warning Missing logic
  return -1;

#else /* if defined(CONFIG_PDCURSES_DJOYSTICK) */
  return fbstate->djfd;

#endif
#else
  PDC_LOG(("PDC_get_input_fd() - called:  No input device\n"));

  /* No input device */

  return -1;
#endif
}

/****************************************************************************
 * Name: PDC_set_keyboard_binary
 *
 * Description:
 *   Set keyboard input to "binary" mode. If you need to do something to keep
 *   the OS from processing ^C, etc. on your platform, do it here. true turns
 *   the mode on; false reverts it. This function is called from raw() and
 *   noraw().
 *
 ****************************************************************************/

void PDC_set_keyboard_binary(bool on)
{
  PDC_LOG(("PDC_set_keyboard_binary() - called\n"));
}

/****************************************************************************
 * Name: PDC_flushinp
 *
 * Description:
 *   This is the core of flushinp(). It discards any pending key or mouse
 *   events, removing them from any internal queue and from the OS queue, if
 *   applicable.
 *
 ****************************************************************************/

void PDC_flushinp(void)
{
  PDC_LOG(("PDC_flushinp() - called\n"));

#ifdef CONFIG_PDCURSES_KEYBOARD
#  warning Missing logic
#endif

#ifdef CONFIG_PDCURSES_MOUSE
#  warning Missing logic
#endif
}

/****************************************************************************
 * Name: PDC_mouse_set
 *
 * Description:
 *   Called by mouse_set(), mouse_on(), and mouse_off() -- all the functions
 *   that modify SP->_trap_mbe. If your platform needs to do anything in
 *   response to a change in SP->_trap_mbe (for example, turning the mouse
 *   cursor on or off), do it here. Returns OK or ERR, which is passed on by
 *   the caller.
 *
 ****************************************************************************/

int PDC_mouse_set(void)
{
#ifdef CONFIG_PDCURSES_MOUSE
#  warning Missing logic
#endif
  return OK;
}

/****************************************************************************
 * Name: PDC_modifiers_set
 *
 * Description:
 *   Called from PDC_return_key_modifiers(). If your platform needs to do
 *   anything in response to a change in SP->return_key_modifiers, do it
 *   here. Returns OK or ERR, which is passed on by the caller.
 *
 ****************************************************************************/

int PDC_modifiers_set(void)
{
#ifdef CONFIG_PDCURSES_KEYBOARD
#  warning Missing logic
#endif
  return OK;
}

/****************************************************************************
 * Name: PDC_input_open
 *
 * Description:
 *   Open and configure any input devices
 *
 ****************************************************************************/

#ifdef CONFIG_PDCURSES_HAVE_INPUT
int PDC_input_open(FAR struct pdc_fbstate_s *fbstate)
{
#ifdef CONFIG_PDCURSES_DJOYSTICK
  /* Initialize discrete joystick state. */

  fbstate->djlast = 0;

  /* Open the djoystick device */

  fbstate->djfd = open(CONFIG_PDCURSES_DJOYDEV, O_RDONLY);
  if (fbstate->djfd < 0)
    {
      PDC_LOG(("ERROR: Failed to open %s: %d\n",
              CONFIG_PDCURSES_DJOYDEV, errno));
      return ERR;
    }
#endif

  return OK;
}
#endif

/****************************************************************************
 * Name: PDC_input_close
 *
 * Description:
 *   Close any input devices and release any resources committed by
 *   PDC_input_open()
 *
 ****************************************************************************/

#ifdef CONFIG_PDCURSES_HAVE_INPUT
void PDC_input_close(FAR struct pdc_fbstate_s *fbstate)
{
#ifdef CONFIG_PDCURSES_DJOYSTICK
  close(fbstate->djfd);
#endif
}
#endif
