/*
 * Softcam plugin to VDR (C++)
 *
 * This code 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 code 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.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 */

#ifndef ___SYSTEM_H
#define ___SYSTEM_H

#include "data.h"
#include "misc.h"

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

#define SYSTEM_MASK 0xFF00

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

class cEcmInfo;
class cPlainKey;
class cSystem;
class cSystems;
class cLogger;
class cOpts;

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

class cFeature {
private:
	static bool keyfile, smartcard, network;
	static int pcount;
public:
	void NeedsKeyFile(void);
	void NeedsSmartCard(void);
	bool KeyFile(void) const { return keyfile; }
	bool SmartCard(void) const { return smartcard; }
};

extern cFeature Feature;

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

class cKeySnoop {
private:
	cSystem *sys;
	int type, id, keynr;
	bool ok;
public:
	cKeySnoop(cSystem *Sys, int Type, int Id, int Keynr);
	~cKeySnoop();
	void OK(cPlainKey *pk);
};

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

struct EcmCheck;

class cSystem : public cSimpleItem {
friend class cKeySnoop;
private:
	int pri, cardNum;
	int lastType, lastId, lastKeynr;
	char *lastTxt;
	char currentKeyStr[48];
	struct EcmCheck *check;
protected:
	const char *name;
	unsigned char cw[16];
	bool doLog;
	// config details
	int maxEcmTry;
	bool expensive, hasLogger, needsLogger, needsDescrData, constant;
	//
	void KeyOK(cPlainKey *pk);
	void KeyOK(const char *txt);
	void KeyFail(int type, int id, int keynr);
public:
	cSystem(const char *Name, int Pri);
	virtual ~cSystem();
	virtual int CheckECM(const cEcmInfo *ecm, const unsigned char *data, bool sync);
	virtual void CheckECMResult(const cEcmInfo *ecm, const unsigned char *data, bool result);
	virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *buffer)=0;
	virtual void ProcessEMM(int pid, int caid, unsigned char *buffer) {};
	unsigned char *CW(void) { return cw; }
	void DoLog(bool Log) { doLog=Log; }
	int Pri(void) { return pri; }
	const char *CurrentKeyStr(void) { return currentKeyStr; }
	const char *Name(void) { return name; }
	int CardNum(void) { return cardNum; }
	void CardNum(int CardNum) { cardNum=CardNum; }
	int MaxEcmTry(void) { return maxEcmTry; }
	bool HasLogger(void) { return hasLogger; }
	bool NeedsLogger(void) { return needsLogger; }
	bool Expensive(void) { return expensive; }
	bool NeedsData(void) { return needsDescrData; }
	bool Constant(void) { return constant; }
	const int badType(void) { return lastType; }
	const int badId(void) { return lastId; }
	const int badKeynr(void) { return lastKeynr; }
};

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

class cSystemLink {
	friend class cSystems;
protected:
	cSystemLink *next;
	const char *name;
	int pri;
	cOpts *opts;
	bool noFF;
public:
	cSystemLink(const char *Name, int Pri);
	virtual ~cSystemLink();
	virtual bool CanHandle(unsigned short SysId)=0;
	virtual cSystem *Create(void)=0;
	virtual bool Init(const char *cfgdir) { return true; }
	virtual void Clean(void) {}
};

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

class cSystems {
	friend class cSystemLink;
private:
	static cSystemLink *first;
	//
	static void Register(cSystemLink *sysLink);
public:
	static cSystem *FindBySysId(unsigned short SysId, bool ff, int oldPri);
	static bool Init(const char *cfgdir);
	static void Clean(void);
};

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

template<class T> class cCardInfos : public cSimpleList<T>, public cLoader, cConfRead {
private:
	const char *sysname;
public:
	cCardInfos(const char *id):cLoader(id) {}
	virtual ~cCardInfos() {}
	bool HaveCards(void) { return (this->Count()!=0); }
	bool Load(const char *cfgdir, const char *SysName, const char *kidName)
	{
		sysname=SysName;
		this->Clear();
		char type[32];
		_snprintf(type,sizeof(type),"%s card infos",SysName);
		cString cname=AddDirectory(cfgdir,kidName);
		ConfRead(type,cname);
		d(printf("cardinfo: loaded %d %s cards from %s\n",this->Count(),SysName,*cname))
		return HaveCards();
	}
	virtual bool ParseLine(const char *line, bool fromCache)
    {
		T *k=new T;
		if(k) {
			if(k->Parse((char *)line)) {
				if(!fromCache) { Add(k); k=0; }
				else {
					T *o=this->First();
					while(o) {
						if(o->Cmp(k)) {
							//d(printf("cardinfo: replacing from cache\n"))
							Add(k,o); k->Updated(); k=0;
							Del(o);
							break;
						}
						o=this->Next(o);
					}
				}
			}
			delete k;
			return true;
		}
		LogError("not enough memory for %s card infos!",sysname);
		return false;
	}
	virtual bool Save(FILE *f)
	{
		bool res=true;
		T *k=this->First();
		while(k) {
			if(k->IsUpdated() && !k->Save(f)) { res=false; break; }
			k=this->Next(k);
		}
		Modified(!res);
		return res;
	}
};

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

struct Cache;

class cMsgCache {
private:
	struct Cache *caches;
	unsigned char *stores;
	int numCache, ptr, storeSize, maxFail;
	cMutex mutex;
	cCondVar wait;
	//
	struct Cache *FindMsg(int crc);
public:
	cMsgCache(int NumCache, int StoreSize);
	~cMsgCache();
	int Cache(int id, bool result, const unsigned char *store);
	int Get(const unsigned char *msg, int len, unsigned char *store);
	void Clear(void);
	void SetMaxFail(int max);
};

#endif //___SYSTEM_H