/****************************************************************************
 * examples/nrf24l01_term/nrf24l01_term.c
 *
 *   Copyright (C) 2013 Laurent Latil. All rights reserved.
 *   Author: Laurent Latil <laurent@latil.nom.fr>
 *
 * 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 <nuttx/config.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <debug.h>
#include <poll.h>
#include <fcntl.h>

#include <nuttx/wireless/nrf24l01.h>

#include "system/readline.h"

#ifdef CONFIG_DISABLE_POLL
#  error The poll interface is required for the nRF24L01 terminal example. You must disable config option DISABLE_POLL.
#endif

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

#define DEV_NAME   "/dev/nrf24l01"

#ifndef STDIN_FILENO
#  define STDIN_FILENO 0
#endif

#define DEFAULT_RADIOFREQ  2450

#define DEFAULT_TXPOWER    -6    /* (0, -6, -12, or -18 dBm) */

#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
#  define N_PFDS  2    /* If RX support is enabled, poll both stdin and the message reception */
#else
#  define N_PFDS  1    /* If RX support is not enabled, we cannot poll the wireless device */
#endif

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

int wireless_cfg(int fd);

int wireless_open(void);

int send_pkt(int wl_fd);

int read_pkt(int wl_fd);

void usage(void);

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const uint8_t defaultaddr[NRF24L01_MAX_ADDR_LEN] = { 0x01, 0xCA, 0xFE, 0x12, 0x34};

char buff[NRF24L01_MAX_PAYLOAD_LEN + 1] = "";

/* polled file descr */

static struct pollfd pfds[N_PFDS];

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

int wireless_cfg(int fd)
{
  int error = 0;

  uint32_t rf = DEFAULT_RADIOFREQ;
  int32_t txpow = DEFAULT_TXPOWER;
  nrf24l01_datarate_t datarate = RATE_1Mbps;

  nrf24l01_retrcfg_t retrcfg =
  {
    .count = 5,
    .delay = DELAY_1000us
  };

  uint32_t addrwidth = NRF24L01_MAX_ADDR_LEN;

  uint8_t pipes_en = (1 << 0);  /* Only pipe #0 is enabled */
  nrf24l01_pipecfg_t pipe0cfg;
  nrf24l01_pipecfg_t *pipes_cfg[NRF24L01_PIPE_COUNT] = {&pipe0cfg, 0, 0, 0, 0, 0};

  nrf24l01_state_t primrxstate;

#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
  primrxstate = ST_RX;
#else
  primrxstate = ST_POWER_DOWN;
#endif

  /* Define the pipe #0 parameters  (AA enabled, and dynamic payload length) */

  pipe0cfg.en_aa = true;
  pipe0cfg.payload_length = NRF24L01_DYN_LENGTH;
  memcpy (pipe0cfg.rx_addr, defaultaddr, NRF24L01_MAX_ADDR_LEN);

  /* Set radio parameters */

  ioctl(fd, WLIOC_SETRADIOFREQ, (unsigned long)((uint32_t *)&rf));
  ioctl(fd, WLIOC_SETTXPOWER, (unsigned long)((int32_t *)&txpow));
  ioctl(fd, NRF24L01IOC_SETDATARATE, (unsigned long)((nrf24l01_datarate_t *)&datarate));
  ioctl(fd, NRF24L01IOC_SETRETRCFG, (unsigned long)((nrf24l01_retrcfg_t *)&retrcfg));

  ioctl(fd, NRF24L01IOC_SETADDRWIDTH, (unsigned long)((uint32_t*) &addrwidth));
  ioctl(fd, NRF24L01IOC_SETTXADDR, (unsigned long)((uint8_t *)&defaultaddr));

  ioctl(fd, NRF24L01IOC_SETPIPESCFG, (unsigned long)((nrf24l01_pipecfg_t **)&pipes_cfg));
  ioctl(fd, NRF24L01IOC_SETPIPESENABLED, (unsigned long)((uint8_t *)&pipes_en));

  /* Enable receiver */

  ioctl(fd, NRF24L01IOC_SETSTATE, (unsigned long)((nrf24l01_state_t *)&primrxstate));

  return error;
}

