#include <ICLIO/UnicapGrabber.h>
#include <ICLCore/Img.h>
#include <string>
#include <map>
#include <unicap.h>
#include <ICLCC/Converter.h>
#include <ICLCC/CCFunctions.h>
#include <ICLIO/UnicapDevice.h>
#include <ICLUtils/StringUtils.h>

#include <ICLIO/UnicapGrabEngine.h>
#include <ICLIO/UnicapConvertEngine.h>

#include <ICLIO/DefaultConvertEngine.h>
#include <ICLIO/DefaultGrabEngine.h>

#include <regex.h>

using namespace icl;
using namespace std;

namespace icl{

  UnicapGrabberImpl::UnicapGrabberImpl(const UnicapDevice &device):
    // {{{ open

    m_oDevice(device),m_poConversionBuffer(0),
    m_poGrabEngine(0),m_poConvertEngine(0), m_bUseDMA(false),
    m_bProgressiveGrabMode(true){

    init();
  }

  // }}}
  
  UnicapGrabberImpl::UnicapGrabberImpl(const std::string &deviceFilter, unsigned int useIndex):
    // {{{ open

    m_poConversionBuffer(0),m_poGrabEngine(0),
    m_poConvertEngine(0), m_bUseDMA(false),
    m_bProgressiveGrabMode(true){
    const std::vector<UnicapDevice> &ds = getDeviceList(deviceFilter);
    if(useIndex < ds.size()){
      m_oDevice = ds[useIndex];
    }else{
      ERROR_LOG("no device found for filter: \""<<deviceFilter<<"\"!");
    }    

    init();
  }

  // }}}

  UnicapGrabberImpl::~UnicapGrabberImpl(){
    // {{{ open
    m_oMutex.lock();
    ICL_DELETE(m_poGrabEngine);
    ICL_DELETE(m_poConvertEngine);
    ICL_DELETE(m_poImage);
    ICL_DELETE(m_poConversionBuffer);
    m_oMutex.unlock();
  }

  // }}}

  void UnicapGrabberImpl::init(){
    // {{{ open

    string modelname = m_oDevice.getModelName();
    if(modelname == "Philips 740 webcam"){
#define DEACTIVATE_PWC_CAM_IN_UNICAP
#ifdef DEACTIVATE_PWC_CAM_IN_UNICAP
      //      printf("Using PWCGrabEngine !\n");
      ERROR_LOG("Philips 740 webcam is not supported by the UnicapGrabberImpl !");
      m_poGrabEngine = 0 ; //new PWCGrabEngine(&m_oDevice);
      m_poConvertEngine = 0;
#else
      m_poGrabEngine = new DefaultGrabEngine(&m_oDevice, m_bUseDMA, m_bProgressiveGrabMode);
      m_poConvertEngine = new DefaultConvertEngine(&m_oDevice);
#endif
    }else if(modelname == "DFW-VL500 2.30"){ // sony cams !
      m_poGrabEngine = new DefaultGrabEngine(&m_oDevice, m_bUseDMA, m_bProgressiveGrabMode);
      m_poConvertEngine = new DefaultConvertEngine(&m_oDevice);
      
    }else if(modelname == "Hauppauge WinTV 34xxx models"){
      m_poGrabEngine = new DefaultGrabEngine(&m_oDevice, m_bUseDMA,m_bProgressiveGrabMode);
      m_poConvertEngine = new DefaultConvertEngine(&m_oDevice);

      static bool AVOID_RECURSIVE_CALL_FLAG = false;
      if(!AVOID_RECURSIVE_CALL_FLAG){
        AVOID_RECURSIVE_CALL_FLAG = true;
        setProperty("video source","S-Video");
        setProperty("video norm","PAL-BG");       //*****************************
        setProperty("size","640x480");            //                            *   
        setProperty("format","24 bpp RGB, le");   //   this properties work     *
        setProperty("Saturation","200");          //   finest, i think!         *
        setProperty("Contrast","66");             //                            *
        AVOID_RECURSIVE_CALL_FLAG = false;        //*****************************
      }                                           
    }else{
      m_poGrabEngine = new DefaultGrabEngine(&m_oDevice, m_bUseDMA, m_bProgressiveGrabMode);
      m_poConvertEngine = new DefaultConvertEngine(&m_oDevice);
    }
    m_fCurrentFps = 0;
    m_oLastTime = icl::Time::now();
  }

