/*
 * 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"

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

#define SYSTEM_MASK 0xFF00

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

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

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

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);
  };

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

#define MAX_ECMS 32
#define MAX_PIDS 16

struct Ecms {
  int num;
  cEcmInfo *ecm[MAX_ECMS];
  };

struct SctPids {
  int num;
  struct {
    int pid, sct, mask;
    } pid[MAX_PIDS];
  };

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

class cSystem {
friend class cKeySnoop;
private:
  int pri, cardNum;
  int firstKey;
  cPlainKey *currentKey;
  int lastType, lastId, lastKeynr;
  char currentKeyStr[48];
  //
  struct Ecms *ecms;
  struct SctPids *pids;
protected:
  const char *name;
  unsigned char cw[16];
  bool doLog;
  // config details
  int maxEcmTry;
  bool expensive, hasLogger, needsLogger;
  //
  virtual void ParseCAT(const cEcmInfo *ecm, const unsigned char *buffer, int n);
  virtual void ParseCADescriptor(unsigned short sysId, const unsigned char *data, int len);
  bool AddPid(int Pid, int Section, int Mask);
  bool AddEcm(cEcmInfo *ecm);
  //
  void FirstKey(void);
  cPlainKey *FastKey(int type, int id, int keynr);
  void KeyOK(cPlainKey *pk);
  void KeyFail(int type, int id, int keynr);
public:
  cSystem(const char *Name, int Pri);
  virtual ~cSystem() {}
  virtual bool CheckECM(const cEcmInfo *ecm, const unsigned char *buffer) { return true; }
  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *buffer)=0;
  virtual void ProcessEMM(int pid, const cEcmInfo *ecm, unsigned char *buffer, int n) {};
  void GetECMs(unsigned short sysId, const unsigned char *buff, int n, struct Ecms *Ecms);
  void GetEMMPids(const cEcmInfo *ecm, const unsigned char *buff, int n, struct SctPids *Pids);
  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; }
  };

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

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) {}
  virtual void NewConfig(void) {}
  };

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

class cSystems {
friend class cSystemLink;
private:
  static cSystemLink *first;
  //
  static void Register(cSystemLink *sysLink);
  static cSystemLink *FindByName(const char *Name);
public:
  static int Provides(const unsigned short *SysIds, bool ff);
  static cSystem *FindBySysId(unsigned short SysId, bool ff, int oldPri);
  static cSystem *FindBySysName(unsigned short SysId, bool ff, const char *Name);
  static bool Init(const char *cfgdir);
  static void Clean(void);
  //
  static bool ConfigParse(const char *Name, const char *Value);
  static void ConfigStore(void);
  static cOpts *GetSystemOpts(bool start);
  };

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

template<class T> class cCardInfos : public cList<T>, public cLoader {
private:
  const char *sysname;
public:
  cCardInfos(const char *id):cLoader(id) {}
  bool HaveCards(void) { return (this->Count()!=0); }
  bool Load(const char *cfgdir, const char *SysName, const char *kidName)
    {
    sysname=SysName;
    this->Clear();
    //Add Directory must be replaced with w32 api s find applications directory command.
    const char *name=AddDirectory(cfgdir,kidName);
    FILE *f=fopen(name,"r");
    if(f) {
      isyslog("loading %s card infos from %s",SysName,name);
  //    m_DLLInstance.Log("loading %s card infos from %s\n",SysName,name);
      char buff[512];
      while(fgets(buff,sizeof(buff),f)) {
        if(buff[0]=='*') break;    // ignore rest of file
        if(buff[0]==';') continue; // skip comment lines
        if(!ParseLine(buff,false)) break;
        }
      fclose(f);
      }
    //d(printf("cardinfo: loaded %d %s cards from %s\n",this->Count(),SysName,name))
 //   m_DLLInstance.Log("loaded %d cards %s card infos from %s\n",this->Count(),SysName,name);
    return HaveCards();
    }
  virtual bool ParseLine(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=cList<T>::Next(o);
              }
            }
          }
        delete k;
        return true;
        }
      esyslog("ERROR: no 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=cList<T>::Next(k);
      }
    Modified(!res);
    return res;
    }
  };

#endif //___SYSTEM_H
