/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

 * Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.

 * Redistributions in binary form must reproduce the
  above copyright notice, this list of conditions
  and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

 * Neither the name of the University of Illinois
  nor the names of its contributors may be used to
  endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************/

/*****************************************************************************
written by
   Yunhong Gu, last updated 01/22/2011
 *****************************************************************************/

#include "list.h"
#include <iostream>
using namespace std;
CSndLossList::CSndLossList(const int& size):
		m_piData1(NULL),
		m_piData2(NULL),
		m_piNext(NULL),
		m_iHead(-1),
		m_iLength(0),
		m_iSize(size),
		m_iLastInsertPos(-1),
		m_ListLock()
{
	m_piData1 = new int32_t [m_iSize];
	m_piData2 = new int32_t [m_iSize];
	m_piNext = new int [m_iSize];

	// -1 means there is no data in the node
	for (int i = 0; i < size; ++ i)
	{
		m_piData1[i] = -1;
		m_piData2[i] = -1;
	}

	// sender list needs mutex protection
#ifndef WIN32
	pthread_mutex_init(&m_ListLock, 0);
#else
	m_ListLock = CreateMutex(NULL, false, NULL);
#endif
}

CSndLossList::~CSndLossList()
{
	delete [] m_piData1;
	delete [] m_piData2;
	delete [] m_piNext;

#ifndef WIN32
	pthread_mutex_destroy(&m_ListLock);
#else
	CloseHandle(m_ListLock);
#endif
}