  // }}}

  void UnicapGrabberImpl::updateFps(){
    // {{{ open

    Time now = icl::Time::now();
    Time dt = now-m_oLastTime;
    long micro = dt.toMicroSeconds();
    m_fCurrentFps = micro ? 1000000.0/micro : 99999999;
    m_oLastTime = now;
  }

  // }}}

  float UnicapGrabberImpl::getCurrentFps() const{
    // {{{ open
    
    return m_fCurrentFps;
  }
    // }}}


  namespace{
    string force_lower_case(const string &s){
      // {{{ open

      static const Range<char> uppers('A','Z');
      static const int offs = 'A' - 'a';

      string r=s;
      for(unsigned int i=0;i<r.length();i++){
        if(uppers.contains(r[i])) r[i]-=offs;
      }
      return r;
    }
    
    // }}}
    string sizeVecToStr(const vector<Size> &v){
      // {{{ open
      if(!v.size()) return "{}";
      std::ostringstream os;

      os << '{';
      for(unsigned int i=0;i<v.size()-1;i++){
        os << '"' << v[i] << '"' << ",";
      }      
      os << '"' << v.back() << '"' << '}';
      return os.str();
    }

    // }}}
  }
  
  void UnicapGrabberImpl::setProperty(const std::string &property, const std::string &value){
    // {{{ open
    
    //    bool verbose = true;
    string p = force_lower_case(property);

    // search for former "params"
    m_oMutex.lock();
    if(p == "size"){
      // {{{ open
      vector<string> ts = tok(value,"x");
      if(ts.size() != 2){
        ERROR_LOG("unable to set parameter size to \""<<value<<"\" (expected syntax WxH)");
        m_oMutex.unlock();
        return;
      }else{
        Size s(atoi(ts[0].c_str()),atoi(ts[1].c_str()));
        delete m_poGrabEngine;
        m_poGrabEngine = 0;
        delete m_poConvertEngine;
        m_poConvertEngine = 0;
        m_oDevice.setFormatSize(s);
        init(); // creates a new grab engine
      }

      // }}}
    }else if(p == "format"){
      // {{{ open

      delete m_poGrabEngine;
      m_poGrabEngine = 0;
      delete m_poConvertEngine;
      m_poConvertEngine = 0;
      m_oDevice.setFormatID(value);
      init(); // creates new grab engine

      // }}}
    }else if(p == "size&format"){
      // {{{ open

      vector<string> tmp = tok(value,"&");
      if(tmp.size() != 2){
        ERROR_LOG("unable to set parameter format&size to \""<<value<<"\" (expected syntax WxH&FORMAT_ID)");
        m_oMutex.unlock();
        return;
      }

      string size = tmp[0];
      string fmt = tmp[1];
      vector<string> ts = tok(size,"x");
      if(ts.size() != 2){
        ERROR_LOG("unable to set size of parameter format&size to \""<<size<<"\" (expected syntax WxH)");
        m_oMutex.unlock();
        return;
      }else{
        Size s(atoi(ts[0].c_str()),atoi(ts[1].c_str()));
        delete m_poGrabEngine;
        m_poGrabEngine = 0;
        delete m_poConvertEngine;
        m_poConvertEngine = 0;
        m_oDevice.setFormat(fmt,s);
        init(); // creates a new grab engine
      }

      // }}}
    }else if(p == "dma"){
      // {{{ open
      if(value == "on" || value == "off"){
        if((value == "on" && !m_bUseDMA) || (value == "off" && m_bUseDMA)){
          delete m_poGrabEngine;
          m_poGrabEngine = 0;
          delete m_poConvertEngine;
          m_poConvertEngine = 0;
          m_bUseDMA = value == "on" ? true : false;
          init(); // creates a new grab engine
        }
      }else{
        ERROR_LOG("unable to set param dma to \"" << value << "\" (allowed values are \"on\" and \"off\")");
      }
      // }}}
    }else if(p == "grab mode"){
      // {{{ open
      if(value == "progressive" || value == "frame by frame"){
        if((value == "progressive" && !m_bProgressiveGrabMode) || (value == "frame by frame" && m_bProgressiveGrabMode)){
          ICL_DELETE(m_poGrabEngine);
          ICL_DELETE(m_poConvertEngine);
          m_bProgressiveGrabMode = value == "progressive" ? true : false;
          init(); // creates a new grab engine
        }
      }else{
        ERROR_LOG("unable to set param dma to \"" << value << "\" (allowed values are \"on\" and \"off\")");
      }
      // }}}
    }
    m_oMutex.unlock();
    
    // else search for properties
    vector<UnicapProperty> ps = m_oDevice.getProperties();
    for(unsigned int i=0;i<ps.size();i++){
      UnicapProperty &prop = ps[i];
      if(force_lower_case(prop.getID()) != p) continue;
      switch(prop.getType()){
        case UnicapProperty::range:{
          // {{{ open

          Range<double> range = prop.getRange();
          double val = atof(value.c_str());
          if(range.contains(val)){
            prop.setValue(val);
            m_oDevice.setProperty(prop);
            //      if(verbose) printf("UnicapGrabberImpl::setParam(%s=%s) [done]\n",param.c_str(),value.c_str());
          }else{
            printf("UnicapGrabberImpl::setParam() value %f is out of range [%f..%f]\n",val,range.minVal, range.maxVal);
          }
          break;
        }

          // }}}
        case UnicapProperty::valueList:{
          // {{{ open

          vector<double> valueList = prop.getValueList();
          double val = atof(value.c_str());
          bool foundValue = false;
          for(unsigned int j=0;j<valueList.size();j++){
            if(abs(valueList[j]-val)<0.00001){
              //              if(verbose) printf("UnicapGrabberImpl::setParam(%s=%s) [done]\n",param.c_str(),value.c_str());
              prop.setValue(valueList[j]);
              m_oDevice.setProperty(prop);
              foundValue = true;
              break;
            }
          }
          if(!foundValue){
            string valueListStr="{";
            char buf[50];
            for(unsigned int j=0;j<valueList.size();j++){
              sprintf(buf,"%f",valueList[j]);
              valueListStr += buf;
              if(j<valueList.size()-1){
                valueListStr+=",";
              }else{
                valueListStr+="}";
              }
            }
            // printf("UnicapGrabberImpl::setParam() value %f is not in value list %s \n",val,valueListStr.c_str());
          }
          break;
        }

          // }}}
        case UnicapProperty::menu:{
          // {{{ open
          vector<string> men = prop.getMenu();
          string val = force_lower_case(value);
          bool foundEntry = false;
          for(unsigned int j=0;j<men.size();j++){
            if(force_lower_case(men[j])==val){
              prop.setMenuItem(men[j]);
              m_oDevice.setProperty(prop);
              //  if(verbose) printf("UnicapGrabberImpl::setParam(%s=%s) [done]\n",param.c_str(),value.c_str());
              foundEntry = true;
            }
          }
          if(!foundEntry){
            printf("UnicapGrabberImpl::setParam() value is entry %s is not an valide menu entry \n",value.c_str());
            printf("menu={");
            for(unsigned int j=0;j<men.size();j++){
              printf("%s%s",men[j].c_str(),j<men.size()-1 ? "," : "}");
            }
            printf("\n");
          }
          break;
        }
        // }}}
        case UnicapProperty::data:{
          // {{{ open

          WARNING_LOG("setting up \"data\"-properties is not yet supported!");
          return;
          break;
        }

          // }}}
        case UnicapProperty::flags:{
          // {{{ open

          char *end = 0;
          prop.setFlags(strtoul(value.c_str(),&end,10));
          m_oDevice.setProperty(prop);
          break;
        }

          // }}}
        default: 
          ERROR_LOG("Unknown unicap property type ![code 8] ");
          break;
      }        
      break;
    }      
  }

