/*
 * hough implementation
 *
 * Copyright (C) Dorit Borrmann, Jan Elseberg, Kai Lingemann, Andreas Nuechter, Remus Dumitru
 *
 * Released under the GPL version 3.
 *
 */

#include "newmat/newmatap.h"
using namespace NEWMAT;
#include "shapes/hough.h"
#include "slam6d/globals.icc"
#include <math.h>
#include <time.h>
#include <limits>
#include "shapes/quadtree.h"
#include <errno.h>
#include <iterator>

#ifdef _MSC_VER
#include <windows.h>
#include <direct.h>
#else
#include <dlfcn.h>
#endif 
#include <sys/stat.h>

/**
  * Hough Constructor.
  * Loads the Configfile, located in "bin/hough.cfg" and initializes the
  * accumulator. For details about the implemented Hough methods please read:
  *
  * Dorit Borrmann, Jan Elseberg, Kai Lingemann, and Andreas Nüchter. The 3D
  * Hough Transform for Plane Detection in Point Clouds - A Review and A new
  * Accumulator Design, Journal 3D Research, Springer, Volume 2, Number 2, 
  * March 2011.
  */

Hough::Hough(bool q, std::string configFile)
{
  quiet = q;

  // If the user has specified a configFile, load it
  if(configFile.size() > 0) {
    myConfigFileHough.LoadCfg(configFile.c_str());
    if (!quiet) {
	 std::cout << "Loaded Configfile" << std::endl;
      myConfigFileHough.ShowConfiguration();
    }
  }
}

Hough::Hough(Scan * GlobalScan, bool q, std::string configFile)
{
 
  quiet = q;
  if(configFile.size() > 0) {
    myConfigFileHough.LoadCfg(configFile.c_str());
    if(!quiet) {
	 std::cout << "Loaded Configfile" << std::endl;
      myConfigFileHough.ShowConfiguration();
    }
  }

  SetScan(GlobalScan);
}

void Hough::SetScan(Scan* scan)
{

  nrEntries = 0;
  maximum = false;

  planeCounter = 0;

  allPoints = new vector<Point>();

  DataXYZ points_red = scan->get("xyz reduced");
  for(unsigned int i = 0; i < points_red.size(); i++)
    {
    Point p(points_red[i]);
    allPoints->push_back(p);
    }

  switch(myConfigFileHough.Get_AccumulatorType()) {
    case 0:
      acc = new AccumulatorSimple(myConfigFileHough);
      break;
    case 1:
      acc = new AccumulatorBall(myConfigFileHough);
      break;
    case 2:
      acc = new AccumulatorCube(myConfigFileHough);
      break;
    case 3:
      acc = new AccumulatorBallI(myConfigFileHough);
      break;
  }
  srand(time(0)); // make the results actually random
  //srand(0); // make the results repeatable
}

/**
  * Desctructor.
  */

Hough::~Hough() {
  if(out.is_open()) {
    out.flush();
    out.close();
  }

  for(vector<ConvexPlane*>::iterator it = planes.begin(); 
      it != planes.end(); it++) {
      ConvexPlane* tmp = (*it);
      delete tmp;
  }
  delete acc;
  delete allPoints;
  
}

/**
  * Randomized Hough Transform
  */
int Hough::RHT() {

  if (!quiet) cout << "RHT" << endl;
  Point p1, p2, p3;
  double theta, phi, rho;
  int planeSize = 2000;

  unsigned int stop = (unsigned int)(allPoints->size()/100.0)*myConfigFileHough.Get_MinSizeAllPoints();
  int plane = 1;
  long start, end;
  start = GetCurrentTimeInMilliSec(); 
  int counter = 0;
  while( allPoints->size() > stop && 
          planes.size() < (unsigned int)myConfigFileHough.Get_MaxPlanes() &&
          counter < (int)myConfigFileHough.Get_TrashMax()) { 
    unsigned int pint = (int) (((*allPoints).size())*(rand()/(RAND_MAX+1.0)));

    p1 = (*allPoints)[pint];
    pint = (int) (((*allPoints).size())*(rand()/(RAND_MAX+1.0)));
    p2 = (*allPoints)[pint];
    pint = (int) (((*allPoints).size())*(rand()/(RAND_MAX+1.0)));
    p3 = (*allPoints)[pint];

    // check distance
    if(!distanceOK(p1, p2, p3)) continue;
    //cout << "Distance OK" << endl;
    // calculate Plane
    if(calculatePlane(p1, p2, p3, theta, phi, rho)) {
      // increment accumulator cell
      if(acc->accumulate(theta, phi, rho)) {
        end = GetCurrentTimeInMilliSec() - start;
        start = GetCurrentTimeInMilliSec();
        if (!quiet) cout << "Time for RHT " << plane << ": " << end << endl; 
        double * n = acc->getMax(rho, theta, phi);
	   if (!quiet) cout << rho << " " << theta << " " << phi << endl;
        planeSize = deletePoints(n, rho);
        delete[] n;
	   if (!quiet) cout << "Delete Points done " << plane << endl;
        if(planeSize < (int)myConfigFileHough.Get_MinPlaneSize()) counter++;
        end = GetCurrentTimeInMilliSec() - start;
        start = GetCurrentTimeInMilliSec();
        if(!quiet) cout << "Time for Polygonization " << plane << ": " << end << endl; 
        acc->resetAccumulator();
        plane++;
        if(!quiet) cout << "Planes " << planes.size() << endl;
      }

    }
  }
  /*
  vector<Point>::iterator itr = allPoints->begin();
 
  Point p;
  start = GetCurrentTimeInMilliSec(); 
  while(itr != allPoints->end()) {
    p = *(itr);
    cout << p.x << " " << p.y << " " << p.z << endl;
    itr++;
  } 
  */
  return (int)planes.size();
}