int CSndLossList::insert(const int32_t& seqno1, const int32_t& seqno2)
{
	CGuard listguard(m_ListLock);

	if (0 == m_iLength)
	{
		// insert data into an empty list

		m_iHead = 0;
		m_piData1[m_iHead] = seqno1;
		if (seqno2 != seqno1)
			m_piData2[m_iHead] = seqno2;

		m_piNext[m_iHead] = -1;
		m_iLastInsertPos = m_iHead;

		m_iLength += CSeqNo::seqlen(seqno1, seqno2);

		return m_iLength;
	}

	// otherwise find the position where the data can be inserted
	int origlen = m_iLength;
	int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno1);
	int loc = (m_iHead + offset + m_iSize) % m_iSize;

	if (offset < 0)
	{
		// Insert data prior to the head pointer

		m_piData1[loc] = seqno1;
		if (seqno2 != seqno1)
			m_piData2[loc] = seqno2;

		// new node becomes head
		m_piNext[loc] = m_iHead;
		m_iHead = loc;
		m_iLastInsertPos = loc;

		m_iLength += CSeqNo::seqlen(seqno1, seqno2);
	}
	else if (offset > 0)
	{
		if (seqno1 == m_piData1[loc])
		{
			m_iLastInsertPos = loc;

			// first seqno is equivlent, compare the second
			if (-1 == m_piData2[loc])
			{
				if (seqno2 != seqno1)
				{
					m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1;
					m_piData2[loc] = seqno2;
				}
			}
			else if (CSeqNo::seqcmp(seqno2, m_piData2[loc]) > 0)
			{
				// new seq pair is longer than old pair, e.g., insert [3, 7] to [3, 5], becomes [3, 7]
				m_iLength += CSeqNo::seqlen(m_piData2[loc], seqno2) - 1;
				m_piData2[loc] = seqno2;
			}
			else
				// Do nothing if it is already there
				return 0;
		}
		else
		{
			// searching the prior node
			int i;
			if ((-1 != m_iLastInsertPos) && (CSeqNo::seqcmp(m_piData1[m_iLastInsertPos], seqno1) < 0))
				i = m_iLastInsertPos;
			else
				i = m_iHead;

			while ((-1 != m_piNext[i]) && (CSeqNo::seqcmp(m_piData1[m_piNext[i]], seqno1) < 0))
				i = m_piNext[i];

			if ((-1 == m_piData2[i]) || (CSeqNo::seqcmp(m_piData2[i], seqno1) < 0))
			{
				m_iLastInsertPos = loc;

				// no overlap, create new node
				m_piData1[loc] = seqno1;
				if (seqno2 != seqno1)
					m_piData2[loc] = seqno2;

				m_piNext[loc] = m_piNext[i];
				m_piNext[i] = loc;

				m_iLength += CSeqNo::seqlen(seqno1, seqno2);
			}
			else
			{
				m_iLastInsertPos = i;

				// overlap, coalesce with prior node, insert(3, 7) to [2, 5], ... becomes [2, 7]
				if (CSeqNo::seqcmp(m_piData2[i], seqno2) < 0)
				{
					m_iLength += CSeqNo::seqlen(m_piData2[i], seqno2) - 1;
					m_piData2[i] = seqno2;

					loc = i;
				}
				else
					return 0;
			}
		}
	}
	else
	{
		m_iLastInsertPos = m_iHead;

		// insert to head node
		if (seqno2 != seqno1)
		{
			if (-1 == m_piData2[loc])
			{
				m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1;
				m_piData2[loc] = seqno2;
			}
			else if (CSeqNo::seqcmp(seqno2, m_piData2[loc]) > 0)
			{
				m_iLength += CSeqNo::seqlen(m_piData2[loc], seqno2) - 1;
				m_piData2[loc] = seqno2;
			}
			else
				return 0;
		}
		else
			return 0;
	}

	// coalesce with next node. E.g., [3, 7], ..., [6, 9] becomes [3, 9]
	while ((-1 != m_piNext[loc]) && (-1 != m_piData2[loc]))
	{
		int i = m_piNext[loc];

		if (CSeqNo::seqcmp(m_piData1[i], CSeqNo::incseq(m_piData2[loc])) <= 0)
		{
			// coalesce if there is overlap
			if (-1 != m_piData2[i])
			{
				if (CSeqNo::seqcmp(m_piData2[i], m_piData2[loc]) > 0)
				{
					if (CSeqNo::seqcmp(m_piData2[loc], m_piData1[i]) >= 0)
						m_iLength -= CSeqNo::seqlen(m_piData1[i], m_piData2[loc]);

					m_piData2[loc] = m_piData2[i];
				}
				else
					m_iLength -= CSeqNo::seqlen(m_piData1[i], m_piData2[i]);
			}
			else
			{
				if (m_piData1[i] == CSeqNo::incseq(m_piData2[loc]))
					m_piData2[loc] = m_piData1[i];
				else
					m_iLength --;
			}

			m_piData1[i] = -1;
			m_piData2[i] = -1;
			m_piNext[loc] = m_piNext[i];
		}
		else
			break;
	}

	return m_iLength - origlen;
}