  // }}}
  
  
  vector<string> UnicapGrabberImpl::getPropertyList(){
    // {{{ open

    vector<string> v;
    v.push_back("size");
    v.push_back("format");
    // [deprecated !] v.push_back("size&format");
    v.push_back("dma");
    v.push_back("grab mode");
    vector<UnicapProperty> ps = m_oDevice.getProperties();
    for(unsigned int i=0;i<ps.size();i++){
      v.push_back(ps[i].getID());
    }
    return v;
  }

  // }}}

  string UnicapGrabberImpl::getInfo(const string &name){
    // {{{ open

    if(name == "size"){
      // {{{ open

      UnicapFormat fmt= m_oDevice.getCurrentUnicapFormat();
      vector<Size> sizes = fmt.getPossibleSizes();
      if(sizes.size()){
        return sizeVecToStr(sizes);
        
      }else{
        Size others[] = {fmt.getSize(),fmt.getMinSize(),fmt.getMaxSize()};
        for(int i=0;i<3;i++){
          if(others[i] != Size::null && others[i] != Size(-1,-1)){
            return string("{\"")+str(others[i])+"\"}";
          }
        }
        return "{}";
      }      

      // }}}
    }else if(name == "format"){
      // {{{ open

      vector<UnicapFormat> fmts = m_oDevice.getFormats();
      if(!fmts.size()) return "{}";
      string s = "{";
      for(unsigned i = 0;i<fmts.size()-1;i++){
        s+=string("\"")+fmts[i].getID()+"\",";
      }
      return s+string("\"")+fmts[fmts.size()-1].getID()+"\"}";

      // }}}
    }else if(name == "size&format"){
      // {{{ open

      string s = "{";
      vector<UnicapFormat> fmts = m_oDevice.getFormats();
      for(unsigned i = 0;i<fmts.size();i++){
        vector<Size> sizes = fmts[i].getPossibleSizes();
        if(sizes.size()){
          for(unsigned int j=0;j<sizes.size();j++){
            s+=string("\"")+fmts[i].getID()+"&"+str(sizes[j])+"\"";
            if(i==fmts.size()-1 && j == sizes.size()-1){
              s+="}";
            }else{
              s+=",";
            }
          }
        }else{
          Size others[] = {fmts[i].getSize(),fmts[i].getMinSize(),fmts[i].getMaxSize()};
          for(int j=0;j<3;j++){
            if(others[j] != Size::null && others[j] != Size(-1,-1)){
              s+=string("\"")+fmts[i].getID()+"&"+str(others[j])+"\"";
              break;
            }
          }
          if(i==fmts.size()-1){
            s+="}";
          }else{
            s+=",";
          }
        }
      }
      return s;

      // }}}
    }else if(name == "dma"){
      // {{{ open
      return "{\"on\",\"off\"}";
      // }}}
    }else if(name == "grab mode"){
      return "{\"progressive\",\"frame by frame\"}";
    }else{ // checking all properties
      // {{{ open
      
      string t = getType(name);
      if(t == "undefined") return t;
      UnicapProperty p;
      vector<UnicapProperty> ps = m_oDevice.getProperties();
      bool found = false;
      for(unsigned int i=0;i<ps.size();i++){
        if(ps[i].getID() == name){
          p = ps[i];
          found = true;
          break;
        }
      }
      if(!found)return "undefined";
      if(t == "menu"){
        return Grabber::translateStringVec(p.getMenu());
      }else if(t == "range"){
        Range<double> r = p.getRange();
        return Grabber::translateSteppingRange(SteppingRange<double>(r.minVal,r.maxVal,p.getStepping()));
      }else if(t == "valueList"){
        return Grabber::translateDoubleVec(p.getValueList());
      }else{
        return "undefined";
      }
    }
    // }}}
  }