/**
 * Standard Hough Transform
 */
void Hough::SHT() {
  vector<Point>::iterator itr = allPoints->begin();
 
  Point p;
  long start, end;
  start = GetCurrentTimeInMilliSec(); 
  while(itr != allPoints->end()) {
    p = *(itr);
    acc->accumulate(p);
    itr++;
  } 
  end = GetCurrentTimeInMilliSec() - start;
  start = GetCurrentTimeInMilliSec();
  if (!quiet) cout << "Time for SHT: " << end << endl; 
  
  if(myConfigFileHough.Get_PeakWindow()) {
    acc->peakWindow(myConfigFileHough.Get_WindowSize());
  }

  multiset<int*, maxcompare>* maxlist = acc->getMax();
  multiset<int*, maxcompare>::iterator it = maxlist->begin();
  int threshold = ((*it)[0] * myConfigFileHough.Get_PlaneRatio());
  unsigned int stop = (int)(allPoints->size()/100.0)*myConfigFileHough.Get_MinSizeAllPoints();
  
  while(it != maxlist->end() && 
        stop < allPoints->size() && 
        planes.size() < (unsigned int)myConfigFileHough.Get_MaxPlanes() && 
        (*it)[0] > threshold) {
    int* tmp = (*it);
    double * polar = acc->getMax(tmp);
    deletePoints(polar, polar[3]); 
    delete[] polar;
    it++;
  } 
  if (!quiet) cout << "Time for Polygonization: " << end << endl; 
  
  it = maxlist->begin();
    for(;it != maxlist->end(); it++) {
      int* tmp = (*it);
      delete[] tmp;
  } 
  
  delete maxlist; 
}

/**
  * Function that returns all the remaining points in the allPoints vector.
  * @return points that do not lie on an already detected plane
  */
double * const* Hough::getPoints(int &size) {
  size = allPoints->size();
  double ** returnPoints = new double*[allPoints->size()];
  for(unsigned int i = 0; i < allPoints->size(); i++) {
    Point p = (*allPoints)[i];
    returnPoints[i] = new double[3];
    returnPoints[i][0] = p.x;
    returnPoints[i][1] = p.y;
    returnPoints[i][2] = p.z;
  }
  return returnPoints;
}

/**
  * Deletes all points that lie on a list of planes. 
  * @param model the list of planes
  * @param size the number of remaining points after delete operation
  * @return the remaining points
  */
double * const* Hough::deletePoints(vector<ConvexPlane*> &model, int &size) {
  for(unsigned int i = 0; i < model.size(); i++) {
    deletePoints(model[i]->n, model[i]->rho); 
  }
  double ** returnPoints = new double*[allPoints->size()];
  for(unsigned int i = 0; i < allPoints->size(); i++) {
    Point p = (*allPoints)[i];
    returnPoints[i] = new double[3];
    returnPoints[i][0] = p.x;
    returnPoints[i][1] = p.y;
    returnPoints[i][2] = p.z;
  }
  size = allPoints->size();
  return returnPoints;
}

/**
 * Probabilistic Hough Transform
 */

void Hough::PHT() {
  unsigned int stop =
  (int)(allPoints->size()/100.0)*myConfigFileHough.Get_MinSizeAllPoints();
  bool *voted = new bool[allPoints->size()];
  for(unsigned int i = 0; i < allPoints->size(); i++) {
    voted[i] = false;
  }

  unsigned int i = 0;
  while(i < stop && planes.size() < (unsigned int)myConfigFileHough.Get_MaxPlanes()) {
    unsigned int pint = (int) (((*allPoints).size())*(rand()/(RAND_MAX+1.0)));

    if(!voted[i]) {
      Point p = (*allPoints)[pint];
      acc->accumulate(p);
      i++;
    }
    
  }
  // List of Maxima
  if(myConfigFileHough.Get_PeakWindow()) {
    acc->peakWindow(myConfigFileHough.Get_WindowSize());
  }
  multiset<int*, maxcompare>* maxlist = acc->getMax();
//  cout << "Mean "  << acc->mean() << endl;
//  cout << "Variance "  << acc->variance() << endl;
  
  multiset<int*, maxcompare>::iterator it = maxlist->begin();
  int threshold = ((*it)[0] * myConfigFileHough.Get_PlaneRatio());
  while(it != maxlist->end() && 
        stop < allPoints->size() && 
        planes.size() < (unsigned int)myConfigFileHough.Get_MaxPlanes() && 
        (*it)[0] > threshold) {
     
    int* tmp = (*it);
    double * tmp2 = acc->getMax(tmp);
    deletePoints(tmp2, tmp2[3]);
    delete [] tmp2;
    it++;
  } 
  it = maxlist->begin();
    for(;it != maxlist->end(); it++) {
      int* tmp = (*it);
      delete[] tmp;
  } 
  
  delete maxlist; 
  delete[] voted;
}

