/*
 * Copyright (c) 2017 Sprint
 *
 * Licensed 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.
 */

#include <stdlib.h>
#include <iostream>

#include "epc.h"

using namespace EPC;

std::string Utility::home_network(const char *mnc, const char *mcc) {
  std::string s;

  // '^mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org'
  s.APPEND_MNC( mnc ).APPEND_MCC( mcc ).APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::home_network(const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return home_network(mnc, mcc);
}

std::string Utility::home_network_gprs(const char *mnc, const char *mcc) {
  std::string s;

  // '^mnc(\d{3})\.mcc(\d{3})\.gprs'
  s.APPEND_MNC( mnc ).APPEND_MCC( mcc ).append("gprs");

  return s;
}

std::string Utility::home_network_gprs(const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return home_network_gprs(mnc, mcc);
}

std::string Utility::tai_fqdn(const char *lb, const char *hb, const char *mnc,
                              const char *mcc) {
  std::string s;

  // '^tac-lb([0-9a-fA-F]{2})\.tac-hb([0-9a-fA-F]{2})\.tac\.epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org'
  s.append("tac-lb").append(lb).append(".tac-hb").append(hb).append(".tac.epc.")
      .APPEND_MNC( mnc ).APPEND_MCC( mcc ).APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::tai_fqdn(const char *lb, const char *hb,
                              const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return tai_fqdn(lb, hb, mnc, mcc);
}

std::string Utility::mme_fqdn(const char *mmec, const char *mmegi,
                              const char *mnc, const char *mcc) {
  std::string s;

  // '^mmec([0-9a-fA-F]{2})\.mmegi([0-9a-fA-F]{4})\.mme.epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org'
  s.append("mmec").append(mmec).append(".mmegi").append(mmegi).append(
      ".mme.epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::mme_fqdn(const char *mmec, const char *mmegi,
                              const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return mme_fqdn(mmec, mmegi, mnc, mcc);
}

std::string Utility::mme_pool_fqdn(const char *mmegi, const char *mnc,
                                   const char *mcc) {
  std::string s;

  // '^mmegi([0-9a-fA-F]{4})\.mme\.epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org'
  s.append("mmegi").append(mmegi).append(".mme.epc.").APPEND_MNC( mnc ).
      APPEND_MCC( mcc ).APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::mme_pool_fqdn(const char *mmegi,
                                   const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return mme_pool_fqdn(mmegi, mnc, mcc);
}

std::string Utility::rai_fqdn(const char *rac, const char *lac, const char *mnc,
                              const char *mcc) {
  std::string s;

  // '^rac([0-9a-fA-F]{4})\.lac([0-9a-fA-F]{4})\.rac\.epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org'
  s.append("rac").append(rac).append(".lac").append(lac).append(".rac.epc.")
      .APPEND_MNC( mnc ).APPEND_MCC( mcc ).APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::rai_fqdn(const char *rac, const char *lac,
                              const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return rai_fqdn(rac, lac, mnc, mcc);
}

std::string Utility::rnc_fqdn(const char *rnc, const char *mnc,
                              const char *mcc) {
  std::string s;

  // '^rnc([0-9a-fA-F]{4})\.rnc\.epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org'
  s.append("rnc").append(rnc).append(".rnc.epc.").APPEND_MNC( mnc ).
      APPEND_MCC( mcc ).APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::rnc_fqdn(const char *rnc, const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return rnc_fqdn(rnc, mnc, mcc);
}

std::string Utility::sgsn_fqdn(const char *nri, const char *rac,
                               const char *lac, const char *mnc,
                               const char *mcc) {
  std::string s;

  // '^nri([0-9a-fA-F]{4})\.rac([0-9a-fA-F]{4})\.lac([0-9a-fA-F]{4})\.rac\.epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org'
  s.append("nri").append(nri).append(".rac").append(rac).append(".lac").append(
      lac).append(".rac.epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).
      APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::sgsn_fqdn(const char *nri, const char *rac,
                               const char *lac, const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return sgsn_fqdn(nri, rac, lac, mnc, mcc);
}

std::string Utility::epc_nodes_domain_fqdn(const char *mnc, const char *mcc) {
  std::string s;

  // '^node\.epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org'
  s.append("node.epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::epc_nodes_domain_fqdn(const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return epc_nodes_domain_fqdn(mnc, mcc);
}

std::string Utility::epc_node_fqdn(const char *node, const char *mnc,
                                   const char *mcc) {
  std::string s;

  // '^(.+)\.node\.epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org$'
  s.append(node).append(".node.epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).
      APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::epc_node_fqdn(const char *node,
                                   const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return epc_node_fqdn(node, mnc, mcc);
}

std::string Utility::nonemergency_epdg_oi_fqdn(const char *mnc,
                                               const char *mcc) {
  std::string s;

  // '^epdg\.epc\.mnc(\d{3})\.mcc(\d{3})\.pub\.3gppnetworks\.org'
  s.append("epdg.epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).append("pub.")
      .APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::nonemergency_epdg_oi_fqdn(const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return nonemergency_epdg_oi_fqdn(mnc, mcc);
}

std::string Utility::nonemergency_epdg_tai_fqdn(const char *lb, const char *hb,
                                                const char *mnc,
                                                const char *mcc) {
  std::string s;

  // '^tac-lb([0-9a-fA-F]{2})\.tac-hb([0-9a-fA-F]{2})\.tac\.epdg\.epc\.mnc(\d{3})\.mcc(\d{3})\.pub\.3gppnetworks\.org'
  s.append("tac-lb").append(lb).append(".tac-hb").append(hb).append(
      ".tac.epdg.epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::nonemergency_epdg_tai_fqdn(const char *lb, const char *hb,
                                                const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return nonemergency_epdg_tai_fqdn(lb, hb, mnc, mcc);
}

std::string Utility::nonemergency_epdg_lac_fqdn(const char *lac,
                                                const char *mnc,
                                                const char *mcc) {
  std::string s;

  // '^lac([0-9a-fA-F]{4})\.epdg\.epc\.mnc(\d{3})\.mcc(\d{3})\.pub\.3gppnetworks\.org'
  s.append("lac").append(lac).append(".epdg.epc.").APPEND_MNC( mnc ).
      APPEND_MCC( mcc ).append("pub.").APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::nonemergency_epdg_lac_fqdn(const char *lac,
                                                const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return nonemergency_epdg_lac_fqdn(lac, mnc, mcc);
}

std::string Utility::nonemergency_epdg_visitedcountry_fqdn(const char *mcc) {
  std::string s;

  // '^epdg\.epc\.mcc(\d{3})\.visited-country\.pub\.3gppnetworks\.org'
  s.append("epdg.epc.").APPEND_MCC( mcc ).append("visited-country.pub.")
      .APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::nonemergency_epdg_visitedcountry_fqdn(
    const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return nonemergency_epdg_visitedcountry_fqdn(mcc);
}

std::string Utility::emergency_epdg_oi_fqdn(const char *mnc, const char *mcc) {
  std::string s;

  // '^sos\.epdg\.epc\.mnc(\d{3})\.mcc(\d{3})\.pub\.3gppnetworks\.org'
  s.append("sos.epdg.epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).append("pub.")
      .APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::emergency_epdg_oi_fqdn(const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return emergency_epdg_oi_fqdn(mnc, mcc);
}

std::string Utility::emergency_epdg_tai_fqdn(const char *lb, const char *hb,
                                             const char *mnc, const char *mcc) {
  std::string s;

  // '^tac-lb([0-9a-fA-F]{2})\.tac-hb([0-9a-fA-F]{2})\.tac\.sos\.epdg\.epc\.mnc(\d{3})\.mcc(\d{3})\.pub\.3gppnetworks\.org',
  s.append("tac-lb").append(lb).append(".tac-hb").append(hb).append(
      ".tac.sos.epdg.epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).append("pub.")
      .APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::emergency_epdg_tai_fqdn(const char *lb, const char *hb,
                                             const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return emergency_epdg_tai_fqdn(lb, hb, mnc, mcc);
}

std::string Utility::emergency_epdg_lac_fqdn(const char *lac, const char *mnc,
                                             const char *mcc) {
  std::string s;

  // '^lac([0-9a-fA-F]{4})\.sos\.epdg\.epc\.mnc(\d{3})\.mcc(\d{3})\.pub\.3gppnetworks\.org'
  s.append("lac").append(lac).append(".sos.epdg.epc.").APPEND_MNC( mnc ).
      APPEND_MCC( mcc ).append("pub.").APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::emergency_epdg_lac_fqdn(const char *lac,
                                             const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return emergency_epdg_lac_fqdn(lac, mnc, mcc);
}

std::string Utility::emergency_epdg_visitedcountry_fqdn(const char *mcc) {
  std::string s;

  // '^sos\.epdg\.epc\.mcc(\d{3})\.visited-country\.pub\.3gppnetworks\.org'
  s.append("sos.epdg.epc.").APPEND_MCC( mcc ).append("visited-country.pub.")
      .APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::emergency_epdg_visitedcountry_fqdn(
    const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return emergency_epdg_visitedcountry_fqdn(mcc);
}

std::string Utility::global_enodeb_id_fqdn(const char *enb, const char *mcc) {
  std::string s;

  // '^enb([0-9a-fA-F]{4})\.enb\.epc\.mcc(\d{3})\.visited-country\.pub\.3gppnetworks\.org'
  s.append("enb").append(enb).append(".enb.epc.").APPEND_MCC( mcc ).append(
      "visited-country.pub.").APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::global_enodeb_id_fqdn(const char *enb,
                                           const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return global_enodeb_id_fqdn(enb, mcc);
}

std::string Utility::local_homenetwork_fqdn(const char *lhn, const char *mcc) {
  std::string s;

  // '^lhn(.+)\.lhn\.epc\.mcc(\d{3})\.visited-country\.pub\.3gppnetworks\.org$'
  s.append("lhn").append(lhn).append(".lhn.epc.").APPEND_MCC( mcc ).append(
      "visited-country.pub.").APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::local_homenetwork_fqdn(const char *lhn,
                                            const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return local_homenetwork_fqdn(lhn, mcc);
}

std::string Utility::epc(const char *mnc, const char *mcc) {
  std::string s;

  // '^epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org'
  s.append("epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::epc(const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return epc(mnc, mcc);
}

std::string Utility::apn_fqdn(const char *apnoi, const char *mnc,
                              const char *mcc) {
  std::string s;

  // '(.+)\.apn\.epc\.mnc(\d{3})\.mcc(\d{3})\.3gppnetworks\.org$'
  s.append(apnoi).append(".apn.epc.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).
      APPEND_3GPPNETWORK;

  return s;
}

std::string Utility::apn_fqdn(const char *apnoi, const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return apn_fqdn(apnoi, mnc, mcc);
}

std::string Utility::apn(const char *apnoi, const char *mnc, const char *mcc) {
  std::string s;

  // '(.+)\.apn\.mnc(\d{3})\.mcc(\d{3})\.gprs$'}
  s.append(apnoi).append(".apn.").APPEND_MNC( mnc ).APPEND_MCC( mcc ).append(
      "gprs");

  return s;
}

std::string Utility::apn(const char *apnoi, const unsigned char *plmnid) {
  PARSE_PLMNID(plmnid);
  return apn(apnoi, mnc, mcc);
}

std::string Utility::apn_label(const std::string &apn) {
  std::string apn_label = { };
  bool to_count = true;
  uint8_t counted = 0;
  int index = 0;

  apn_label.push_back('?');

  for (int i = 0; i < apn.length(); ++i) {
    if (isalnum(apn[i]) || (apn[i] == '-')) {
      apn_label.push_back(apn[i]);
      counted++;
    } else if (apn[i] == '.') {
      apn_label.push_back('?');
      if (to_count) {  // always true
        apn_label[index] = counted;
      }
      to_count = true;
      counted = 0;
      index = apn_label.length() - 1;
    }
  }
  if (to_count) {
    apn_label[index] = counted;
  }
  return apn_label;
}