  // }}}
  
  string UnicapGrabberImpl::getType(const string &name){
    // {{{ open

    static map<UnicapProperty::type,string> *typeMap = 0;
    if(!typeMap){
      typeMap = new map<UnicapProperty::type,string>;
      (*typeMap)[UnicapProperty::valueList] = "valueList";
      (*typeMap)[UnicapProperty::menu]      = "menu";
      (*typeMap)[UnicapProperty::range]     = "range";
      (*typeMap)[UnicapProperty::flags]     = "undefined";
      (*typeMap)[UnicapProperty::data]      = "undefined";
      (*typeMap)[UnicapProperty::anytype]   = "undefined";
    }
    
    vector<UnicapProperty> ps = m_oDevice.getProperties();
    for(unsigned int i=0;i<ps.size();i++){
      if(ps[i].getID() == name){
        return (*typeMap)[ps[i].getType()];
      }
    }

    if(name == "size" || name == "format" || name == "format&size" || name == "dma" || name == "grab mode"){
      return "menu";
    }
    return "undefined";
  }

  // }}}
  
  string UnicapGrabberImpl::getValue(const std::string &name){
    // {{{ open

    // look for a specific property:
    vector<UnicapProperty> ps = m_oDevice.getProperties();
    for(unsigned int i=0;i<ps.size();i++){
      if(ps[i].getID() == name){
        char buf[30];
        switch(ps[i].getType()){
          case UnicapProperty::range:
          case UnicapProperty::valueList:
            sprintf(buf,"%f",ps[i].getValue());
            return buf;
          case UnicapProperty::menu:
            return ps[i].getMenuItem();
          default:
            return "undefined";
        }
      }
    }
    if(name == "size"){
      return str(m_oDevice.getCurrentSize());
    }else if(name == "format"){
      return m_oDevice.getFormatID();
    }else if(name == "size&format"){
      return str(m_oDevice.getCurrentSize())+"&"+m_oDevice.getFormatID();
    }else if(name == "dma"){
      if( m_bUseDMA ){
        return "on";
      }else{
        return "off";
      }
    }else if(name == "grab mode"){
      if(m_bProgressiveGrabMode){
        return "progressive";
      }else{
        return "frame by frame";
      }
    }else{
      return "undefined";
    }
  }

