/****************************************************************************
 * apps/lte/alt1250/alt1250_reset_seq.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

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

#include <nuttx/config.h>
#include <nuttx/net/usrsock.h>

#include <sys/poll.h>
#include <sys/param.h>
#include <assert.h>
#include <ctype.h>
#include <unistd.h>

#include "alt1250_dbg.h"
#include "alt1250_devif.h"
#include "alt1250_devevent.h"
#include "alt1250_postproc.h"
#include "alt1250_container.h"
#include "alt1250_ioctl_subhdlr.h"
#include "alt1250_usrsock_hdlr.h"
#include "alt1250_reset_seq.h"
#include "alt1250_atcmd.h"
#include "alt1250_evt.h"

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

struct reset_arg_s
{
  int seq_no;
  unsigned long arg;
};

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

static struct reset_arg_s reset_arg;

static postproc_hdlr_t ponreset_seq[] =
{
  postproc_fwgetversion,
};
#define PONRESET_SEQ_NUM  nitems(ponreset_seq)

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

/****************************************************************************
 * name: postproc_ponresetseq
 ****************************************************************************/

static int postproc_ponresetseq(FAR struct alt1250_s *dev,
                                FAR struct alt_container_s *reply,
                                FAR struct usock_s *usock,
                                FAR int32_t *usock_result,
                                FAR uint32_t *usock_xid,
                                FAR struct usock_ackinfo_s *ackinfo,
                                unsigned long arg)
{
  int ret = REP_NO_ACK_WOFREE;
  FAR struct reset_arg_s *rarg = (FAR struct reset_arg_s *)arg;
  ASSERT(rarg->seq_no < PONRESET_SEQ_NUM);

  ponreset_seq[rarg->seq_no](dev, reply, usock, usock_result, usock_xid,
                             ackinfo, rarg->arg);
  rarg->seq_no++;
  if (rarg->seq_no == PONRESET_SEQ_NUM)
    {
      /* On last postproc, container should be free */

      dev->recvfrom_processing = false;
      ret = REP_NO_ACK;
      MODEM_STATE_PON(dev);
    }

  return ret;
}

/****************************************************************************
 * name: send_getversion_onreset
 ****************************************************************************/

static int send_getversion_onreset(FAR struct alt1250_s *dev,
                                   FAR struct alt_container_s *container,
                                   FAR int32_t *usock_result)
{
  reset_arg.seq_no = 0;
  reset_arg.arg = 0;

  set_container_ids(container, -1, LTE_CMDID_GETVER);
  set_container_argument(container, NULL, 0);
  set_container_response(container, alt1250_getevtarg(LTE_CMDID_GETVER), 2);
  set_container_postproc(container, postproc_ponresetseq,
                         (unsigned long)&reset_arg);

  return altdevice_send_command(dev->altfd, container, usock_result);
}

/****************************************************************************
 * name: str_toupper_case
 ****************************************************************************/

static void str_toupper_case(FAR char *data, int len)
{
  int i;

  for (i = 0; i < len; i++)
    {
      data[i] = (char)toupper(data[i]);
    }
}

/****************************************************************************
 * name: recv_atreply_onreset
 ****************************************************************************/

static int recv_atreply_onreset(atreply_parser_t parse,
                                FAR struct alt1250_s *dev,
                                FAR void *arg)
{
  int ret;
  uint64_t bitmap;
  struct pollfd fds;
  nfds_t nfds;
  FAR struct alt_container_s *rlist;
  FAR struct alt_container_s *container;
  FAR char *reply;
  int rlen;

  fds.fd = dev->altfd;
  fds.events = POLLIN;
  nfds = 1;

  ret = poll(&fds, nfds, -1);
  ASSERT(ret > 0);
  ASSERT(fds.revents & POLLIN);

  ret = altdevice_getevent(dev->altfd, &bitmap, &rlist);
  ASSERT(ret == OK);

  if (bitmap & ALT1250_EVTBIT_RESET)
    {
      /* Reset is happened again... */

      container_free_all(rlist);
      dev->sid = -1;

      ret = REP_MODEM_RESET;
    }
  else if (bitmap & ALT1250_EVTBIT_REPLY)
    {
      container = container_pick_listtop(&rlist);
      ASSERT(rlist == NULL);

      reply = (FAR char *)container->outparam[0];
      rlen = *(FAR int *)container->outparam[2];

      str_toupper_case(reply, rlen);
      ret = parse(reply, rlen, arg);
      ASSERT(ret == OK);

      ret = REP_NO_ACK;
    }
  else
    {
      ASSERT(0);  /* Should not be here */
    }

  return ret;
}

/****************************************************************************
 * name: alt1250_lwm2m_ponreset
 ****************************************************************************/