/**
 * Progressive Probabilistic Hough Transform
 */

void Hough::PPHT() {
  unsigned int stop =
  (int)(allPoints->size()/100.0)*myConfigFileHough.Get_MinSizeAllPoints();
  while(stop < allPoints->size() && 
        planes.size() < (unsigned int)myConfigFileHough.Get_MaxPlanes()) {
    bool *voted = new bool[allPoints->size()];
    for(unsigned int i = 0; i < allPoints->size(); i++) {
      voted[i] = false;
    }
   
    unsigned int pint;
    do { 
      pint = (int) (((*allPoints).size())*(rand()/(RAND_MAX+1.0)));

      Point p = (*allPoints)[pint];
      if(!voted[pint]) {
        double * angles = acc->accumulateRet(p);
        if(angles[0] > -0.0001) {
          double * n = polar2normal(angles[1], angles[2]);
          deletePoints(n, angles[0]);
          acc->resetAccumulator();
          delete [] n;
        } else {
          voted[pint] = true;
        }
        delete [] angles;
      }

    } while(!voted[pint]);
   
	delete[] voted;
  }
}

/**
 * Adaptive Probabilistic Hough Transform
 */

void Hough::APHT() {

  int max = 0;
  int maxpos = 0;
  vector<int*> mergelist = vector<int*>(); 
  int stability[20] = {0}; 
  do {
  
    //randomly select points and perform HT for them
    vector<int*> altlist = mergelist; 
    mergelist = vector<int*>(); 
    multiset<int*,valuecompare> maxlist = multiset<int*,valuecompare>();
    for(int i = 0; i < 10; i++) {
      unsigned int pint = (int) (((*allPoints).size())*(rand()/(RAND_MAX+1.0)));
      Point p = (*allPoints)[pint];
      int * max = acc->accumulateAPHT(p);
      // store the maximum cell touched by the HT
      maxlist.insert(max);
    }
 
    // merge active with previous list
    multiset<int*,valuecompare>::iterator mi = maxlist.begin(); 
    vector<int*>::iterator ai = altlist.begin(); 

    int i = 0;
    while(i < 20) {
      bool mi_oder_ai = false;
      if(ai == altlist.end()) {
        if(mi == maxlist.end()) {
          break;
        } else {
          mi_oder_ai = true;
        }
      } else if(mi == maxlist.end()) {
        mi_oder_ai = false;
      } else if((*mi)[0] <= (*ai[0])) {
        mi_oder_ai = false;
      } else {
        mi_oder_ai = true;
      }
    
      int *tmp;
      if(mi_oder_ai) {
        tmp = (*mi);
        mi++;
      } else {
        tmp = (*ai);
        ai++;
      }

      bool insert = true;
      for(vector<int*>::iterator it = mergelist.begin(); it != mergelist.end();
    it++) {
        if(myConfigFileHough.Get_AccumulatorType() != 2) {
          if( sqrt(
            ((*it)[3] - (tmp[3])) * ((*it)[3] - (tmp[3])) +
            ((*it)[1] - (tmp[1])) * ((*it)[1] - (tmp[1])) +
            ((*it)[2] - (tmp[2])) * ((*it)[2] - (tmp[2])) ) < 3.0) {
            insert = false;
            break;
          }
        } else { 
          if( sqrt(
            ((*it)[4] - (tmp[4])) * ((*it)[4] - (tmp[4])) +
            ((*it)[1] - (tmp[1])) * ((*it)[1] - (tmp[1])) +
            ((*it)[2] - (tmp[2])) * ((*it)[2] - (tmp[2])) +
            ((*it)[3] - (tmp[3])) * ((*it)[3] - (tmp[3])) ) < 3.0) {
            insert = false;
            break;
          }
        }
      }
      if(insert) {
        mergelist.push_back(tmp);
        i++;
      }
    }
    // compare lists, calculate stability
    i = 0;
    for(unsigned int i = 1; i < altlist.size(); i++) {
      for(unsigned int j = 0; j < i; j++) {
        int * tmp1 = mergelist[j];
        bool treffer = false;
        for(unsigned int k = 0; k < i; k++) {
          int * tmp2 = altlist[k];
          if(myConfigFileHough.Get_AccumulatorType() != 2) {
            if( sqrt(
              ((tmp2)[3] - (tmp1[3])) * ((tmp2)[3] - (tmp1[3])) +
              ((tmp2)[1] - (tmp1[1])) * ((tmp2)[1] - (tmp1[1])) +
              ((tmp2)[2] - (tmp1[2])) * ((tmp2)[2] - (tmp1[2])) ) < 3.0) {
              treffer = true;
              break;
            }
          } else { 
            if( sqrt(
              ((tmp2)[4] - (tmp1[4])) * ((tmp2)[4] - (tmp1[4])) +
              ((tmp2)[1] - (tmp1[1])) * ((tmp2)[1] - (tmp1[1])) +
              ((tmp2)[2] - (tmp1[2])) * ((tmp2)[2] - (tmp1[2])) +
              ((tmp2)[3] - (tmp1[3])) * ((tmp2)[3] - (tmp1[3])) ) < 3.0) {
              treffer = true;
              break;
            }
          }
        }
        if(!treffer) {
          stability[i-1] = -1;
          break;
        }
      }
      stability[i-1]++;
    }
    for(int i = mergelist.size(); i < 20; i++) {
      stability[i] = 0;
    }
    // determine stability count, 
    // a set of n entries is considered to be stable, if all planes in the set
    // are the maximal entries of the mergelist in this iteration and have also
    // been the maximal entries in the previous iteration (the order in the set
    // does not count). The stability count for a number n is the number of
    // iterations for which the set of size n has been stable.
    // The maximum stability count is the stability count for the size n, that
    // is the maximum of all stability counts.
    max = 0;
    maxpos = 0;
    for(int i = 0; i < 20; i++) {
      if(stability[i] >= max) {
        max = stability[i];
        maxpos = i;
      }
    }
    // repeat until maximum stability count exceeds a threshold
  } while(max < (int)myConfigFileHough.Get_AccumulatorMax()
  && stability[myConfigFileHough.Get_MaxPlanes()] < 
  myConfigFileHough.Get_AccumulatorMax() * myConfigFileHough.Get_PlaneRatio()
  );
  if(stability[myConfigFileHough.Get_MaxPlanes()] >= myConfigFileHough.Get_AccumulatorMax() * myConfigFileHough.Get_PlaneRatio()) {
    maxpos = myConfigFileHough.Get_MaxPlanes();
  }
  
  for(int i = 0; i <= maxpos; i++) {
    double * n = acc->getMax(mergelist[i]); 
    deletePoints(n, n[3]);
   
    delete[] n;
  }
      
}