  // }}}

  const ImgBase* UnicapGrabberImpl::grabUD(ImgBase **ppoDst){
    // {{{ open

    ICLASSERT_RETURN_VAL(m_poGrabEngine , 0);

    if(!ppoDst) ppoDst = &m_poImage;  


    if(getIgnoreDesiredParams()){
      m_oMutex.lock();
      m_poGrabEngine->lockGrabber();

      // ---
      if(m_poGrabEngine->needsConversion()){
        const icl8u *rawData = m_poGrabEngine->getCurrentFrameUnconverted();
        m_poConvertEngine->cvtNative(rawData,ppoDst);
      }else{
        ERROR_LOG("unable to estimate hardware image parameters!\n"
                  "returning empty image with desired params");
        ensureCompatible(ppoDst,getDesiredDepth(),getDesiredParams());
        return *ppoDst;
      }

    
      // ---
      
      m_poGrabEngine->unlockGrabber();
      m_oMutex.unlock();
      updateFps();
      return *ppoDst;
    }else{
      ensureCompatible(ppoDst,getDesiredDepth(),getDesiredParams());
      
      // indicates whether a conversion to the desired parameters will be needed
      bool needFinalConversion = false; // assume, that no conversion is needed
      
      // get the image from the grabber
      m_oMutex.lock();
      m_poGrabEngine->lockGrabber();
      if(m_poGrabEngine->needsConversion()){
        const icl8u *rawData = m_poGrabEngine->getCurrentFrameUnconverted();
        if(m_poConvertEngine->isAbleToProvideParams(m_oDesiredParams,m_eDesiredDepth)){
          m_poConvertEngine->cvt(rawData,m_oDesiredParams,m_eDesiredDepth,ppoDst);
        }else{
          m_poConvertEngine->cvt(rawData,m_oDesiredParams,m_eDesiredDepth,&m_poConversionBuffer);
          needFinalConversion = true;
        }
      }else{
        if(m_poGrabEngine->isAbleToProvideParams(m_oDesiredParams,m_eDesiredDepth)){
          m_poGrabEngine->getCurrentFrameConverted(m_oDesiredParams,m_eDesiredDepth,ppoDst);
        }else{
          m_poGrabEngine->getCurrentFrameConverted(m_oDesiredParams,m_eDesiredDepth,&m_poConversionBuffer);
          needFinalConversion = true;
        }
      }
      m_poGrabEngine->unlockGrabber();
      
      
      /// 3rd step: the image, got by the Grab/Convert-Engine must not have the desired
      /// parameters, so: check and convert on demand
      if(needFinalConversion){
        m_oConverter.apply(m_poConversionBuffer,*ppoDst);
      }
      m_oMutex.unlock();
      updateFps();
      return *ppoDst;
    }
  }

  // }}}
  
  namespace{
    
    enum matchmode{
      eq, // == equal 
      in, // ~= contains
      rx  // *= regex-match
    };

    bool match_regex(const string &text,const string &patternIn){
      // {{{ open

      string patternSave = patternIn;
      char *pattern = const_cast<char*>(patternSave.c_str());
      int    status;
      regex_t    re;
      
      if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
        ERROR_LOG("error in regular expression " << patternIn);
        return false; 
      }
      status = regexec(&re, text.c_str(), (size_t) 0, NULL, 0);
      regfree(&re);
      return !status;
    }