static int alt1250_lwm2m_ponreset(FAR struct alt1250_s *dev,
                                  FAR struct alt_container_s *container)
{
  int ret = REP_NO_ACK;
  int recv_ret;
  struct atreply_truefalse_s t_or_f;
  int32_t usock_result = OK;

  /* Make sure LwM2M func enabled */

  t_or_f.target_str = "\nTRUE\r";
  lwm2mstub_send_getenable(dev, container, &usock_result);
  if (usock_result == -ENOTSUP)
    {
      return REP_NO_ACK;
    }

  recv_ret = recv_atreply_onreset(check_atreply_truefalse, dev, &t_or_f);
  if (recv_ret == REP_MODEM_RESET)
    {
      return recv_ret;
    }

  if (!t_or_f.result)
    {
      lwm2mstub_send_setenable(dev, container, true);
      recv_ret = recv_atreply_onreset(check_atreply_ok, dev, NULL);
      if (recv_ret == REP_MODEM_RESET)
        {
          return recv_ret;
        }

      ret = REP_SEND_ACK;
    }

  /* Make sure LwM2M AutoConnect is disabled */

  t_or_f.target_str = "\nFALSE\r";
  lwm2mstub_send_getautoconnect(dev, container);
  recv_ret = recv_atreply_onreset(check_atreply_truefalse, dev, &t_or_f);
  if (recv_ret == REP_MODEM_RESET)
    {
      return recv_ret;
    }

  if (!t_or_f.result)
    {
      lwm2mstub_send_setautoconnect(dev, container, false);
      recv_ret = recv_atreply_onreset(check_atreply_ok, dev, NULL);
      if (recv_ret == REP_MODEM_RESET)
        {
          return recv_ret;
        }

      ret = REP_SEND_ACK;
    }

  /* Make sure LwM2M Version is 1.1 */

  t_or_f.target_str = "\n1.1\r";
  lwm2mstub_send_getversion(dev, container);
  recv_ret = recv_atreply_onreset(check_atreply_truefalse, dev, &t_or_f);
  if (recv_ret == REP_MODEM_RESET)
    {
      return recv_ret;
    }

  if (!t_or_f.result)
    {
      lwm2mstub_send_setversion(dev, container, true);
      recv_ret = recv_atreply_onreset(check_atreply_ok, dev, NULL);
      if (recv_ret == REP_MODEM_RESET)
        {
          return recv_ret;
        }

      ret = REP_SEND_ACK;
    }

  /* Make sure LwM2M NameMode is 0:UserName */

  t_or_f.target_str = "\n0\r";
  lwm2mstub_send_getnamemode(dev, container);
  recv_ret = recv_atreply_onreset(check_atreply_truefalse, dev, &t_or_f);
  if (recv_ret == REP_MODEM_RESET)
    {
      return recv_ret;
    }

  if (!t_or_f.result)
    {
      lwm2mstub_send_setnamemode(dev, container, 0);
      recv_ret = recv_atreply_onreset(check_atreply_ok, dev, NULL);
      if (recv_ret == REP_MODEM_RESET)
        {
          return recv_ret;
        }

      ret = REP_SEND_ACK;
    }

  /* Make sure NWOPER is not DEFAULT */

  t_or_f.target_str = "DEFAULT";
  ltenwop_send_getnwop(dev, container);
  recv_ret = recv_atreply_onreset(check_atreply_truefalse, dev, &t_or_f);
  if (recv_ret == REP_MODEM_RESET)
    {
      return recv_ret;
    }

  if (t_or_f.result)
    {
      ltenwop_send_setnwoptp(dev, container);
      recv_ret = recv_atreply_onreset(check_atreply_ok, dev, NULL);
      if (recv_ret == REP_MODEM_RESET)
        {
          return recv_ret;
        }

      ret = REP_SEND_ACK;
    }

  if (ret == REP_SEND_ACK)
    {
      /* Force Reset is needed. */

      altdevice_reset(dev->altfd);
    }

  dev->is_support_lwm2m = true;

  return ret;
}

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

/****************************************************************************
 * name: handle_poweron_reset_stage2
 ****************************************************************************/

int handle_poweron_reset_stage2(FAR struct alt1250_s *dev)
{
  int ret;
  int32_t unused;
  FAR struct alt_container_s *container;

  container = container_alloc();
  ASSERT(container != NULL);

  ret = send_getversion_onreset(dev, container, &unused);

  if (IS_NEED_CONTAINER_FREE(ret))
    {
      container_free(container);
    }

  if (ret > 0)
    {
      /* for blocking next usrsock request */

      ret = REP_MODEM_RESET;
      dev->recvfrom_processing = true;
    }

  return ret;
}

/****************************************************************************
 * name: handle_poweron_reset
 ****************************************************************************/

int handle_poweron_reset(FAR struct alt1250_s *dev)
{
  int ret = REP_MODEM_RESET;
  FAR struct alt_container_s *container;

  container = container_alloc();
  ASSERT(container != NULL);

  while (ret == REP_MODEM_RESET)
    {
      ret = alt1250_lwm2m_ponreset(dev, container);
    }

  container_free(container);

  MODEM_STATE_B4PON_2ND(dev);

  if (ret == REP_NO_ACK)
    {
      /* In this sequence,
       * NO_ACK means no force reset.
       * In that case, get version is needed to send here.
       */

      handle_poweron_reset_stage2(dev);
      ret = REP_MODEM_RESET;
    }

  return ret;
}