/**
  * Verifies the distance criterion needed for the RHT. The distance between the
  * three selected points may not exceed a maximal or fall below a minimal
  * distance. 
  */
bool Hough::distanceOK(Point p1, Point p2, Point p3) {
  // p1 - p2
  double distance = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) + (p1.z - p2.z) * (p1.z - p2.z); 
  if(sqrt(distance) < myConfigFileHough.Get_MinDist()) return false;
  if(sqrt(distance) > myConfigFileHough.Get_MaxDist()) return false;

  // p2 - p3
  distance = (p3.x - p2.x) * (p3.x - p2.x) + (p3.y - p2.y) * (p3.y - p2.y) + (p3.z - p2.z) * (p3.z - p2.z); 
  if(sqrt(distance) < myConfigFileHough.Get_MinDist()) return false;
  if(sqrt(distance) > myConfigFileHough.Get_MaxDist()) return false;

  // p3 - p1
  distance = (p1.x - p3.x) * (p1.x - p3.x) + (p1.y - p3.y) * (p1.y - p3.y) + (p1.z - p3.z) * (p1.z - p3.z); 
  if(sqrt(distance) < myConfigFileHough.Get_MinDist()) return false;
  if(sqrt(distance) > myConfigFileHough.Get_MaxDist()) return false;

  return true;
  
}

/**
  * Calculates the polar coordinates (rho, theta, phi) of the plane spanned by
  * three points p1, p2, and p3.
  */
bool Hough::calculatePlane(Point p1, Point p2, Point p3, double &theta, double &phi, double &rho) {

  double v1[3];
  double v2[3];
  double n[3];

  v1[0] = p3.x - p2.x;
  v1[1] = p3.y - p2.y;
  v1[2] = p3.z - p2.z;
  v2[0] = p1.x - p2.x;
  v2[1] = p1.y - p2.y;
  v2[2] = p1.z - p2.z;

  Cross(v1,v2,n);

  if(sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]) < 0.000000001) return false;

  Normalize3(n);

  rho = p1.x * n[0] + p1.y * n[1] + p1.z * n[2];
  
  if(rho < 0) {
    rho = -rho;
    for(int i = 0; i < 3; i++) {
      n[i] = -n[i];
    }
  }
  double polar[3];
  toPolar(n, polar);

  phi = polar[0];
  theta = polar[1];

  if((fabs(theta) < 0.001) && (fabs(phi) < 0.001) ) return false;
  return true;
}

/**
  * Improved function for point deletion.
  */