    // }}}

    bool match_string(const string &text, const string &pattern, matchmode mode){
      switch(mode){
        case eq: return text == pattern;
        case in: return text.find(pattern,0) != string::npos;
        default: return match_regex(text,pattern);
      }
    } 
    
    struct ParamFilter{
      // {{{ open
      ParamFilter(const string &str,matchmode mode, unsigned int ui=0, unsigned long long ull=0):
        str(str),ui(ui),ull(ull),mode(mode){ }
      virtual ~ParamFilter(){}
      virtual bool ok(const UnicapDevice &d)=0;
      string str;
      unsigned int ui;
      unsigned long long ull;
      matchmode mode;
    };

    // }}}
    
    struct ParamFilterID : public ParamFilter{
      // {{{ open

      ParamFilterID(const string &id, matchmode mode):ParamFilter(str,mode){}
      virtual bool ok(const UnicapDevice &d){
        return match_string(d.getID(),str,mode);
      }
    };

    // }}}
    struct ParamFilterModelName : public ParamFilter{
      // {{{ open

      ParamFilterModelName(const string &mn, matchmode mode):ParamFilter(mn,mode){}
      virtual bool ok(const UnicapDevice &d){
        return match_string(d.getModelName(),str,mode);
      }
    };

    // }}}
    struct ParamFilterVendorName : public ParamFilter{
      // {{{ open

      ParamFilterVendorName(const string &vn, matchmode mode):ParamFilter(vn,mode){}
      virtual bool ok(const UnicapDevice &d){
        return match_string(d.getVendorName(),str,mode);
      }
    };

    // }}}
    struct ParamFilterModelID : public ParamFilter{
      // {{{ open

      ParamFilterModelID(unsigned long long mid, matchmode mode):ParamFilter("",mode,0,mid){}
      virtual bool ok(const UnicapDevice &d){
        return d.getModelID()==ull;
      }
    };

    // }}}
    struct ParamFilterVendorID : public ParamFilter{
      // {{{ open

      ParamFilterVendorID(unsigned int vid, matchmode mode):ParamFilter("",mode,vid){}
      virtual bool ok(const UnicapDevice &d){
        return d.getVendorID()==ui;
      }
    };

    // }}}
    struct ParamFilterCPILayer : public ParamFilter{
      // {{{ open

      ParamFilterCPILayer(const string &cpil, matchmode mode):ParamFilter(cpil,mode){}
      virtual bool ok(const UnicapDevice &d){
        return match_string(d.getCPILayer(),str,mode);
      }
    };

    // }}}
    struct ParamFilterDevice : public ParamFilter{
      // {{{ open

      ParamFilterDevice(const string &dev, matchmode mode):ParamFilter(dev,mode){}
      virtual bool ok(const UnicapDevice &d){
        return match_string(d.getDevice(),str,mode);
      }
    };
    
    // }}}
    struct ParamFilterFlags : public ParamFilter{
      // {{{ open
      
      ParamFilterFlags(unsigned int flags, matchmode mode):ParamFilter("",mode,flags){}
      virtual bool ok(const UnicapDevice &d){
        return d.getFlags()==ui;
      }
    };

    // }}}
   