void CSndLossList::remove(const int32_t& seqno)
{
	CGuard listguard(m_ListLock);

	if (0 == m_iLength)
		return;

	// Remove all from the head pointer to a node with a larger seq. no. or the list is empty
	int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno);
	int loc = (m_iHead + offset + m_iSize) % m_iSize;

	if (0 == offset)
	{
		// It is the head. Remove the head and point to the next node
		loc = (loc + 1) % m_iSize;

		if (-1 == m_piData2[m_iHead])
			loc = m_piNext[m_iHead];
		else
		{
			m_piData1[loc] = CSeqNo::incseq(seqno);
			if (CSeqNo::seqcmp(m_piData2[m_iHead], CSeqNo::incseq(seqno)) > 0)
				m_piData2[loc] = m_piData2[m_iHead];

			m_piData2[m_iHead] = -1;

			m_piNext[loc] = m_piNext[m_iHead];
		}

		m_piData1[m_iHead] = -1;

		if (m_iLastInsertPos == m_iHead)
			m_iLastInsertPos = -1;

		m_iHead = loc;

		m_iLength --;
	}
	else if (offset > 0)
	{
		int h = m_iHead;

		if (seqno == m_piData1[loc])
		{
			// target node is not empty, remove part/all of the seqno in the node.
			int temp = loc;
			loc = (loc + 1) % m_iSize;

			if (-1 == m_piData2[temp])
				m_iHead = m_piNext[temp];
			else
			{
				// remove part, e.g., [3, 7] becomes [], [4, 7] after remove(3)
				m_piData1[loc] = CSeqNo::incseq(seqno);
				if (CSeqNo::seqcmp(m_piData2[temp], m_piData1[loc]) > 0)
					m_piData2[loc] = m_piData2[temp];
				m_iHead = loc;
				m_piNext[loc] = m_piNext[temp];
				m_piNext[temp] = loc;
				m_piData2[temp] = -1;
			}
		}
		else
		{
			// target node is empty, check prior node
			int i = m_iHead;
			while ((-1 != m_piNext[i]) && (CSeqNo::seqcmp(m_piData1[m_piNext[i]], seqno) < 0))
				i = m_piNext[i];

			loc = (loc + 1) % m_iSize;

			if (-1 == m_piData2[i])
				m_iHead = m_piNext[i];
			else if (CSeqNo::seqcmp(m_piData2[i], seqno) > 0)
			{
				// remove part/all seqno in the prior node
				m_piData1[loc] = CSeqNo::incseq(seqno);
				if (CSeqNo::seqcmp(m_piData2[i], m_piData1[loc]) > 0)
					m_piData2[loc] = m_piData2[i];

				m_piData2[i] = seqno;

				m_piNext[loc] = m_piNext[i];
				m_piNext[i] = loc;

				m_iHead = loc;
			}
			else
				m_iHead = m_piNext[i];
		}

		// Remove all nodes prior to the new head
		while (h != m_iHead)
		{
			if (m_piData2[h] != -1)
			{
				m_iLength -= CSeqNo::seqlen(m_piData1[h], m_piData2[h]);
				m_piData2[h] = -1;
			}
			else
				m_iLength --;

			m_piData1[h] = -1;

			if (m_iLastInsertPos == h)
				m_iLastInsertPos = -1;

			h = m_piNext[h];
		}
	}
}

int CSndLossList::getLossLength()
{
	CGuard listguard(m_ListLock);

	return m_iLength;
}

int32_t CSndLossList::getLostSeq()
{
	if (0 == m_iLength)
		return -1;

	CGuard listguard(m_ListLock);

	if (0 == m_iLength)
		return -1;

	if (m_iLastInsertPos == m_iHead)
		m_iLastInsertPos = -1;

	// return the first loss seq. no.
	int32_t seqno = m_piData1[m_iHead];

	// head moves to the next node
	if (-1 == m_piData2[m_iHead])
	{
		//[3, -1] becomes [], and head moves to next node in the list
		m_piData1[m_iHead] = -1;
		m_iHead = m_piNext[m_iHead];
	}
	else
	{
		// shift to next node, e.g., [3, 7] becomes [], [4, 7]
		int loc = (m_iHead + 1) % m_iSize;

		m_piData1[loc] = CSeqNo::incseq(seqno);
		if (CSeqNo::seqcmp(m_piData2[m_iHead], m_piData1[loc]) > 0)
			m_piData2[loc] = m_piData2[m_iHead];

		m_piData1[m_iHead] = -1;
		m_piData2[m_iHead] = -1;

		m_piNext[loc] = m_piNext[m_iHead];
		m_iHead = loc;
	}

	m_iLength --;

	return seqno;
}

////////////////////////////////////////////////////////////////////////////////