int Hough::deletePointsQuad(double * n, double rho) {

  if (!quiet) cout << allPoints->size() ;
  Normalize3(n);
  vector<Point> *nallPoints = new vector<Point>();

  vector<Point> planePoints;
  out << n[0] << " " << n[1] << " " << n[2] << " " << rho << " ";
  
  Point p;
  vector<Point>::iterator itr = allPoints->begin();
  
  while(itr != allPoints->end()) {
    p = *(itr);
    // if point close to plane, delete it
    double distance = p.x * n[0] + p.y * n[1] + p.z*n[2] - rho;
    if( fabs(distance) < myConfigFileHough.Get_MaxPointPlaneDist()) {
   
      planePoints.push_back(p);

    } 
   itr++;
  }
  double n2[4];

  // calculating the best fit plane
  calcPlane(planePoints, n2);
  out << n2[0] << " " << n2[1] << " " << n2[2] << " " << n2[3] << " " ;
  
  itr = allPoints->begin();
  planePoints.clear();  
 
  while(itr != allPoints->end()) {
    p = *(itr);
    // if point close to plane, delete it
    double distance = p.x * n2[0] + p.y * n2[1] + p.z*n2[2] - n2[3];
    if( fabs(distance) < myConfigFileHough.Get_MaxPointPlaneDist()) {
   
      planePoints.push_back(p);

    } else {
      nallPoints->push_back(p);
    }
   itr++;
  }
  delete allPoints;
  allPoints = nallPoints;
  
  if (!quiet) cout << "Planepoints " << planePoints.size() << endl;
  int nr_points = planePoints.size();
  // TODO Clustering
  double min_angle = rad(1.0);
  double _phi, _theta;
	double **pppoints;
	pppoints = new double*[nr_points];

  for (unsigned int i = 0; i < planePoints.size(); i++) {
    pppoints[i] = new double[3];

    Point p(planePoints[i]);
    double m[3];
    m[0] = p.x;
    m[1] = p.y;
    m[2] = p.z;

    Normalize3(m);
    _phi = acos(m[2]);
    double costheta = acos(m[0]/sin(_phi));
    double sintheta = m[1]/sin(_phi);
    double EPS = 0.0001;

    if(fabs(sin(costheta) - sintheta) < EPS) {
      _theta = costheta;
    } else if(fabs( sin( 2*M_PI - costheta ) - sintheta ) < EPS) {
      _theta = 2*M_PI - costheta;
    } else {
      _theta = 0;
      cout << "Error JP" << endl;
    }

    pppoints[i][0] = _phi;
    pppoints[i][1] = _theta;
    pppoints[i][2] = sqrt(p.x*p.x + p.y*p.y + p.z*p.z);
  }
  planePoints.clear();

  QuadTree tree(pppoints, nr_points , 0.3, min_angle);
	vector<set<double *> > cps;
  cps = tree.getClusters();
  
  unsigned int max_points = 0;
  int index = -1;
  for (unsigned int i = 0; i < cps.size(); i++) {
    if (max_points < cps[i].size()) {
      max_points = cps[i].size();
      index = i;
    }
  }
  for (unsigned int i = 0; i < cps.size(); i++) {
    for (set<double *>::iterator it = cps[i].begin(); 
        it != cps[i].end(); it++) {
      Point p;
      p.x = (*it)[2] * cos( (*it)[1] ) * sin( (*it)[0] );
      p.y = (*it)[2] * sin( (*it)[1] ) * sin( (*it)[0] );
      p.z = (*it)[2] * cos( (*it)[0] );
      if ((int)i != index) {
        allPoints->push_back(p);
      } else {
        planePoints.push_back(p);
      }
    }
  }

  double n4[4];
  calcPlane(planePoints, n4);

  ConvexPlane * plane1 = new ConvexPlane(n4, planePoints);
  planes.push_back(plane1);
	

  for (int i = 0; i < nr_points; i++) {
    delete[] pppoints[i];
  }
  delete[] pppoints;
  return max_points;
  
  // ENDE
}

/**
  * Deletes points from allPoints that lie on the given plane. All points from
  * the biggest cluster on that plane are deleted. If the cluster is
  * sufficiently planar, the convex hull is added to the result list.
  *
  * @param n normal vector of the plane
  * @param rho distance between plane and origin
  * @return the number of points in the deleted cluster plane
  */
