/** @file

  A brief file description

  @section license License

  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.
 */

/*****************************************************************************
 * Filename: EventRegistration.cc
 * Purpose: This file contains functions and structures used in event
 *          notification and callbacks for remote clients; also has the
 *          thread that services event notification.
 * Created: 2/15/01
 *
 *
 ***************************************************************************/

#include "libts.h"
#include <stdio.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <string.h>

#include "ink_thread.h"

#include "MgmtSocket.h"

#include "mgmtapi.h"
#include "EventRegistration.h"
#include "CoreAPIShared.h"
#include "NetworkUtilsDefs.h"
#include "NetworkUtilsRemote.h"
#include "EventCallback.h"

CallbackTable *remote_event_callbacks;

/**********************************************************************
 * event_poll_thread_main
 *
 * purpose: thread listens on the client's event socket connection;
 *          only reads from the event_socket connection and
 *          processes EVENT_NOTIFY messages; each time client
 *          makes new event-socket connection to TM, must launch
 *          a new event_poll_thread_main thread
 * input:   arg - contains the socket_fd to listen on
 * output:  NULL - if error
 * notes:   each time the client's socket connection to TM is reset
 *          a new thread will be launched as old one dies; there are
 *          only two places where a new thread is created:
 *          1) when client first connects (TSInit call)
 *          2) client reconnects() due to a TM restart
 * Uses blocking socket; so blocks until receives an event notification.
 * Shouldn't need to use select since only waiting for a notification
 * message from event_callback_main thread!
 **********************************************************************/
void *
event_poll_thread_main(void *arg)
{
  TSMgmtError err;
  int sock_fd;
  TSMgmtEvent *event_notice = NULL;

  sock_fd = *((int *) arg);     // should be same as event_socket_fd

  // the sock_fd is going to be the one we listen for events on
  while (1) {
    // possible sock_fd is invalid if TM restarts and client reconnects
    if (sock_fd < 0) {
      break;
    }

    // read the entire message, so create TSMgmtEvent for the callback
    event_notice = TSEventCreate();
    err = parse_event_notification(sock_fd, event_notice);
    if (err == TS_ERR_NET_READ || err == TS_ERR_NET_EOF) {
      break;
    } else if (err != TS_ERR_OKAY) {
      TSEventDestroy(event_notice);
      continue;                 // skip the message
    }
    // got event notice; spawn new thread to handle the event's callback functions
    ink_thread_create(event_callback_thread, (void *) event_notice, 0, DEFAULT_STACK_SIZE);
  }

  if (event_notice)
    TSEventDestroy(event_notice);
  ink_thread_exit(NULL);
  return NULL;
}

/**********************************************************************
 * event_callback_thread
 *
 * purpose: Given an event, determines and calls the registered cb functions
 *          in the CallbackTable for remote events
 * input: arg - should be an TSMgmtEvent with the event info sent from TM msg
 * output: returns when done calling all the callbacks
 * notes: None
 **********************************************************************/
void *
event_callback_thread(void *arg)
{
  TSMgmtEvent *event_notice;
  EventCallbackT *event_cb;
  int index;

  event_notice = (TSMgmtEvent *) arg;
  index = (int) event_notice->id;
  LLQ *func_q;                  // list of callback functions need to call

  func_q = create_queue();
  if (!func_q) {
    if (event_notice)
      TSEventDestroy(event_notice);
    return NULL;
  }

  // obtain lock
  ink_mutex_acquire(&remote_event_callbacks->event_callback_lock);

  TSEventSignalFunc cb;

  // check if we have functions to call
  if (remote_event_callbacks->event_callback_l[index] && (!queue_is_empty(remote_event_callbacks->event_callback_l[index]))) {
    int queue_depth = queue_len(remote_event_callbacks->event_callback_l[index]);

    for (int i = 0; i < queue_depth; i++) {
      event_cb = (EventCallbackT *) dequeue(remote_event_callbacks->event_callback_l[index]);
      cb = event_cb->func;
      enqueue(remote_event_callbacks->event_callback_l[index], event_cb);
      enqueue(func_q, (void *) cb);     // add callback function only to list
    }
  }
  // release lock
  ink_mutex_release(&remote_event_callbacks->event_callback_lock);

  // execute the callback function
  while (!queue_is_empty(func_q)) {
    cb = (TSEventSignalFunc) dequeue(func_q);
    (*cb) (event_notice->name, event_notice->description, event_notice->priority, NULL);
  }

  // clean up event notice
  TSEventDestroy(event_notice);
  delete_queue(func_q);

  // all done!
  ink_thread_exit(NULL);
  return NULL;
}
