/********************************************************************
**                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   : ICLBlob/src/RegionBasedBlobSearcher.cpp                **
** Module : ICLBlob                                                **
** Authors: Christof Elbrechter                                    **
**                                                                 **
**                                                                 **
** 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 <ICLBlob/RegionBasedBlobSearcher.h>
#include <ICLCC/Converter.h>
#include <ICLCore/Img.h>
#include <ICLBlob/RegionDetector.h>
#include <math.h>
#include <limits>
#include <ICLBlob/RegionFilter.h>
#include <ICLBlob/FMCreator.h>

namespace icl{
  using namespace std;

  namespace{

    /// struct to use a Size struct as std::map - key
   


    std::vector<int> &toPOD(const std::vector<Point> &src, std::vector<int> &dst){
      // {{{ open

    dst.clear();
    for(unsigned int i=0;i<src.size();++i){
      dst.push_back(src[i].x);
      dst.push_back(src[i].y);
    }   
    return dst;
  }

    // }}}   

    std::vector<float> &toPOD(const std::vector<Point32f> &src, std::vector<float> &dst){
      // {{{ open
      
      dst.clear();
      for(unsigned int i=0;i<src.size();++i){
        dst.push_back(src[i].x);
        dst.push_back(src[i].y);
      }   
      return dst;
    }
    
    // }}}       

    std::vector<std::vector<int> > &toPOD(const std::vector<std::vector<Point> >&src, std::vector<std::vector<int> > &dst){
      // {{{ open
      dst.clear();
      for(unsigned int i=0;i<src.size();++i){
        dst.push_back(std::vector<int>());
        for(unsigned int j=0;j<src[i].size();++j){
          dst[i].push_back(src[i][j].x);
          dst[i].push_back(src[i][j].y);
        }
      }   
      return dst;
    }

  // }}}



    std::vector<int> &toPOD(const std::vector<std::vector<Rect> > &src, std::vector<int> &dst){
      // {{{ open

    dst.clear();
    for(unsigned int i=0;i<src.size();++i){
      for(unsigned int j=0;j<src[i].size();++j){
        dst.push_back(src[i][j].x);
        dst.push_back(src[i][j].y);
        dst.push_back(src[i][j].width);
        dst.push_back(src[i][j].height);
      }
    }
    return dst;
  }

  // }}}
    std::vector<float> &toPOD(const std::vector<std::vector<RegionPCAInfo> > &src, std::vector<float> &dst){
      // {{{ open
      
    dst.clear();
    for(unsigned int i=0;i<src.size();++i){
      for(unsigned int j=0;j<src[i].size();++j){    
        dst.push_back(src[i][j].len1);
        dst.push_back(src[i][j].len2);
        dst.push_back(src[i][j].arc1);
        dst.push_back(src[i][j].arc2);
      }
    }
    return dst;
  }

  // }}}
  }

  RegionBasedBlobSearcher::RegionBasedBlobSearcher(){
    // {{{ open

    m_poRD = new RegionDetector(0,0,0,0);
    m_poInputImage = 0;
    m_poConverter = new Converter;
  }

  // }}}
  RegionBasedBlobSearcher::~RegionBasedBlobSearcher(){
    // {{{ open
    removeAll();
    delete m_poRD;

  }

  // }}}
 
  const std::vector<const ImgBase*> RegionBasedBlobSearcher::getFeatureMaps() const{
    // {{{ open

    std::vector<const ImgBase *> v(m_oFMRF.size());
    for(unsigned int i=0;i<m_oFMRF.size();i++){
      v[i] = m_oFMRF[i].fmc->getLastFM();
    }
    return v;
  }

  // }}}

  const std::vector<Point> &RegionBasedBlobSearcher::getCOGs(){
    // {{{ open

    m_oCOGsOut.clear();
    for(unsigned int i=0;i<m_oInternalData.size();++i){
      FF &fac = m_oScaleFactors[i];
      Point32f p32 = m_oInternalData[i]->getCOG();
      Point p(p32.x,p32.y);
      Point q = p.transform(fac.f1,fac.f2);
      m_oCOGsOut.push_back(q);
      // m_oCOGsOut.push_back(m_oInternalData[i]->getCOG().transform(fac.f1,fac.f2));
    }
    return m_oCOGsOut; 
  }

  // }}}


  const std::vector<Point32f> &RegionBasedBlobSearcher::getCOGsFloat(){
    // {{{ open

    m_oCOGsFloatOut.clear();
    for(unsigned int i=0;i<m_oInternalData.size();++i){
      FF &fac = m_oScaleFactors[i];
      Point32f p =  m_oInternalData[i]->getCOG();
      Point32f q = p.transform(fac.f1,fac.f2);
      m_oCOGsFloatOut.push_back(q);
      // m_oCOGsOut.push_back(m_oInternalData[i]->getCOG().transform(fac.f1,fac.f2));
    }
    return m_oCOGsFloatOut; 
  }

  // }}}


  const std::vector<Rect> &RegionBasedBlobSearcher::getBoundingBoxes(){
    // {{{ open

    m_oBBsOut.clear();
    for(unsigned int i=0;i<m_oInternalData.size();++i){
      FF &fac = m_oScaleFactors[i];
      m_oBBsOut.push_back(m_oInternalData[i]->getBoundingBox().transform(fac.f1,fac.f2));
    }
    return m_oBBsOut; 
  }

  // }}}
  const std::vector<RegionPCAInfo> &RegionBasedBlobSearcher::getPCAInfo(){
    // {{{ open

    m_oPCAInfosOut.clear();
    for(unsigned int i=0;i<m_oInternalData.size();++i){
      FF &fac = m_oScaleFactors[i];
      RegionPCAInfo pcaInfo = m_oInternalData[i]->getPCAInfo();
      //      pcaInfo.len1 = .. TODO adapt to factor
      (void)fac;
      m_oPCAInfosOut.push_back(pcaInfo);
    }
    return m_oPCAInfosOut; 
  }

  // }}}

  
  const std::vector<std::vector<Point> > &RegionBasedBlobSearcher::getBoundaries(){
    // {{{ open

    m_oBoundariesOut.clear();
    for(unsigned int i=0;i<m_oInternalData.size();++i){
      RegionBasedBlobSearcher::FF &fac = m_oScaleFactors[i];
      const vector<Point> &b = m_oInternalData[i]->getBoundary();
      m_oBoundariesOut.push_back(std::vector<Point>());
      std::vector<Point> &l = m_oBoundariesOut[i];
      for(unsigned int j=0;j<b.size();++j){
        l.push_back(b[j].transform(fac.f1,fac.f2));
      }
    }
    return m_oBoundariesOut;
    
  }

  // }}}
  
  const std::vector<int> &RegionBasedBlobSearcher::getBoundaryLengthsPOD(){
    // {{{ open

    m_oBoundaryLengthsPOD.clear();
    for(unsigned int i=0;i<m_oInternalData.size();++i){
      m_oBoundaryLengthsPOD.push_back(m_oInternalData[i]->getBoundaryLength());
    }
    return m_oBoundaryLengthsPOD; 
  }

  // }}}
  
  const std::vector<float> &RegionBasedBlobSearcher::getFormFactorsPOD(){
    // {{{ open

    m_oFormFactorsPOD.clear();
    for(unsigned int i=0;i<m_oInternalData.size();++i){
      m_oFormFactorsPOD.push_back(m_oInternalData[i]->getFormFactor());
    }
    return m_oFormFactorsPOD; 
  }

  // }}}
  
  const std::vector<ImageRegion*> &RegionBasedBlobSearcher::getRegions(){
    // {{{ open

    return m_oInternalData;
  }

  // }}}
  const std::vector<int> &RegionBasedBlobSearcher::getCOGsPOD(){
    // {{{ open
		getCOGs();
    return toPOD(m_oCOGsOut,m_oCOGsOutPOD);
  }

  // }}}

  const std::vector<float> &RegionBasedBlobSearcher::getCOGsFloatPOD(){
    // {{{ open
    getCOGsFloat();
    return toPOD(m_oCOGsFloatOut,m_oCOGsFloatOutPOD);
  }

  // }}}

  const std::vector<int> &RegionBasedBlobSearcher::getBoundingBoxesPOD(){
    // {{{ open 
    getBoundingBoxes();
    return toPOD(m_oBBs,m_oBBsOutPOD);
  }

  // }}}
  const std::vector<float> &RegionBasedBlobSearcher::getPCAInfoPOD(){
    // {{{ open
    getPCAInfo();
    return toPOD(m_oPCAInfos,m_oPCAInfosOutPOD);
  }

  // }}}

  const std::vector<std::vector<int> > &RegionBasedBlobSearcher::getBoundariesPOD(){
    // {{{ open
    getBoundaries();
    return toPOD(getBoundaries(),m_oBoundariesPOD);
  }

  // }}}




  
  Img8u *RegionBasedBlobSearcher::getImage(const Size &size, format fmt, const ImgBase *inputImage){
    // {{{ open

    //map<Size,map<format,Img8u*> > m_mmImages;
    sizemap &m = m_mmImages;
    sizemap::iterator i = m.find(size);
    if(i != m.end()){
      fmtmap::iterator j = (*i).second.find(fmt);
      if(j != (*i).second.end()){
        // this does only work if the images have valid timestanps
        // if(m_poInputImage->getTime() != (*j).second->getTime()){
        m_poConverter->apply(inputImage,(*j).second);
        //}
        return (*j).second;
      }else{
        Img8u *image = new Img8u(size,fmt);
        m_poConverter->apply(inputImage,image);
        ((*i).second)[fmt] = image;
        return image;
      }
    }else{
      Img8u *image = new Img8u(size,fmt); 
      m_poConverter->apply(inputImage,image);
      (m[size])[fmt]=image;
      return image;
    }
    ERROR_LOG("unknown error!");    
  }       

  // }}}

  void RegionBasedBlobSearcher::extractRegions(const ImgBase *image){
    // {{{ open

    m_oInternalData.clear(); // std::vector<BlobData*>
    m_oScaleFactors.clear(); // std::vector<FF> >  
    const Size &ims = image->getSize();
    
    for(unsigned int i=0;i<m_oFMRF.size(); ++i){
      FMCreator &fmc = *(m_oFMRF[i].fmc);
      RegionFilter &rf = *(m_oFMRF[i].rf);
      
      Range<unsigned int> sizeRange(rf.getSizeRange().minVal, rf.getSizeRange().maxVal);
      m_poRD->setConstraints(sizeRange.minVal,sizeRange.maxVal,
                              rf.getValueRange().castTo<icl64f>().minVal,
                              rf.getValueRange().castTo<icl64f>().maxVal);
      FF factor( (float)(ims.width)/fmc.getSize().width,(float)(ims.height)/fmc.getSize().height);
      const vector<ImageRegion> &vecBD = m_poRD->detect(fmc.getFM(getImage(fmc.getSize(),fmc.getFormat(),image)));
      for(unsigned int i=0;i<vecBD.size();++i){
        if(rf.validate(vecBD[i])){
          m_oInternalData.push_back(const_cast<ImageRegion*>(&(vecBD[i])));        
          m_oScaleFactors.push_back(factor);
        }        
      }
    }
  }

  // }}}

  /******************************************************************************
      void RegionBasedBlobSearcher::extractRegions(){
      m_oCenters.clear();
      m_oBBs.clear();
      m_oRegionPCAInfos.clear();
      
      for(unsigned int i=0;i<m_oFMCreators.size(); ++i){
      FMCreator &fmc = *(m_oFMCreators[i]);
      Img8u *image = getImage(fmc.getSize(),fmc.getFormat());
        Img8u *fm = fmc.getFM(image);
      RegionFilter &rf = *(fmc.getFilter());
      m_poRD->setRestrictions(rf.getMinSize(),rf.getMaxSize(),rf.getMinVal(),rf.getMaxVal());
      float facx =  (float)(m_poInputImage->getSize().width) / (float)(fmc.getSize().width);
      float facy =  (float)(m_poInputImage->getSize().height) / (float)(fmc.getSize().height);
      
      m_oCenters.push_back(std::vector<Point>());
      m_oBBs.push_back(std::vector<Rect>());
      m_oPCAInfos.push_back(std::vector<PCAInfo>());
      
      const vector<BlobData> &vecBD = m_poRD->detect(fm);
      for(vector<BlobData>::const_iterator it = vecBD.begin();it!= vecBD.end();it++){
      const BlobData &bd = *it;
      
      if(!rf.needSpecialFeatures()){
      Point pos = bd.getCOG();
      if(rf.ok(bd.getVal(),pos)){
      m_oCenters[i].push_back(pos.transform(facx,facy));
      }
      }else{
      Point pos = bd.getCOG();
      Rect bb = bd.getBoundingBox();
      PCAInfo pca = bd.getPCAInfo();
      if(rf.ok(bd.getVal(),pos,bb,pca)){
      m_oCenters[i].push_back(pos.transform(facx,facy));
      m_oBBs[i].push_back(bb.transform(facx,facy));
      m_oPCAInfos[i].push_back(pca);
      }
      }        
      }
      }
      }
  ****************************************************************************/

  void RegionBasedBlobSearcher::add(FMCreator* fmc, RegionFilter *rf){
    // {{{ open

    m_oFMRF.push_back(Plugin(fmc,rf));
  }

  // }}}

  void RegionBasedBlobSearcher::remove(FMCreator *fmc, bool release){
    // {{{ open
    for(unsigned int i=0;i<m_oFMRF.size();++i){
      if(m_oFMRF[i].fmc == fmc){
        if(release){
          delete m_oFMRF[i].fmc;
          delete m_oFMRF[i].rf;
        }
        m_oFMRF.erase(m_oFMRF.begin()+i);
        return;
      }
    }
  }
  // }}}
  
  void RegionBasedBlobSearcher::remove(RegionFilter *rf, bool release){
    // {{{ open
    for(unsigned int i=0;i<m_oFMRF.size();++i){
      if(m_oFMRF[i].rf == rf){
        if(release){
          delete m_oFMRF[i].fmc;
          delete m_oFMRF[i].rf;
        }
        m_oFMRF.erase(m_oFMRF.begin()+i);
        return;
      }
    }
  }
  
  // }}}


  RegionBasedBlobSearcher::Plugin RegionBasedBlobSearcher::getPlugin(FMCreator *fmc){
    // {{{ open

    for(unsigned int i=0;i<m_oFMRF.size();++i){
      if(m_oFMRF[i].fmc == fmc){
        return m_oFMRF[i];
      }
    }
    return Plugin();  
  }

  // }}}
  
  RegionBasedBlobSearcher::Plugin RegionBasedBlobSearcher::getPlugin(RegionFilter *rf){
    // {{{ open

    for(unsigned int i=0;i<m_oFMRF.size();++i){
      if(m_oFMRF[i].rf == rf){
        return m_oFMRF[i];
      }
    }
    return Plugin();  
  }

  // }}}

  void RegionBasedBlobSearcher::removeAll(bool release){
    // {{{ open
    if(release){
      for(unsigned int i=0;i<m_oFMRF.size();++i){
        delete m_oFMRF[i].fmc;
        delete m_oFMRF[i].rf;
      }
    }
    m_oFMRF.clear();
  }
  
  // }}}

}