int Hough::deletePoints(double * n, double rho) {
  char direction = ' ';
  Normalize3(n);
  vector<Point> *nallPoints = new vector<Point>();

  vector<Point> planePoints;
  
  Point p;
  vector<Point>::iterator itr = allPoints->begin();
 
  while(itr != allPoints->end()) {
    p = *(itr);
    // if point close to plane, delete it
    double distance = p.x * n[0] + p.y * n[1] + p.z*n[2] - rho;
    if( fabs(distance) < myConfigFileHough.Get_MaxPointPlaneDist()) {
   
      planePoints.push_back(p);

    } 
   itr++;
  }
  double n2[4];
  // calculating the best fit plane
  double D = calcPlane(planePoints, n2);
  
  bool nocluster = false;
  
  if(D > myConfigFileHough.Get_MinPlanarity()) {
    nocluster = true;
  } 
  
  if(fabs(n2[0]) < fabs(n2[1])) {
    if(fabs(n2[1]) < fabs(n2[2])) {
      direction = 'z';
    } else {
      direction = 'y';
    } 
  } else if (fabs(n2[2]) < fabs(n2[0])){
    direction = 'x';
  } else {
    direction = 'z';
  }

  itr = allPoints->begin();
  planePoints.clear();  
 
  vPtPair planePairs;
  double minx, maxx, miny, maxy;
  minx = 1000000;
  miny = 1000000;
  maxx = -1000000;
  maxy= -1000000;
  while(itr != allPoints->end()) {
    p = *(itr);
    // if point close to plane, delete it
    double distance = p.x * n2[0] + p.y * n2[1] + p.z*n2[2] - n2[3];
    if( fabs(distance) < myConfigFileHough.Get_MaxPointPlaneDist()) {
   
      Point tmp, p2;
      double distance = p.x * n2[0] + p.y * n2[1] + p.z*n2[2] - n2[3];
      tmp.x = p.x - distance * n2[0];
      tmp.y = p.y - distance * n2[1];
      tmp.z = p.z - distance * n2[2];
      switch(direction) {
        case 'x': p2.x = tmp.y;
                  p2.y = tmp.z;
                  break; 
        case 'y': p2.x = tmp.x;
                  p2.y = tmp.z;
                  break;
        case 'z': p2.x = tmp.x;
                  p2.y = tmp.y; 
                  break;
        default: cout << "OHOH" << endl;
      }
      p2.z = -1;
      if(p2.x < minx) minx = p2.x; 
      if(p2.y < miny) miny = p2.y; 
      if(p2.x > maxx) maxx = p2.x; 
      if(p2.y > maxy) maxy = p2.y; 
      PtPair myPair(p,p2);
      
      planePairs.push_back(myPair);

    } else {
      nallPoints->push_back(p);
    }
    itr++;
  }
  delete allPoints;
  allPoints = nallPoints;

  int region = -1;
  if(planePairs.size() > 2) {
    region = cluster(planePairs, minx, maxx, miny, maxy);
  }
  // delete points from this list
  list< double*> point_list;
  
  vPtPair::iterator vitr;  
  vector<Point> tmp_points;
  for(vitr = planePairs.begin(); vitr != planePairs.end(); vitr++) {
    
  // Case distinction x-z or x-y or y-z
    p = (*vitr).p1;
    Point p2 = (*vitr).p2;
    if(fabs(p2.z - region) < 0.1) {
      double * point = new double[2];
      point[0] = p2.x;
      point[1] = p2.y;
      point_list.push_back(point);
      tmp_points.push_back(p);
    } else {
      allPoints->push_back(p);
    }
  }
  D = calcPlane(tmp_points, n2);
  
  nocluster = false;
  
  if(D > myConfigFileHough.Get_MinPlanarity()) {
    nocluster = true;
  }
  unsigned int maxPlane = point_list.size();
  
  // color points
  unsigned char rgb[3];
  for(int x = 0; x < 3; x++) {
    rgb[x] = (unsigned char)((255)*(rand()/(RAND_MAX+1.0)));
  }
  for(itr = tmp_points.begin(); itr != tmp_points.end(); itr++) {
      p = (*itr);
      if(nocluster || maxPlane >= myConfigFileHough.Get_MinPlaneSize()) {
        p.rgb[0] = 0;
        p.rgb[1] = 0;
        p.rgb[2] = 0;
      } else { 
        p.rgb[0] = rgb[0];
        p.rgb[1] = rgb[1];
        p.rgb[2] = rgb[2];
      }
      coloredPoints.push_back(p);
  }
  tmp_points.clear();

  if(nocluster || maxPlane < myConfigFileHough.Get_MinPlaneSize()) {
    for(list<double* >::iterator it = point_list.begin();
      it != point_list.end(); ) {
      double* tmp = (*it);
      it = point_list.erase(it);
      delete[] tmp;
    }
    point_list.clear();
    return maxPlane;
  }

  // If plane ok, calculate convex hull
  vector<double *> convex_hull;
  ConvexPlane::JarvisMarchConvexHull(point_list,convex_hull);
  
  for(list<double* >::iterator it = point_list.begin();
      it != point_list.end(); ) {
      double* tmp = (*it);
      it = point_list.erase(it);
      delete[] tmp;
  }
  point_list.clear();
  
  if(nocluster) {
    for(vector<double* >::iterator it = convex_hull.begin();
      it != convex_hull.end(); it++) {
      double* tmp = (*it);
      delete[] tmp;
    } 
    return maxPlane; 
  }

  ConvexPlane * plane1 = new ConvexPlane(n2, n2[3], direction, convex_hull);
  plane1->pointsize = maxPlane;
  planes.push_back(plane1);

  if(!quiet) cout << "Points left " << allPoints->size() << "\n";
  return maxPlane;
  // ENDE
}

/**
  * Clustering using Two-Pass algorithm
  */
