/********************************************************************
**                Image Component Library (ICL)                    **
**                                                                 **
** Copyright (C) 2006-2012 CITEC, University of Bielefeld          **
**                         Neuroinformatics Group                  **
** Website: www.iclcv.org and                                      **
**          http://opensource.cit-ec.de/projects/icl               **
**                                                                 **
** File   : ICLIO/src/Grabber.cpp                                  **
** Module : ICLIO                                                  **
** Authors: Christof Elbrechter, Viktor Richter                    **
**                                                                 **
**                                                                 **
** Commercial License                                              **
** ICL can be used commercially, please refer to our website       **
** www.iclcv.org for more details.                                 **
**                                                                 **
** GNU General Public License Usage                                **
** Alternatively, this file may be used under the terms of the     **
** GNU General Public License version 3.0 as published by the      **
** Free Software Foundation and appearing in the file LICENSE.GPL  **
** included in the packaging of this file.  Please review the      **
** following information to ensure the GNU General Public License  **
** version 3.0 requirements will be met:                           **
** http://www.gnu.org/copyleft/gpl.html.                           **
**                                                                 **
** The development of this software was supported by the           **
** Excellence Cluster EXC 277 Cognitive Interaction Technology.    **
** The Excellence Cluster EXC 277 is a grant of the Deutsche       **
** Forschungsgemeinschaft (DFG) in the context of the German       **
** Excellence Initiative.                                          **
**                                                                 **
*********************************************************************/

#include <ICLIO/Grabber.h>
#include <ICLFilter/WarpOp.h>
#include <ICLUtils/StringUtils.h>
#include <ICLUtils/Mutex.h>
#include <ICLUtils/ConfigFile.h>
#include <ICLCore/Converter.h>

using namespace std;
using namespace icl::utils;
using namespace icl::core;

namespace icl{
  namespace io{
    namespace {
      inline bool inList(const string &s, const std::vector<string> &vec){
        return find(vec.begin(),vec.end(),s) != vec.end();
      }
    }
    
    struct Grabber::Data{
        Size desiredSize;
        format desiredFormat;
        depth desiredDepth;
        Converter converter;
        ImgBase  *image;
        filter::WarpOp *warp;

        Mutex callbackMutex;
        std::vector<Grabber::callback> callbacks;
    };

    


    Grabber::Grabber():
      data(new Data){
      data->desiredSize = Size::null;
      data->desiredFormat = (format)-1;
      data->desiredDepth = (depth)-1;
      data->image = 0;
      data->warp = 0;
    }

    Grabber::~Grabber() {
      ICL_DELETE( data->image );
      ICL_DELETE( data->warp );
      ICL_DELETE( data );
    }

    void Grabber::useDesired(depth d, const Size &size, format fmt){
      useDesired(d); useDesired(size);useDesired(fmt);
    }
    void Grabber::ignoreDesired(){
      ignoreDesired<depth>();
      ignoreDesired<Size>();
      ignoreDesired<format>();
    }

    void Grabber::setDesiredFormatInternal(format fmt){
      data->desiredFormat = fmt;
    }
    void Grabber::setDesiredSizeInternal(const Size &size){
      data->desiredSize = size;
    }
    void Grabber::setDesiredDepthInternal(depth d){
      data->desiredDepth = d;
    }
    format Grabber::getDesiredFormatInternal() const{
      return data->desiredFormat;
    }
    depth Grabber::getDesiredDepthInternal() const{
      return data->desiredDepth;
    }
    Size Grabber::getDesiredSizeInternal() const{
      return data->desiredSize;
    }

    string Grabber::translateSteppingRange(const SteppingRange<double>& range){
      return str(range);
    }

    SteppingRange<double> Grabber::translateSteppingRange(const string &rangeStr){
      return parse<SteppingRange<double> >(rangeStr);
    }

    template <class T> static std::string translate_any_vec(const std::vector<T> &v){
      std::ostringstream s;
      s << "{";
      for(unsigned int i=0;i<v.size();++i){
        s << '"' << v[i] << '"' << (i<v.size()-1 ? ',' : '}');
      }
      return s.str();
    }
    
    static std::string strip_quotes(std::string s){
      if(!s.length()) return s;
      if(s[0] == '"') s = s.substr(1);
      if(!s.length()) return s;
      if(s[s.length()-1] == '"') return s.substr(0,s.length()-1);
      return s;
    }
    
    template <class T> static std::vector<T> translate_any_string(const std::string &v){
      std::vector<string> vs = tok(v,",");
      std::vector<T> ts(vs.size());
      for(unsigned int i=0; i<vs.size();++i){
        if(i==0 && vs[i].length() && vs[i][0] == '{'){
          vs[i] = vs[i].substr(1);
        }else if(i==vs.size()-1 && vs[i].length() && vs[i][vs[i].length()-1] == '}'){
          vs[i] = vs[i].substr(0,vs[i].length()-1);
        }
        ts[i] = parse<T>(strip_quotes(vs[i]));
      }
      return ts;
    }
    
    
    string Grabber::translateDoubleVec(const vector<double> &v){
      return translate_any_vec(v);
    }
    vector<double> Grabber::translateDoubleVec(const string &s){
      return translate_any_string<double>(s);
    }
    string Grabber::translateStringVec(const vector<string> &v){
      return translate_any_vec(v);
    }
    vector<string> Grabber::translateStringVec(const string &v){
      return translate_any_string<std::string>(v);
    }

