/******************************************************************************/
// @copyright   Copyright Notification
//   No part may be reproduced except as authorized by written permission.
//   The copyright and the foregoing restriction extend to reproduction in all media.
//   Trademark 2010, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC).
//   All rights reserved.
// @version: IWD_10wk37
// $Date: 2010-06-11 17:02:25 +0200 (Fri, 11 Jun 2010) $
// $Rev: 3355 $
/******************************************************************************/

module IP_PTC_Routing {

  //import from CommonDefs all;
  import from IP_ASP_TypeDefs all;
  import from IP_PTC_Component all;

  //----------------------------------------------------------------------------

  group RoutingTableManagement {

  function fl_IpInfo2MatchingInfo(template (omit) IP_Socket_Type p_SocketInfo) return template IP_Socket_Type
  { /* converts the given information into a template which can be matched */

    var template IP_Socket_Type v_SocketInfo := p_SocketInfo;

    if (isvalue(v_SocketInfo)) {
      v_SocketInfo := *;        // omit it means that the addr is "don't care"
    } else {
      if (not ispresent(v_SocketInfo.IpAddr)) {
        v_SocketInfo.IpAddr := *;
      }
      if (not ispresent(v_SocketInfo.Port)) {
        v_SocketInfo.Port := *;
      }
    }
    return v_SocketInfo;
  }

  function fl_IpInfoMatch(IP_Connection_Type p_IpInfoValue,
                          template (value) IP_Connection_Type p_IpInfoToMatch) return boolean
  { /* checks whether p_IpInfoValue matches against p_IpInfoToMatch taking into accoun that in IP_Connection_Type
     * fields may be omit what means don't care */
    var template IP_Connection_Type v_IpInfoToMatch := p_IpInfoToMatch;

    v_IpInfoToMatch.Local  := fl_IpInfo2MatchingInfo(p_IpInfoToMatch.Local);
    v_IpInfoToMatch.Remote := fl_IpInfo2MatchingInfo(p_IpInfoToMatch.Remote);

    return match(p_IpInfoValue, v_IpInfoToMatch);
  }

  function fl_RoutingTable_CheckIpInfoForConflict(IP_RoutingTable_Type p_RoutingTable,
                                                  IP_Connection_Type p_IpInfo) return boolean
  { /* local function to check the given IP info against the routing table
     * Note: in general the IP_Connection is not fully specified; if a field in IP_Connection is omit this means it is "don't care";
     *       therefore a IpInfo maybe subset of another IpInfo and vice versa
     * return
     * false, when for any entry of the RoutingTable IpInfo is equal to the given IpInfo or is a superset or is a subset
     * true else */
    var IP_Connection_Type v_TableEntryIpInfo;
    var integer i;
    for (i := 0; i < lengthof(p_RoutingTable); i := i+1) {
      v_TableEntryIpInfo := p_RoutingTable[i].IpInfo;
      if (fl_IpInfoMatch(v_TableEntryIpInfo, p_IpInfo) or fl_IpInfoMatch(p_IpInfo, v_TableEntryIpInfo)) {
        return false;
      }
    }
    return true;
  }

  function f_RoutingTable_AddEntry(IP_RoutingTable_Type p_RoutingTable,
                                   IP_Connection_Type p_IpInfo,
                                   IP_DrbInfo_Type p_DrbInfo) return IP_RoutingTable_Type
  { /* add a new entry to the routing table;
     * firstly the new RoutingInfo is checked whether there is any conflict with existing entries
     * in which case it would not be added; => RoutingTable is assumed to be free of conflicts */
    var IP_RoutingTable_Type v_RoutingTable := p_RoutingTable;
    var integer v_TableSize;

    if (not fl_RoutingTable_CheckIpInfoForConflict(p_RoutingTable, p_IpInfo)) {
      f_IP_PTC_Error(__FILE__, __LINE__, "conflict for routing table");
    } else {
      v_TableSize := lengthof(p_RoutingTable);
      v_RoutingTable[v_TableSize] := {
        IpInfo := p_IpInfo,
        DRB := p_DrbInfo
      };
    }
    return v_RoutingTable;
  }

  function fl_RoutingTable_FindEntry(IP_RoutingTable_Type p_RoutingTable,
                                     IP_Connection_Type p_IpInfo) return integer
  { /* return index of the entry in the routing table or -1 if no entry has been found;
     * Note: the IP_Connection info does not need to be fully specified;
     *       nevertheless when refering to an entry in the routing table the IP_Connection shall be exactly the same */
    var IP_RoutingInfo_Type v_CurrentEntry;
    var integer v_TableSize := lengthof(p_RoutingTable);
    var integer i;

    for (i := 0; i < v_TableSize; i := i+1) {
      v_CurrentEntry := p_RoutingTable[i];
      if (v_CurrentEntry.IpInfo == p_IpInfo) {
        return i;
      }
    }
    return -1;
  }

  function f_RoutingTable_RemoveEntry(IP_RoutingTable_Type p_RoutingTable,
                                      IP_Connection_Type p_IpInfo) return IP_RoutingTable_Type
  {
    var integer v_TableSize := lengthof(p_RoutingTable);
    var integer v_Index := fl_RoutingTable_FindEntry(p_RoutingTable, p_IpInfo);
    var IP_RoutingTable_Type v_RoutingTable := {};
    var integer i;
    var integer k;

    if (v_Index < 0) {
      return p_RoutingTable;
    }
    k := 0;
    for (i := 0; i < v_TableSize; i := i+1) {
      if (i != v_Index) {
        v_RoutingTable[k] := p_RoutingTable[i];
        k := k + 1;
      }
    }
    return v_RoutingTable;
  }

  function f_RoutingTable_ModifyEntry(IP_RoutingTable_Type p_RoutingTable,
                                      IP_Connection_Type p_IpInfo,
                                      IP_DrbInfo_Type p_DrbInfo) return IP_RoutingTable_Type
  {
    var integer v_Index := fl_RoutingTable_FindEntry(p_RoutingTable, p_IpInfo);
    var IP_RoutingTable_Type v_RoutingTable;

    if (v_Index < 0) {
      return p_RoutingTable;
    }
    v_RoutingTable := p_RoutingTable;
    v_RoutingTable[v_Index].DRB := p_DrbInfo;
    return v_RoutingTable;
  }

  } // end of group RoutingTableManagement

  //----------------------------------------------------------------------------

  group DRBMUX_CONFIG {

  template (value) DRBMUX_CONFIG_REQ cs_DRBMUX_CONFIG_REQ(IP_RoutingTable_Type p_RoutingTable) :=
  {
    RoutingInfo := p_RoutingTable
  };

  template DRBMUX_COMMON_IND_CNF cr_DRBMUX_COMMON_IND_CNF :=
  {
    Confirm := true
  };

  function f_IP_PTC_SendRoutingTable(IP_RoutingTable_Type p_RoutingTable) runs on IP_PTC
  { /* Note: there is no default behaviour activated for the IP_PTC
     *       => waiting for the SA's response is blocking (what helps to avoid enexpected behaviour) */
    IP_CTRL.send(cs_DRBMUX_CONFIG_REQ(p_RoutingTable));
    IP_CTRL.receive(cr_DRBMUX_COMMON_IND_CNF);
  }

  } // end of group DRBMUX_CONFIG


}
