//-----------------------------------------------------------------------------
// Purpose: Updates the current sequence info with the new packet
//			scans the message window to see if future packets can be used
//-----------------------------------------------------------------------------
void CTrackerNET::UpdateSequence(NetworkTarget_t *target, int targetIndex, int seqNum)
{
	// update sequence
	target->incomingSequence = seqNum;

	// check to see when we should send acknowledgement
	if (!target->needAck)
	{
		target->ackTime = m_pThreads->GetTime() + ACK_DELAY;
		target->needAck = true;

		// move the target to the end of the ack list
		m_TargetList.Unlink(targetIndex);

		if (m_TargetList.IsValidIndex(m_iHighestAckTarget) && m_TargetList.IsInList(m_iHighestAckTarget))
		{
			m_TargetList.LinkAfter(m_iHighestAckTarget, targetIndex);
		}
		else
		{
			m_TargetList.LinkToHead(targetIndex);
		}

		// set the need high mark for the packets that need ack
		m_iHighestAckTarget = targetIndex;
	}

	// check to see if packets in the window are now valid
	while (target->m_MessageWindow.Count())
	{
		int index = target->m_MessageWindow.Head();
		
		if (target->m_MessageWindow[index].sequenceNumber == target->incomingSequence + 1)
		{
			// this is the next message

			// update sequence numbers
			target->incomingSequence = target->m_MessageWindow[index].sequenceNumber;

			// add message to received list
			int receivedIndex = m_ReceivedMsgs.AddToTail();
			m_ReceivedMsgs[receivedIndex].message = target->m_MessageWindow[index].message;

			WriteToLog("-> Recv match in window (is: %d)\n", target->m_MessageWindow[index].sequenceNumber);

			// remove message from window
			target->m_MessageWindow.Remove(index);
		}
		else
		{
			// no more
			break;
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if we need to send any ack packets 
//			in response to reliable messages we've received
//-----------------------------------------------------------------------------
void CTrackerNET::CheckSendingAcknowledgements()
{
	float time = (float)m_pThreads->GetTime();

	// the target list is in order of ackTime's
	while (m_TargetList.Count())
	{
		int targetIndex = m_TargetList.Head();
		NetworkTarget_t &target = m_TargetList[targetIndex];

		if (!target.needAck)
			break;	// target does not need acknowledging, we're at the end of the list

		if (target.ackTime > time)
			break;	// this packet not yet ready to be acknowledged

		if (target.incomingSequence > target.incomingAcknowledged)
		{
			// send an acknowledgement
			ISendMessage *pMsg = CreateMessage(TMSG_ACK);
			pMsg->SetNetAddress(target.netAddress);
			SendMessage(pMsg, NET_UNRELIABLE);
		}

		// this target has been acknowledged, move to the end of the list
		target.needAck = false;
		m_TargetList.Unlink(targetIndex);
		m_TargetList.LinkToTail(targetIndex);

		// check to see if the highest ack target needs to be reset
		if (targetIndex == m_iHighestAckTarget)
		{
			m_iHighestAckTarget = -1;
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if we should resend any reliable packets
//-----------------------------------------------------------------------------
void CTrackerNET::CheckReliablePacketSending()
{
	float time = m_pThreads->GetTime();

	// reliable packets are held in the m_ReliableMessages queue in the order that they were sent
	// if a packet is resent, it is moved to the end of the queue
	// packets are also checked at this time to see if they have been acknowledged
	while (m_ReliableMessages.Count())
	{
		// get the first message
		int index = m_ReliableMessages.Head();

		// get the message
		SentMessage_t &msg = m_ReliableMessages[index];

		// check the resend time, if it's not time to resend then we have nothing more to do 
		if (msg.resendTime > time)
			break;	

		// get the network target for this message
		if (!m_TargetList.IsValidIndex(msg.networkTargetHandle) 
			|| !m_TargetList.IsInList(msg.networkTargetHandle) 
			|| m_TargetList[msg.networkTargetHandle].targetID != msg.networkTargetID)
		{
			// message's target has been removed, kill
			if (msg.message)
			{
				msg.message->deleteThis();
			}
			m_ReliableMessages.Remove(index);
			continue;
		}

		NetworkTarget_t &target = m_TargetList[msg.networkTargetHandle];

		// check to see if it's already been acknowledged
		if (msg.sequenceNumber <= target.outgoingAcknowledged)
		{
			// message has been acknowledged, kill
			if (msg.message)
			{
				msg.message->deleteThis();
			}
			m_ReliableMessages.Remove(index);

			// move onto next message
			continue;
		}

		// check to see if we should resend this packet
		msg.resendAttempt++;
		if (msg.resendAttempt < MAX_SEND_ATTEMPTS)
		{
			// only send the message if it's in the message send window
			if (msg.sequenceNumber < target.outgoingAcknowledged + MESSAGE_WINDOW)
			{
				WriteToLog("-> Resending '%d' (os:%d > %d) (%s)\n", msg.message->GetMsgID(), msg.sequenceNumber, target.outgoingAcknowledged, target.netAddress.ToStaticString());

				// send that it again
				InternalSendMessage(msg.message, &target, msg.sequenceNumber);
			}
			else
			{
				// hold back on sending the message
				WriteToLog("-> Holding back resend '%d' (os:%d > %d) (%s)\n", msg.message->GetMsgID(), msg.sequenceNumber, target.outgoingAcknowledged, target.netAddress.ToStaticString());
				msg.resendAttempt--;
			}

			// set the time before retrying again
			// send times: 0.0 1.5 1.5 1.5 (1.5) = 6.0 second timeout, 4 packets sent
			msg.resendTime = time + RESEND_TIME;

			// move it to the end of the list
			m_ReliableMessages.Unlink(index);
			m_ReliableMessages.LinkToTail(index);

			// next packet
			continue;
		}

		// target has failed to respond, remove the target and respond with a failed message
		WriteToLog("Could not deliver packet: %d (os:%d)\n", msg.message->GetMsgID(), msg.sequenceNumber);

		// send back a send failure message to the app
		// convert the send message into a receive message
		CSendMessage *sendMsg = dynamic_cast<CSendMessage *>(msg.message);
		if (sendMsg)
		{
			IBinaryBuffer *buf = sendMsg->GetBuffer();
			
			buf->SetPosition(buf->GetBufferData());
			buf->Advance(buf->GetReservedSize());
			CReceiveMessage *failMsg = new CReceiveMessage(buf, true);

			if (failMsg->IsValid())
			{
				failMsg->SetFailed();
				failMsg->SetNetAddress(target.netAddress);
				failMsg->SetSessionID(sendMsg->SessionID());

				int newIndex = m_FailedMsgs.AddToTail();
				FailedMsg_t &fmsg = m_FailedMsgs[newIndex];
				fmsg.message = failMsg;
			}
			else
			{
				delete failMsg;
			}
		}

		// target not responding, so cancel the connection
		// remove from target map
		int mapindex = FindTargetIndexByAddress(target.netAddress);
		assert(m_TargetMap.IsValidIndex(mapindex));
		m_TargetMap.RemoveAt(mapindex);

		// remove target from list
		m_TargetList.Remove(msg.networkTargetHandle);
		
		// message will automatically be delete since it's target is gone
	}
}