int wireless_open(void)
{
  int fd;

  fd = open(DEV_NAME, O_RDWR);

  if (fd < 0)
    {
      perror("Cannot open nRF24L01 device");
    }
  else
    {
      wireless_cfg(fd);
    }

  return fd;
}

int send_pkt(int wl_fd)
{
  int ret;
  int len;

  /* Remove carriage return */

  len = strlen(buff);
  if (len > 0 && buff[len - 1] == '\n')
    {
      len --;
      buff[len] = '\0';
    }

  ret = write(wl_fd, buff, len);
  if (ret < 0)
    {
      perror("Error sending packet");
      return ret;
    }
  else
    {
      int retrcount;

      ioctl(wl_fd, NRF24L01IOC_GETLASTXMITCOUNT, (unsigned long)((uint32_t *)&retrcount));
      printf("Packet sent successfully !  (%d retransmitted packets)\n", retrcount);
    }

  return OK;
}

int read_pkt(int wl_fd)
{
  int ret;
  uint32_t pipeno;

  ret = read(wl_fd, buff, sizeof(buff));
  if (ret < 0)
    {
      perror("Error reading packet\n");
      return ret;
    }

  if (ret == 0)
    {
      /* Should not happen ... */
      printf("Packet payload empty !\n");
      return ERROR;
    }

  /* Get the recipient pipe #  (for demo purpose, as here the receiving pipe can only be pipe #0...) */

  ioctl(wl_fd, NRF24L01IOC_GETLASTPIPENO, (unsigned long)((uint32_t *)&pipeno));

  buff[ret] = '\0';   /* end the string */
  printf("Message received : %s   (on pipe #%d)\n", buff, pipeno);

  return 0;
}

void usage(void)
{
  printf("nRF24L01+ wireless terminal demo.\nUsage:\n");
  printf("- Type in any message ( <= 32 char length) to send it to communication peer.\n");
  printf("- Ctrl-C to exit.\n\n");
}

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

#ifdef CONFIG_BUILD_KERNEL
int main(int argc, FAR char *argv[])
#else
int nrf24l01_term_main(int argc, char *argv[])
#endif
{
  int ret;
  bool quit = false;

  int wl_fd;

  wl_fd = wireless_open();
  if (wl_fd < 0)
    {
      return -1;
    }

  usage();

  pfds[0].fd = STDIN_FILENO;
  pfds[0].events = POLLIN;

#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
  pfds[1].fd = wl_fd;
  pfds[1].events = POLLIN;
#endif

  while (!quit)
    {
      ret = poll(pfds, N_PFDS, -1);
      if (ret < 0)
        {
          perror("Error polling console / wireless");
          goto out;
        }

      if (pfds[0].revents & POLLIN)
        {
          char c;
          read(STDIN_FILENO, &c, 1);

          if (c < ' ')
            {
              /* Any non printable char -> exits */

              quit = true;
            }
          else
            {
              printf("Message to send > %c", c);
              fflush(stdout);

              /* Prepend the initial character */

              buff[0] = c;

#ifndef CONFIG_SYSTEM_READLINE
              /* Use fgets if readline utility method is not enabled */

              if (fgets(&buff[1], sizeof(buff) - 1, stdin) == NULL)
                {
                  printf("ERROR: fgets failed: %d\n", errno);
                  goto out;
                }
#else
              ret = readline(&buff[1], sizeof(buff) - 1, stdin, stdout);

              /* Readline normally returns the number of characters read,
               * but will return EOF on end of file or if an error occurs.  Either
               * will cause the session to terminate.
               */

              if (ret == EOF)
                {
                  printf("ERROR: readline failed: %d\n", ret);
                  goto out;
                }
#endif
              /* Send content */

              send_pkt(wl_fd);
            }
        }

#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
      if (!quit && (pfds[1].revents & POLLIN))
        {
          read_pkt(wl_fd);
        }
#endif
    }

out:
  close(wl_fd);

  printf ("Bye !\n");
  return 0;
}