int Hough::cluster(vPtPair &pairs, double minx, double maxx, double miny, double maxy) {
  if(pairs.size() < 3) return -1;
  vPtPair::iterator vitr;
  vector<set<int> > linked;
  double factor = myConfigFileHough.Get_PointDist();
  int xlength = 1 + (maxx - minx) / factor;
  int ylength = 1 + (maxy - miny) / factor; 

  vector< vector<int> > colors;

  for(int i = 0; i < ylength; i++) {
    vector<int> *tmp = new vector<int>;
    colors.push_back(*tmp);
    for(int j = 0; j < xlength; j++) {
      colors[i].push_back(0);
    }
    delete tmp;
  }
 
  //int colors[ylength][xlength];
  vector< vector<bool> > points;
  for(int i = 0; i < ylength; i++) {
    vector<bool> *tmp = new vector<bool>;
    points.push_back(*tmp);
    for(int j = 0; j < xlength; j++) {
      points[i].push_back(false);
    }
    delete tmp;
  }

  int region = 0;
  
  for(vitr = pairs.begin(); vitr != pairs.end(); vitr++) {
    int x = (int)(((*vitr).p2.x - minx)  / (maxx - minx) * xlength - 0.5);   
    int y = (int)(((*vitr).p2.y - miny)  / (maxy - miny) * ylength - 0.5);   
    points[y][x] = true; 
  }
  int up, left;
  for(int y = 0; y < ylength; y++) {
    for(int x = 0; x < xlength; x++) {
      if(points[y][x]) {
        if(x==0) {
          left = 0;
        } else {
          left = colors[y][x-1];
        }
        if(y==0) {
          up = 0;
        } else {
          up = colors[y-1][x];
        }
        if (left == 0) {
          if (up == 0) {
            colors[y][x] = ++region;            // new region
            set<int> joined;
            joined.insert(region);
            linked.push_back(joined);
          } else {
            colors[y][x] = up;    // use upper region
          }
        } else if (up == 0) {
            colors[y][x] = left;  // use left region
        } else {
            colors[y][x] = up;    // use upper region (left is the same)
            if(left == up) {
              continue;
            }
            else {
              set<int> current;
              set<int> joined;
              joined.insert(up);
              joined.insert(left);
              for(vector<set<int> >::iterator itr = linked.begin(); itr != linked.end(); ) {
                current = (*itr);
                if(current.find(up) != current.end() || current.find(left) != current.end()) {
                  set<int> newjoined;
                  insert_iterator<set<int> > res_ins(newjoined, newjoined.begin());

                  set_union(joined.begin(), joined.end(), current.begin(), current.end(), res_ins); 
                  joined.clear();
                 // delete joined;
                  joined = newjoined;
                  itr=linked.erase(itr);
                } else {
                  itr++;
                }
              }
              linked.push_back(joined);
            }
          
        }
        
      }
    }
  }
  int *counter = new int[region+1];
  for(int i = 0; i <= region; i++) {
    counter[i] = 0;
  }
  for(int y = 0; y < ylength; y++) {
    for(int x = 0; x < xlength; x++) {
      counter[colors[y][x]]++;
    }
  }

  int *linkedindex = new int[linked.size()];
  for(unsigned int i = 0; i < linked.size(); i++) {
    linkedindex[i] = 0;
  }  
  set<int> current;
  for(unsigned int i = 0; i < linked.size(); i++) {
    current = linked[i];
    for(set<int>::iterator it = current.begin(); it != current.end(); it++) {
      linkedindex[i] += counter[(*it)];
    }
  }
  
  delete[] counter;

  int maxindex = 0;
  for(unsigned int i = 1; i < linked.size(); i++) {
    if(linkedindex[i] > linkedindex[maxindex]) {
      maxindex = i;
    }
  }
 
  delete[] linkedindex;
  set<int> max = linked[maxindex];
  int zaehler = 0;
  for(vitr = pairs.begin(); vitr != pairs.end(); vitr++) {
    int x = (int)(((*vitr).p2.x - minx) / (maxx - minx) * xlength - 0.5);   
    int y = (int)(((*vitr).p2.y - miny) / (maxy - miny) * ylength - 0.5);   
    if(max.find(colors[y][x]) != max.end()) {
      (*vitr).p2.z = maxindex;
      zaehler++;
    } else {
      (*vitr).p2.z = -1;
    } 
  }
  
   
  /*            
  for(vector<set<int> >::iterator itr = linked.begin(); itr != linked.end(); ) {
    set<>current = (*itr);
                if(current.find(up) != current.end() || current.find(left) != current.end()) {
  */  
  return maxindex;
}


/**
  * Writes <the convex hull of each detected plane to the directory given in the
  * config file. Also writes a list of detected planes to the directory.
  */
