/*
 * 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
 *
 * Author and copyright: Laurent Thomas, open-cells.com
 *
 * 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
 */
#include <openair3/NAS/COMMON/NR_NAS_defs.h>
#include <openair3/SECU/secu_defs.h>


void SGSabortNet(void *msg, nr_user_nas_t *UE) {
}

void ue_nas_schedule(void) {
}

/*
 *Message reception
 */

void SGSauthenticationReq(void *msg, nr_user_nas_t *UE) {
  authenticationrequestHeader_t *amsg=(authenticationrequestHeader_t *) msg;
  arrayCpy(UE->uicc->rand,amsg->RAND);
  arrayCpy(UE->uicc->autn,amsg->AUTN);
  // AUTHENTICATION REQUEST message that contains a valid ngKSI, SQN and MAC is received
  // TBD verify ngKSI (we set it as '2', see gNB code)
  // SQN and MAC are tested in auth resp processing
  ue_nas_schedule();
}

void SGSidentityReq(void *msg, nr_user_nas_t *UE) {
  Identityrequest_t *idmsg=(Identityrequest_t *) msg;

  if (idmsg->it == SUCI ) {
    LOG_I(NAS,"Received Identity request, scheduling answer\n");
    ue_nas_schedule();
  } else
    LOG_E(NAS,"Not developped: identity request for %d\n", idmsg->it);
}

void SGSsecurityModeCommand(void *msg, nr_user_nas_t *UE) {
}


void UEprocessNAS(void *msg,nr_user_nas_t *UE) {
  SGScommonHeader_t *header=(SGScommonHeader_t *) msg;

  if ( header->sh > 4 )
    SGSabortNet(msg, UE);
  else {
    switch  (header->epd) {
      case SGSmobilitymanagementmessages:
        LOG_I(NAS,"Received message: %s\n", idStr(message_text_info, header->mt));

        switch (header->mt) {
          case Authenticationrequest:
            SGSauthenticationReq(msg, UE);
            break;

          case Identityrequest:
            SGSidentityReq(msg, UE);
            break;

          case Securitymodecommand:
            SGSsecurityModeCommand(msg, UE);
            break;

          default:
            SGSabortNet(msg, UE);
        }

        break;

      case SGSsessionmanagementmessages:
        SGSabortNet(msg, UE);
        break;

      default:
        SGSabortNet(msg, UE);
    }
  }
}

/*
 * Messages emission
 */

int identityResponse(void **msg, nr_user_nas_t *UE) {
  if (UE->uicc == NULL)
    // config file section hardcoded as "uicc", nevertheless it opens to manage several UEs or a multi SIM UE
    UE->uicc=init_uicc("uicc");

  // TS 24.501 9.11.3.4
  int imsiL=strlen(UE->uicc->imsiStr);
  int msinL=imsiL-3-UE->uicc->nmc_size;
  int respSize=sizeof(IdentityresponseIMSI_t) + (msinL+1)/2;
  IdentityresponseIMSI_t *resp=(IdentityresponseIMSI_t *) calloc(respSize,1);
  resp->common.epd=SGSmobilitymanagementmessages;
  resp->common.sh=0;
  resp->common.mt=Identityresponse;
  resp->common.len=htons(respSize-sizeof(Identityresponse_t));
  resp->mi=SUCI;
  resp->mcc1=UE->uicc->imsiStr[0]-'0';
  resp->mcc2=UE->uicc->imsiStr[1]-'0';
  resp->mcc3=UE->uicc->imsiStr[2]-'0';
  resp->mnc1=UE->uicc->imsiStr[3]-'0';
  resp->mnc2=UE->uicc->imsiStr[4]-'0';
  resp->mnc3=UE->uicc->nmc_size==2? 0xF : UE->uicc->imsiStr[3]-'0';
  // TBD: routing to fill (FF ?)
  char *out=(char *)(resp+1);
  char *ptr=UE->uicc->imsiStr + 3 + UE->uicc->nmc_size;

  while ( ptr < UE->uicc->imsiStr+strlen(UE->uicc->imsiStr) ) {
    *out=((*(ptr+1)-'0')<<4) | (*(ptr) -'0');
    out++;
    ptr+=2;
  }

  if (msinL%2 == 1)
    *out=((*(ptr-1)-'0')) | 0xF0;

  *msg=resp;
  return respSize;
}

int authenticationResponse(void **msg,nr_user_nas_t *UE) {
  if (UE->uicc == NULL)
    // config file section hardcoded as "uicc", nevertheless it opens to manage several UEs or a multi SIM UE
    UE->uicc=init_uicc("uicc");

  myCalloc(resp, authenticationresponse_t);
  resp->epd=SGSmobilitymanagementmessages;
  resp->sh=0;
  resp->mt=Authenticationresponse;
  resp->iei=IEI_AuthenticationResponse;
  resp->RESlen=sizeof(resp->RES); // always 16 see TS 24.501 Table 8.2.2.1.1
  // Verify the AUTN
  // a full implementation need to test several SQN
  // as the last 5bits can be any value
  // and the value can also be greater and accepted (if it is in the accepted window)
  uint8_t AUTN[16];
  uicc_milenage_generate(AUTN, UE->uicc);

  if ( memcmp(UE->uicc->autn, AUTN, sizeof(AUTN)) == 0 ) {
    // prepare and send good answer
    resToresStar(resp->RES,UE->uicc);
  } else {
    // prepare and send autn is not compatible with our data
    abort();
  }

  *msg=resp;
  return sizeof(authenticationresponse_t);
}

int securityModeComplete(void **msg, nr_user_nas_t *UE) {
  return -1;
}

int registrationComplete(void **msg, nr_user_nas_t *UE) {
  return -1;
}