#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int N = 1e4 + 10;
//四个正整数和一个字符串,分别是:、、、 和 ,
int n, m, t_def, t_max, t_min;
string h;
struct IP
{
int state; //0:未分配, 1:待分配, 2:占用, 3过期
int t; //过期时间
string owner; //占用者
}ip[N];
void update_ip(int t) {
/*处于待分配和占用状态的 IP 地址拥有一个大于零的过期时刻。
在到达该过期时刻时,
若该地址的状态是待分配,则该地址的状态会自动变为未分配,且占用者清空,过期时刻清零;
否则该地址的状态会由占用自动变为过期,且过期时刻清零。
处于未分配和过期状态的 IP 地址过期时刻为零,即没有过期时刻。*/
for (size_t i = 1; i <= n; i++){
if (ip[i].state == 1 && ip[i].t && ip[i].t <= t) {
ip[i].state = 0;
ip[i].owner = "";
ip[i].t = 0;
}
else if (ip[i].state == 2 && ip[i].t && ip[i].t <= t) {
ip[i].state = 3;
ip[i].t = 0;
}
else if (ip[i].state == 0 || ip[i].state == 3) { //细节
ip[i].t = 0;
}
}
}
int get_ip_by_owner(string owner) {
for (size_t i = 1; i <= n; i++)
{
if (ip[i].owner == owner)
{
return i;
}
}
return 0;
}
int get_ip_by_state(int state) {
for (size_t i = 1; i <= n; i++)
{
if (ip[i].state == state) {
return i;
}
}
return 0;
}
int main() {
cin >> n >> t_def >> t_max >> t_min >> h;
cin >> m;
while (m--){
//第 个报文是在 时刻收到
int t;
string client, server, type;
int id, te;//过期时刻
cin >> t >> client >> server >> type >> id >> te;
//否为本机,为 *,若不是,则判断类型是否为 Request,若不是,则不处理;
if (server != h && server != "*") {
if(type != "REQ")
continue;
}
//若类型不是 Discover、Request 之一,则不处理;
if (type != "DIS"&&type != "REQ") continue;
//若接收主机为 *,但类型不是 Discover,或接收主机是本机,但类型是 Discover,则不处理。
if (server == "*"&&type != "DIS") continue;
if (server == h && type == "DIS") continue;
update_ip(t);
//对于 Discover 报文,按照下述方法处理:
if (type == "DIS") {
/*检查是否有占用者为发送主机的 IP 地址:
若有,则选取该 IP 地址;
若没有,则选取最小的状态为未分配的 IP 地址;
若没有,则选取最小的状态为过期的 IP 地址;
若没有,则不处理该报文,处理结束;*/
int k = get_ip_by_owner(client);
if (!k) k = get_ip_by_state(0);
if (!k) k = get_ip_by_state(3);
if (!k) continue;
//将该 IP 地址状态设置为待分配,占用者设置为发送主机;
ip[k].state = 1;
ip[k].owner = client;
/*
若报文中过期时刻为 0 ,则设置过期时刻为 t + t_def ;t
否则根据报文中的过期时刻和收到报文的时刻计算过期时间,
判断是否超过上下限:
若没有超过,则设置过期时刻为报文中的过期时刻;
否则则根据超限情况设置为允许的最早或最晚的过期时刻;*/
if (te == 0) {
te = t + t_def;
}
else {
if (te > t + t_max) te = t + t_max;
else if (te < t + t_min) te = t + t_min;
}
ip[k].t = te;//记得加过期时间
//向发送主机发送 Offer 报文,其中,IP 地址为选定的 IP 地址,过期时刻为所设定的过期时刻。
printf("%s %s OFR %d %d\n", h.c_str(), client.c_str(), k, te);
}
//对于 Request 报文,按照下述方法处理
if (type == "REQ") {
/*
检查接收主机是否为本机:
若不是,则找到占用者为发送主机的所有 IP 地址,
对于其中状态为待分配的,将其状态设置为未分配,并清空其占用者,清零其过期时刻,处理结束;
*/
if (server != h) {
for (size_t i = 1; i <= n; i++){
if (ip[i].owner == client) {
if (ip[i].state == 1) {
ip[i].state = 0;
ip[i].owner = "";
ip[i].t = 0;
}
}
}
continue;
}
/*
检查报文中的 IP 地址是否在地址池内,且其占用者为发送主机,
若不是,则向发送主机发送 Nak 报文,处理结束;*/
if (id > n || id < 0 || ip[id].owner != client) {
//dhcp b NAK 2 0
printf("%s %s NAK %d 0\n", h.c_str(), client.c_str(), id);
continue;
}
/*
无论该 IP 地址的状态为何,将该 IP 地址的状态设置为占用;
与 Discover 报文相同的方法,设置 IP 地址的过期时刻;
向发送主机发送 Ack 报文。*/
ip[id].state = 2;
if (te == 0) { //注意超时定义
te = t + t_def;
}
else {
if (te > t + t_max) te = t + t_max;
else if (te < t + t_min) te = t + t_min;
}
ip[id].t = te;
//dhcp c ACK 3 20
printf("%s %s ACK %d %d\n", h.c_str(), client.c_str(), id, te);
}
}
return 0;
}