    const ImgBase *Grabber::grab(ImgBase **ppoDst){
      const ImgBase *acquired = acquireImage();
      
      // todo, on which image is the warping applied ?
      // on the aqcuired image or on the adapte image?
      // for now, we use the adapted which seem to make
      // much more sence
      
      const ImgBase *adapted = adaptGrabResult(acquired,data->warp ? 0 : ppoDst);
      if(data->warp){
        if(ppoDst){
          data->warp->apply(adapted, ppoDst);
          return *ppoDst;
        }else{
          return data->warp->apply(adapted);
        }
      }else{
        return adapted;
      }
    }

    void Grabber::enableUndistortion(const ImageUndistortion &udist){
      enableUndistortion(udist.createWarpMap());//warpMap);
    }

    void Grabber::enableUndistortion(const std::string &filename){
      enableUndistortion(ImageUndistortion(filename));
    }
    
    void Grabber::enableUndistortion(const ProgArg &pa){
      enableUndistortion(utils::pa(pa.getID(),0).as<std::string>());
    }

    void Grabber::setUndistortionInterpolationMode(scalemode mode){
      if(data->warp){
        data->warp->setScaleMode(mode);
      }else {
        WARNING_LOG("cannot set undistortion interpolation mode if distortion was not disabled before (skipped)!");
      }
    }


    bool Grabber::isUndistortionEnabled() const{
      return data->warp;
    }
    
    void Grabber::enableUndistortion(const Img32f &warpMap){
      if(!data->warp){
        data->warp = new filter::WarpOp;
      }
      data->warp->setWarpMap(warpMap);
      data->warp->setScaleMode(interpolateLIN);
    }
    
    void Grabber::disableUndistortion(){
      ICL_DELETE(data->warp);
    }
    
    
    const ImgBase *Grabber::adaptGrabResult(const ImgBase *src, ImgBase **dst){
      bool adaptDepth = desiredUsed<depth>() && (getDesired<depth>() != src->getDepth());
      bool adaptSize = desiredUsed<Size>() && (getDesired<Size>() != src->getSize());
      bool adaptFormat = desiredUsed<format>() && (getDesired<format>() != src->getFormat());
      if(adaptDepth || adaptSize || adaptFormat){
        if(!dst){
          dst = &data->image;
        }
        ensureCompatible(dst,
                         adaptDepth ? getDesired<depth>() : src->getDepth(),
                         adaptSize ? getDesired<Size>() : src->getSize(),
                         adaptFormat ? getDesired<format>() : src->getFormat());
        data->converter.apply(src,*dst);
        return *dst;
      }else{
        if(dst){
          src->deepCopy(dst);
          return *dst;
        }else{
          return src;
        }
      }
    }
    
    /*static std::vector<std::string> filter_unstable_params(const std::vector<std::string> ps){
      std::vector<std::string> fs; fs.reserve(ps.size());

      static std::string unstable[6]={
        "trigger-from-software",
        "trigger-mode",
        "trigger-polarity",
        "trigger-power",
        "trigger-source",
        "iso-speed"
      };
      for(unsigned int i=0;i<ps.size();++i){
        if(std::find(unstable,unstable+6,ps[i]) == unstable+6){
          fs.push_back(ps[i]);
        }
      }
      return fs;
    }*/

    const Img32f *Grabber::getUndistortionWarpMap() const{
      return data->warp ? &data->warp->getWarpMap() : 0;
    }


    void Grabber::registerCallback(Grabber::callback cb){
      Mutex::Locker lock(data->callbackMutex);
      data->callbacks.push_back(cb);
    }

    void Grabber::removeAllCallbacks(){
      Mutex::Locker lock(data->callbackMutex);
      data->callbacks.clear();
    }

    void Grabber::notifyNewImageAvailable(const ImgBase *image){
      Mutex::Locker lock(data->callbackMutex);
      for(size_t i=0;i<data->callbacks.size();++i){
        data->callbacks[i](image);
      }
    }

    void Grabber::processPropertyChange(const utils::Configurable::Property &prop){
      if(prop.name == "desired size"){
        if(prop.value == "not used"){
          ignoreDesired<Size>();
        } else {
          useDesired<Size>(parse<Size>(prop.value));
        }
      } else if (prop.name == "desired depth"){
        if(prop.value == "not used"){
          ignoreDesired<depth>();
        } else {
          useDesired<depth>(parse<depth>(prop.value));
        }
      } else if (prop.name == "desired format"){
        if(prop.value == "not used"){
          ignoreDesired<format>();
        } else {
          useDesired<format>(parse<format>(prop.value));
        }
      }
    }

    REGISTER_CONFIGURABLE_DEFAULT(Grabber);
  } // namespace io
}