CRcvLossList::CRcvLossList(const int& size):
		m_piData1(NULL),
		m_piData2(NULL),
		m_piNext(NULL),
		m_piPrior(NULL),
		m_iHead(-1),
		m_iTail(-1),
		m_iLength(0),
		m_iSize(size)
{
	m_piData1 = new int32_t [m_iSize];
	m_piData2 = new int32_t [m_iSize];
	m_piNext = new int [m_iSize];
	m_piPrior = new int [m_iSize];

	// -1 means there is no data in the node
	for (int i = 0; i < size; ++ i)
	{
		m_piData1[i] = -1;
		m_piData2[i] = -1;
	}
}

CRcvLossList::~CRcvLossList()
{
	delete [] m_piData1;
	delete [] m_piData2;
	delete [] m_piNext;
	delete [] m_piPrior;
}

void CRcvLossList::insert(const int32_t& seqno1, const int32_t& seqno2)
{
	// Data to be inserted must be larger than all those in the list
	// guaranteed by the UDT receiver

	if (0 == m_iLength)
	{
		// insert data into an empty list
		m_iHead = 0;
		m_iTail = 0;
		m_piData1[m_iHead] = seqno1;
		if (seqno2 != seqno1)
			m_piData2[m_iHead] = seqno2;

		m_piNext[m_iHead] = -1;
		m_piPrior[m_iHead] = -1;
		m_iLength += CSeqNo::seqlen(seqno1, seqno2);

		return;
	}

	// otherwise searching for the position where the node should be
	int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno1);
	int loc = (m_iHead + offset) % m_iSize;

	if ((-1 != m_piData2[m_iTail]) && (CSeqNo::incseq(m_piData2[m_iTail]) == seqno1))
	{
		// coalesce with prior node, e.g., [2, 5], [6, 7] becomes [2, 7]
		loc = m_iTail;
		m_piData2[loc] = seqno2;
	}
	else
	{
		// create new node
		m_piData1[loc] = seqno1;

		if (seqno2 != seqno1)
			m_piData2[loc] = seqno2;

		m_piNext[m_iTail] = loc;
		m_piPrior[loc] = m_iTail;
		m_piNext[loc] = -1;
		m_iTail = loc;
	}

	m_iLength += CSeqNo::seqlen(seqno1, seqno2);
}

