//
// This file is part of 3DCellAtlas. 
// Copyright (C) 2015 George W. Bassel and collaborators.
//
// If you use 3DCellAtlas in your work, please cite:
//   http://dx.doi.org/10.1105/tpc.15.00175
//
// 3DCellAtlas is an AddOn for MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// 3DCellAtlas and MorphoGraphX are free software, and are licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#ifndef ClusterMap_H
#define ClusterMap_H

#include "Geometry.hpp"
#include "Mesh.hpp"

//#include <CellAtlasUtils.hpp>

using namespace std;

namespace mgx {

// if in 2D mode only key 0 and 1 are used
struct ManualCluster{

  std::map<int, double> val;
  int label;

};

double realToImageCoord(Point2d minMax, double value, double gridSize);
double imageToRealCoord(Point2d minMax, double value, double gridSize);
Point2d minMaxFromMap(AttrMap<int,double>& inputMap, std::set<int>& keys);

// TODO refactor
// new class just to take care of the heatmap
class ClusterMap{

  public:

  // cell data points
  std::set<int> cellLabels;
  std::vector<AttrMap<int, double> > cellFeatures;
  std::map<QString, int> attrIdxMap;

  std::vector<Point2d> minMaxV;

  // min and max values for the display
  Point2d xMinMax;
  Point2d yMinMax;

  bool customMinMax;

  bool mode2D;
  std::pair<int, int> attrIdx2D;

  double heatMax;

  bool showParentLabels;

  // manually specified clusters
  std::vector<ManualCluster> cluster; // each cluster is a double (pos) defined by the attrIdx

  int activeCluster;

  std::vector<Point2d> maximaHeatMap;
  std::map<int,int> cellLabelMaximaIdxMap;
  std::map<int,int> maximaIdxManualClusterIdxMap;

  QString selectedX, selectedY, selectedHeat;

  // gaussian clusters
  double sigma;

  // size of the GUI image
  bool largeDots;

  int gridSize;
  int gridFactor;
  // view array (data of the GUI image)
  std::vector<std::vector<double> > viewData;

  // the current QImage
  QImage clusterMapImage;

  std::unordered_map<std::pair<int, int>, int> imageBackgroundMap;

  std::map<int, int> parentLabels;
  std::map<std::pair<int, int>, int> imageLabelMap;


  // input values
  void addCellFeature(QString featureName, AttrMap<int, double>& newFeature, std::set<int>& selectedCells) {
    if(cellFeatures.size() == 0){
      //typedef std::pair<int, double> IntDoubleP;
      std::set<int> allLabels;
      //forall(IntDoubleP p, newFeature){
      //  allLabels.insert(p.first);
      //}

      cellLabels = selectedCells;//allLabels;
      
    }
    std::cout << "in add " << featureName.toLocal8Bit().constData() << std::endl;
    if(attrIdxMap.find(featureName) == attrIdxMap.end()){
      std::cout << "added " << std::endl;
      cellFeatures.push_back(newFeature);
      attrIdxMap[featureName] = cellFeatures.size();
      Point2d minMax = minMaxFromMap(cellFeatures[attrIdxMap[featureName]-1], cellLabels);
      minMaxV.push_back(minMax);
    }
    
  }
  double scaledValue(double value, int measureIdx)
  {
    double diff = minMaxV[measureIdx].y()-minMaxV[measureIdx].x();
    if(diff == 0) return 0;
    return ((value - minMaxV[measureIdx].x())/diff);
  }

  void resetCellFeatures() {
    std::vector<AttrMap<int, double> > cellFeaturesNew;
    std::vector<Point2d> minMaxVNew;

    std::map<QString, int> attrIdxMapNew;
    cellFeatures = cellFeaturesNew;
    attrIdxMap = attrIdxMapNew;
    minMaxV = minMaxVNew;
  }

  void setActiveMeasures(QString measure1, QString measure2, QString measureHeat);

  // create the cell display
  void createCellView();

  // create the heatmap display
  void createHeatMap2D();

  QImage createImage(bool heatMapMode, bool hide0, bool highlightNon0, int backgroundMode); // in heatmapsmode: draw local maxima crosses and scale automatically

  // set cluster postion
  void addCluster(QString measure1, QString measure2, double value1, double value2);
  void updateCluster(int clusterIdx, QString measure, double value, bool xCoord = true);
  bool nearbyManualCluster(QString measure1, QString measure2, Point2d pos, int& maxIdx);
  void resetClusters(){
    std::vector<ManualCluster> newClusters;
    cluster = newClusters;
  }

  void setParentsCellView();
  void setParentsHeatMapView();

  void findMaximaHeatMap();
  void relateCellsToMaxima();

  // initialize the data array with the right size (gridSize)
  void initDataArray();

  void calcHeatMax();

  // return the attr value for a specific label and measure
  double getValue(QString measure, int label);


  double getClusterValue(QString measure, int clusterIdx);
  void getNearestCluster(Point2d imageP, int idx1, int idx2, double& minDis, int& minIdx);
  void setBackground();
};



}

#endif