/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (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.openairinterface.org/?page_id=698
 *
 * 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.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*! \file openair1/PHY/TOOLS/phy_scope_interface.c
 * \brief soft scope API interface implementation
 * \author Nokia BellLabs France, francois Taburet
 * \date 2019
 * \version 0.1
 * \company Nokia BellLabs France
 * \email: francois.taburet@nokia-bell-labs.com
 * \note
 * \warning
 */
#include <stdio.h>
#include "common/config/config_userapi.h"
#include "common/utils/load_module_shlib.h"
#include "phy_scope_interface.h"

#define SOFTSCOPE_ENDFUNC_IDX 0
#define THREAD_MEM 4

static  loader_shlibfunc_t scope_fdesc[]= {{"end_forms",NULL}};

pthread_mutex_t UEcopyDataMutex;

int UEcopyDataMutexInit(void) {
  return pthread_mutex_init(&UEcopyDataMutex, NULL);
}

int load_softscope(char *exectype, void *initarg) {
  char libname[64];
  sprintf(libname,"%.10sscope",exectype);
  return load_module_shlib(libname,scope_fdesc,1,initarg);
}

int end_forms(void) {
  if (scope_fdesc[SOFTSCOPE_ENDFUNC_IDX].fptr) {
    scope_fdesc[SOFTSCOPE_ENDFUNC_IDX].fptr();
    return 0;
  }

  return -1;
}

void UEcopyData(PHY_VARS_NR_UE *ue, enum UEdataType type, void *dataIn, int elementSz, int colSz, int lineSz) {
  // Local static copy of the scope data bufs
  // The active data buf is alterned to avoid interference between the Scope thread (display) and the Rx thread (data input)
  // Index of THREAD_MEM could be set to the number of Rx threads + 1. Rx slots could run asynchronous to each other.
  // THREAD_MEM = 4 slot process running in parallel is an assumption. THREAD_MEM can be increased if scope appears inconsistent.
  static scopeGraphData_t *copyDataBufs[UEdataTypeNumberOfItems][THREAD_MEM] = {0};
  static int  copyDataBufsIdx[UEdataTypeNumberOfItems] = {0};

  scopeData_t *tmp = (scopeData_t *)ue->scopeData;

  if (tmp) {
    // Begin of critical zone between UE Rx threads that might copy new data at the same time:
    pthread_mutex_lock(&UEcopyDataMutex);
    int newCopyDataIdx = (copyDataBufsIdx[type]<(THREAD_MEM-1))?copyDataBufsIdx[type]+1:0;
    copyDataBufsIdx[type] = newCopyDataIdx;
    pthread_mutex_unlock(&UEcopyDataMutex);
    // End of critical zone between UE Rx threads

    // New data will be copied in a different buffer than the live one
    scopeGraphData_t *copyData = copyDataBufs[type][newCopyDataIdx];

    if (copyData == NULL || copyData->dataSize < elementSz*colSz*lineSz) {
      scopeGraphData_t *ptr = (scopeGraphData_t*) realloc(copyData, sizeof(scopeGraphData_t) + elementSz*colSz*lineSz);

      if (!ptr) {
        LOG_E(PHY,"can't realloc\n");
        return;
      } else {
        copyData = ptr;
      }
    }

    copyData->dataSize = elementSz*colSz*lineSz;
    copyData->elementSz = elementSz;
    copyData->colSz = colSz;
    copyData->lineSz = lineSz;
    memcpy(copyData+1, dataIn,  elementSz*colSz*lineSz);
    copyDataBufs[type][newCopyDataIdx] = copyData;

    // The new data just copied in the local static buffer becomes live now
    ((scopeGraphData_t **)tmp->liveData)[type] = copyData;
  }
}