int CRcvLossList::remove(const int32_t& seqno)
{
	if (0 == m_iLength)
		return false;

	// locate the position of "seqno" in the list
	int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno);
	if (offset < 0)
		return false;

	int loc = (m_iHead + offset) % m_iSize;

	if (seqno == m_piData1[loc])
	{
		// This is a seq. no. that starts the loss sequence

		if (-1 == m_piData2[loc])
		{
			// there is only 1 loss in the sequence, delete it from the node
			if (m_iHead == loc)
			{
				m_iHead = m_piNext[m_iHead];
				if (-1 != m_iHead)
					m_piPrior[m_iHead] = -1;
			}
			else
			{
				m_piNext[m_piPrior[loc]] = m_piNext[loc];
				if (-1 != m_piNext[loc])
					m_piPrior[m_piNext[loc]] = m_piPrior[loc];
				else
					m_iTail = m_piPrior[loc];
			}

			m_piData1[loc] = -1;
		}
		else
		{
			// there are more than 1 loss in the sequence
			// move the node to the next and update the starter as the next loss inSeqNo(seqno)

			// find next node
			int i = (loc + 1) % m_iSize;

			// remove the "seqno" and change the starter as next seq. no.
			m_piData1[i] = CSeqNo::incseq(m_piData1[loc]);

			// process the sequence end
			if (CSeqNo::seqcmp(m_piData2[loc], CSeqNo::incseq(m_piData1[loc])) > 0)
				m_piData2[i] = m_piData2[loc];

			// remove the current node
			m_piData1[loc] = -1;
			m_piData2[loc] = -1;

			// update list pointer
			m_piNext[i] = m_piNext[loc];
			m_piPrior[i] = m_piPrior[loc];

			if (m_iHead == loc)
				m_iHead = i;
			else
				m_piNext[m_piPrior[i]] = i;

			if (m_iTail == loc)
				m_iTail = i;
			else
				m_piPrior[m_piNext[i]] = i;
		}

		m_iLength --;

		return true;
	}

	// There is no loss sequence in the current position
	// the "seqno" may be contained in a previous node

	// searching previous node
	int i = (loc - 1 + m_iSize) % m_iSize;
	while (-1 == m_piData1[i])
		i = (i - 1 + m_iSize) % m_iSize;

	// not contained in this node, return
	if ((-1 == m_piData2[i]) || (CSeqNo::seqcmp(seqno, m_piData2[i]) > 0))
		return false;

	if (seqno == m_piData2[i])
	{
		// it is the sequence end

		if (seqno == CSeqNo::incseq(m_piData1[i]))
			m_piData2[i] = -1;
		else
			m_piData2[i] = CSeqNo::decseq(seqno);
	}
	else
	{
		// split the sequence

		// construct the second sequence from CSeqNo::incseq(seqno) to the original sequence end
		// located at "loc + 1"
		loc = (loc + 1) % m_iSize;

		m_piData1[loc] = CSeqNo::incseq(seqno);
		if (CSeqNo::seqcmp(m_piData2[i], m_piData1[loc]) > 0)
			m_piData2[loc] = m_piData2[i];

		// the first (original) sequence is between the original sequence start to CSeqNo::decseq(seqno)
		if (seqno == CSeqNo::incseq(m_piData1[i]))
			m_piData2[i] = -1;
		else
			m_piData2[i] = CSeqNo::decseq(seqno);

		// update the list pointer
		m_piNext[loc] = m_piNext[i];
		m_piNext[i] = loc;
		m_piPrior[loc] = i;
		//cout<<"SplitHappens!!"<<endl;
		if (m_iTail == i)
			m_iTail = loc;
		else
			m_piPrior[m_piNext[loc]] = loc;
	}

	m_iLength --;

	return i+2;
}

int CRcvLossList::remove(const int32_t& seqno1, const int32_t& seqno2)
{
	if (seqno1 <= seqno2)
	{
		for (int32_t i = seqno1; i <= seqno2; ++ i)
			remove(i);
	}
	else
	{
		for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++ j)
			remove(j);
		for (int32_t k = 0; k <= seqno2; ++ k)
			remove(k);
	}

	return true;
}

bool CRcvLossList::find(const int32_t& seqno1, const int32_t& seqno2) const
{
	if (0 == m_iLength)
		return false;

	int p = m_iHead;

	while (-1 != p)
	{
		if ((CSeqNo::seqcmp(m_piData1[p], seqno1) == 0) ||
				((CSeqNo::seqcmp(m_piData1[p], seqno1) > 0) && (CSeqNo::seqcmp(m_piData1[p], seqno2) <= 0)) ||
				((CSeqNo::seqcmp(m_piData1[p], seqno1) < 0) && (m_piData2[p] != -1) && CSeqNo::seqcmp(m_piData2[p], seqno1) >= 0))
			return true;

		p = m_piNext[p];
	}

	return false;
}

int CRcvLossList::getLossLength() const
{
	return m_iLength;
}

int CRcvLossList::getFirstLostSeq() const
{
	if (0 == m_iLength)
		return -1;

	return m_piData1[m_iHead];
}

void CRcvLossList::getLossArray(int32_t* array, int& len, int limit, int & offset)
{
   len = 0;

   int i = m_iHead;
   int seek=offset;
while(seek>0)
{

i=m_piNext[i];
seek--;
}
   while ((len < limit - 1) && (-1 != i))
   {
      array[len] = m_piData1[i];
      if (-1 != m_piData2[i])
      {
         // there are more than 1 loss in the sequence
         array[len] |= 0x80000000;
         ++ len;
         array[len] = m_piData2[i];
      }

      ++ len;

      i = m_piNext[i];
   ++offset;
   }
//cout<<"len"<<len<<endl;
}