    void filter_devices(const vector<UnicapDevice> &src, 
                        vector<UnicapDevice> &dst, const string &filter){
      // {{{ open

      dst.clear();
      const std::vector<string> toks = tok(filter,"\n");
      vector<ParamFilter*> filters;
      for(unsigned int i=0;i<toks.size();++i){
        static const char* apcOps[] = {"==","~=","*="};
        static const matchmode aeModes[] = {eq,in,rx};
        
        size_t pos = string::npos;
        matchmode mode;
        string id,value;
        for(int j=0;j<3;j++){
          if((pos=toks[i].find(apcOps[j],0)) != string::npos){
            id = toks[i].substr(0,pos);
            value = toks[i].substr(pos+2,toks[i].size()-pos-1);
            mode = aeModes[j];
            printf("filter: id=[%s] mode=[%s] value=[%s]\n ",id.c_str(),apcOps[j],value.c_str());
            break;
          }
        }
        if(pos ==  string::npos){
          WARNING_LOG("unknown filter operator token in \""<< toks[i] <<"\"");
          continue;
        }
        if(id == "id"){
          filters.push_back(new ParamFilterID(value,mode));
        }else if (id == "modelname"){
          filters.push_back(new ParamFilterModelName(value,mode));
        }else if (id == "vendorname"){
          filters.push_back(new ParamFilterVendorName(value,mode));
        }else if (id == "modelid"){
          filters.push_back(new ParamFilterModelID(atol(value.c_str()),mode));
        }else if (id == "vendorid"){
          filters.push_back(new ParamFilterVendorID((unsigned int)atol(value.c_str()),mode));
        }else if (id == "cpilayer"){
          filters.push_back(new ParamFilterCPILayer(value,mode));
        }else if (id == "device"){
          filters.push_back(new ParamFilterDevice(value,mode));
        }else if (id == "flags"){
          filters.push_back(new ParamFilterFlags((unsigned int )atol(value.c_str()),mode));
        }else{
          WARNING_LOG("unknown filter id \"" << id << "\"");
        }
      }
      for(unsigned int i=0;i<src.size();++i){
        bool ok = true;
        for(unsigned int j=0;j<filters.size();++j){
          if(!filters[j]->ok(src[i])){
            ok = false;
            break;
          }
        }
        if(ok) dst.push_back(src[i]);
      }
      for(unsigned int j=0;j<filters.size();++j){
        delete filters[j];
      }
    }    
    // }}}
  }  


  
  vector<UnicapDevice>
  UnicapGrabberImpl::getDeviceList(const string &filter){
    // {{{ open

    static std::vector<UnicapDevice> vCurrentDevices;
    // rebuild current list of available devices
    vCurrentDevices.clear();

    for(int i=0;true;++i){
       UnicapDevice d(i);
       if(!d.isValid()) break;

       vCurrentDevices.push_back(d);
    }
    return filterDevices (vCurrentDevices, filter);
  }

  // }}}
  
  /* Returning a real vector instead of a const reference to some static variable
     here allows to apply several filterDevices-calls in series */
  vector<UnicapDevice>
  UnicapGrabberImpl::filterDevices(const std::vector<UnicapDevice> &devices, 
                               const string &filter){
    // {{{ open

    std::vector<UnicapDevice> vResult;
    filter_devices(devices,vResult,filter);
    return vResult;
  }

  // }}}

}

// {{{ unicap_device_t

/************************************
typedef struct { 	
    char identifier [128];
    char model_name [128] ;
    char vendor_name [128] ;
    unsigned long long model_id ;
    unsigned int vendor_id ;
    char cpi_layer [1024] ;
    char device [1024] ;
    unsigned int flags ;
}unicap_device_t;
*************************************/

// }}}

// {{{ unicap_format_t

/************************************
typedef struct{
  char identifier[128];
  // default
  unicap_rect_t size;
  
  // min and max extends
  unicap_rect_t min_size;
  unicap_rect_t max_size;
  
  // stepping:
  // 0 if no free scaling available
  int h_stepping;
  int v_stepping;
   
  // array of possible sizes
  unicap_rect_t *sizes;
  int size_count;
   
  int bpp;
  unsigned int fourcc;
  unsigned int flags;
  
  unsigned int buffer_types;    // available buffer types
  int system_buffer_count;
  
  size_t buffer_size;
  
  unicap_buffer_type_t buffer_type;
};
*************************************/

// }}}

// {{{ unicap_property_t
/************************************************************
struct unicap_property_t{
  char identifier[128];         //mandatory
  char category[128];
  char unit[128];               //
  
  // list of properties identifier which value / behaviour may change if this property changes
  char **relations;
  int relations_count;
  
  union
  {
    double value;               // default if enumerated
    char menu_item[128];
  };
    
  union
  {
    unicap_property_range_t range;      // if UNICAP_USE_RANGE is asserted
    unicap_property_value_list_t value_list;    // if UNICAP_USE_VALUE_LIST is asserted
      unicap_property_menu_t menu;
  };
  
  double stepping;
  
  unicap_property_type_enum_t type;
  u_int64_t flags;              // defaults if enumerated
  u_int64_t flags_mask;         // defines capabilities if enumerated
  
  // optional data
  void *property_data;
  size_t property_data_size;
};
*********************************************************************/
// }}}