int Hough::writePlanes(int startCount) {
  int counter = startCount;
  string blub = "";
  blub = blub + myConfigFileHough.Get_PlaneDir();
#ifdef _MSC_VER
  int success = mkdir(blub.c_str());
#else
  int success = mkdir(blub.c_str(), S_IRWXU|S_IRWXG|S_IRWXO);
#endif

  if(success != 0 && errno != EEXIST) { 
    cerr << "Creating directory " << blub << " failed" << endl;
    return -1;
  }
  
  if(!quiet) cout << "Writing " << planes.size() << " Planes to " << blub << endl;
  
  ofstream out;
  string blub3 = blub + "/planes.list";
  out.open(blub3.c_str());
  for(vector<ConvexPlane*>::iterator it = planes.begin(); it != planes.end(); it++) {
    string blub2 = blub + "/plane"+ to_string(counter,3) + ".3d";
    out << "Plane " << blub2 << endl;
    (*it)->writePlane(blub2, counter);
    counter++;
  }

  out.close();
  out.flush();

  return (int)planes.size();
}

/**
  * Writes the convex hull of each detected plane to a file.
  */
void Hough::writePlanes(std::string filePrefix) 
{
  int counter = 0;
  
  if (!quiet) std::cout << "There are " << planes.size() << " planes." << std::endl;
    
  for(vector<ConvexPlane*>::iterator it = planes.begin(); it != planes.end(); it++) 
  {
    std::stringstream ss;
    ss << filePrefix << "_" << counter << ".txt";
    (*it)->writeNormal(ss.str(), counter);
    //(*it)->writePlane(ss.str(), counter);
    counter++;
  }

}
/**
  * Writes the remaining points from allPoints to the plane directory given in
  * the config file.
  */
void Hough::writePlanePoints(string filename) {

  ofstream out;
  out.open(filename.c_str());

  Point p;
  vector<Point>::iterator itr = coloredPoints.begin();
 
  while(itr != coloredPoints.end()) {
    p = *(itr);
    out << p.x << " " << p.y << " " << p.z << " " << (int)p.rgb[0] << " " << (int)(p.rgb[1]) << " " << (int)(p.rgb[2]) << endl;
    itr++;
  }
  itr = allPoints->begin();
  
  while(itr != allPoints->end()) {
    p = *(itr);
    out << p.x << " " << p.y << " " << p.z << " " << 255 << " " << 255 << " " << 255 << endl;
    itr++;
  }
  out.close();
}

/**
  * Writes the remaining points from allPoints to the plane directory given in
  * the config file.
  */
void Hough::writeAllPoints(int index, vector<Point> points) {

  string blub = "";
  blub = blub + myConfigFileHough.Get_PlaneDir() + "/scans";
#ifdef _MSC_VER
  int success = mkdir(blub.c_str());
#else
  int success = mkdir(blub.c_str(), S_IRWXU|S_IRWXG|S_IRWXO);
#endif

  if(success != 0 && errno != EEXIST) { 
    cerr << "Creating directory " << blub << " failed" << endl;
    return;
  }
  
  blub = blub + "/scan" + to_string(index,3) + ".3d";

  ofstream out;
  out.open(blub.c_str());

  Point p;
  vector<Point>::iterator itr = points.begin();
  
  while(itr != points.end()) {
    p = *(itr);
    out << p.x << " " << p.y << " " << p.z << " " << (int)p.rgb[0] << " " << (int)(p.rgb[1]) << " " << (int)(p.rgb[2]) << endl;
    itr++;
  }
  out.close();
}


/**
  * Given a set of points this function will calculate the best fit plane
  * @param the set of points
  * @param the plane description as normal vector and distance to origin
  */
double calcPlane(vector<Point> &ppoints, double plane[4]) {
  SymmetricMatrix A(3);
	A = 0;
  int n;
  n = ppoints.size();
  if(n < 3) return 0;
  double cx, cy, cz;
  cx = 0.0;
  cy = 0.0;
  cz = 0.0;

  for (int i = 0; i < n; i++) {
    Point p = ppoints[i];
    cx += p.x;
    cy += p.y;
    cz += p.z;
  }
  cx /= n;
  cy /= n;
  cz /= n;
       
  for (int i = 0; i < n; i++) {
    Point p = ppoints[i];
    A(1, 1) += (p.x - cx)*(p.x - cx);
    A(2, 2) += (p.y - cy)*(p.y - cy);
    A(3, 3) += (p.z - cz)*(p.z - cz);
    A(1, 2) += (p.x - cx)*(p.y - cy);
    A(1, 3) += (p.x - cx)*(p.z - cz);
    A(2, 3) += (p.y - cy)*(p.z - cz);
  }

  DiagonalMatrix D;
  Matrix V;
  try {
    Jacobi(A,D,V);
  } catch (ConvergenceException) {
    cout << "couldn't find plane..." << endl;
    return 0;
  }
  /*
     cout << "A: "<< endl << A << endl;
     cout << "D: "<< endl << D << endl;
     cout << "V: "<< endl << V << endl;
     */
  int index;
  D.MinimumAbsoluteValue1(index);
     
  plane[0] = V(1,index);
  plane[1] = V(2,index);
  plane[2] = V(3,index);
  plane[3] = plane[0]*cx + plane[1]*cy + plane[2]*cz;
  
  double sum = 0.0;
  for(int i = 1; i < 4; i++) {
    sum += D(i);
  }
  sum = D(index)/sum;
  return sum;
}

