/* Copyright (C) 2000, 2001  SWsoft, Singapore
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*  $Id: documents.h,v 1.8 2001/06/01 11:48:31 igor Exp $
    Author : Alexander F. Avdonkin
*/

#ifndef _DOCUMENTS_H_
#define _DOCUMENTS_H_

#include "defines.h"
#include <string>
#include <vector>
#include <pthread.h>
#include <errno.h>

template<int n>
class CFixedString
{
public:
	char m_content[n];

public:
	CFixedString()
	{
		m_content[0] = 0;
	}
	CFixedString(const char* str)
	{
		strncpy(m_content, str, n - 1);
		m_content[n - 1] = 0;
	}
	const char* c_str() const
	{
		return m_content;
	}
	void operator=(const char* src)
	{
		strncpy(m_content, src, n - 1);
		m_content[n - 1] = 0;
	}
	bool operator==(const CFixedString& Word) const
	{
		return strcmp(m_content, Word.m_content) == 0;
	}
};


class CDocument
{
public:
	CDocument()
	{
		Init();
	}
	CDocument* Init()
	{
		m_hops = 0;
		m_referrer = 0;
		m_slow = 0;
		m_redir = 0;
		return this;
	}
	int Slow()
	{
		return m_slow;
	}

public:
	ULONG m_urlID;
	ULONG m_siteID;
	int m_last_index_time;
	int m_next_index_time;
	int m_hops;
	int m_referrer;
	int m_status;
	int m_slow;
	int m_redir;
	CFixedString<33> m_crc;
	CFixedString<33> m_last_modified;
	CFixedString<MAX_ETAG_LEN> m_etag;
	CFixedString<MAX_URL_LEN + 1> m_url;
#ifdef OPTIMIZE_CLONE
	int m_origin;
#endif
};

typedef vector<ULONG> CULONGVector;
typedef vector<string> CStringVector;

class CSrcDocument : public CDocument
{
public:
	string m_descr;
	string m_keywords;
	string m_title;
	string m_content_type;
	string m_text;
	CStringVector m_excerpts;
#ifdef UNICODE
	string m_charset;
	CULONGVector m_descrH;
	CULONGVector m_keywordsH;
	CULONGVector m_titleH;
	CULONGVector m_textH;
	CULONGVector m_excerptsH;
#endif
	char* m_content;
	ULONG m_size;

public:
	CSrcDocument()
	{
		m_content = NULL;
	}
	~CSrcDocument()
	{
		if (m_content) delete m_content;
	}
};

class CSrcDocVector : public vector<CSrcDocument*>
{
public:
	~CSrcDocVector()
	{
		for (CSrcDocument** doc = begin(); doc != end(); doc++)
		{
			if (*doc) delete *doc;
		}
	}
};

/// Class used for communication between threads
class CEvent
{
public:
	pthread_mutex_t		m_mutex;	///< Mutex sem & attribute
	pthread_condattr_t	m_cattr;	///< CondVar attribute
	pthread_cond_t		m_cv;		///< Condition variable
	int			m_signal;	///< Signal

public:
	CEvent()
	{
		if (pthread_condattr_init(&m_cattr)) return;
		if (pthread_mutex_init(&m_mutex, NULL))
		{
			pthread_condattr_destroy(&m_cattr);
			return;
		}
		if (pthread_cond_init(&m_cv, &m_cattr))
		{
			pthread_condattr_destroy(&m_cattr);
			return;
		}
		pthread_condattr_destroy(&m_cattr);
		m_signal = 0;
	}
	~CEvent()
	{
		pthread_mutex_destroy(&m_mutex);
		pthread_cond_destroy(&m_cv);
	}
	/// Post event
	void Post()
	{
		Lock();
		PostN();
		Unlock();
	}
	/// Post event without locking mutex
	void PostN()
	{
		if (m_signal == 0)
		{
			m_signal = 1;
			pthread_cond_signal(&m_cv);
		}
	}
	void Lock()
	{
		pthread_mutex_lock(&m_mutex);
	}
	void Unlock()
	{
		pthread_mutex_unlock(&m_mutex);
	}
	/// Wait for event
	int Wait(ULONG timeOut = 0xFFFFFFFF)
	{
		struct timespec to;
		int	ret = 0;
//		int	CurTime;

#define NANOSECS 1000000000 ///< number of nano secs in a second

		pthread_mutex_lock(&m_mutex);
		switch (timeOut)
		{
			case 0xFFFFFFFF:
			while (m_signal == 0)
			{
				if (( ret = pthread_cond_wait(&m_cv, &m_mutex)) != 0)
				{
					if (ret != EINTR)
					{
						break;
					}
				}
			}
			break;

			case 0:
				sched_yield();
				break;

			default:
				if ((to.tv_sec = timeOut / 1000) < 1)
					to.tv_sec++; /// At least 1 sec for Solaris threads
				to.tv_sec += time(NULL);
				to.tv_nsec = 0;
				while (m_signal == 0)
				{
				if ((ret = pthread_cond_timedwait(&m_cv, &m_mutex, &to)) != 0)
				{
					if (/* ( ret == ETIME ) || */( ret == ETIMEDOUT ))
						break;

					if (ret != EINTR)
					{
						break;
					}
				}
			}
			break;
		}
		m_signal = 0;

		pthread_mutex_unlock(&m_mutex);
		if (ret) return -1;
		return ret;
	}
};

class CEventLink
{
public:
	CEvent m_event;
	CEventLink* m_next;

public:
	CEventLink()
	{
		m_next = NULL;
	}
};

#endif


