#include <QtGui>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime>
#include "bsdetectionwidget.h"

using namespace std;


const int BSDetectionWidget::BACK_BLACK = 0;
const int BSDetectionWidget::BACK_WHITE = 1;
const int BSDetectionWidget::BACK_IMAGE = 2;
const int BSDetectionWidget::BACK_GRAD = 3;
const int BSDetectionWidget::BACK_GRADX = 4;
const int BSDetectionWidget::BACK_GRADY = 5;

const int BSDetectionWidget::DEFAULT_PEN_WIDTH = 1;



BSDetectionWidget::BSDetectionWidget (QWidget *parent)
{
  Q_UNUSED (parent);

  // Sets initial user inputs parameters
  setFocus ();
  grabKeyboard ();
  udef = false;
  nodrag = true;
  nbdettrials = 0;
  nbmaxdettrials = 0;

  // Initializes the gradient map and the auxiliary views
  gMap = NULL;
  accuview = NULL;
  strucview = NULL;
  profileview = NULL;
  idetview = NULL;

  // Sets initial user outputs parameters
  alternate = 0;
  verbose = false;
  background = BACK_IMAGE;
  bsBoundsVisible = false;
  blevel = 0;

  // Sets display parameters
  selectionColor = Qt::red;
  bsColor = Qt::blue;
  //bsHighColor = Qt::black;
  bsHighColor = Qt::yellow;
  bsPointsVisible = true;
  boundColor = Qt::green;
  //boundHighColor = Qt::black;
  boundHighColor = Qt::magenta;
}


BSDetectionWidget::~BSDetectionWidget ()
{
  if (accuview != NULL) delete accuview;
  if (strucview != NULL) delete strucview;
}


QSize BSDetectionWidget::openImage (const QString &fileName, int type)
{
  QSize newSize (0, 0);
  loadedImage.load (fileName);
  width = loadedImage.width ();
  height = loadedImage.height ();
  newSize = loadedImage.size ();
 
  augmentedImage = loadedImage;
  if (gMap != NULL) delete gMap;
  gMap = new VMap (width, height, getBitmap (augmentedImage), type);
  detector.setGradientMap (gMap);
  buildGradientImage (0);
 
  update ();
  if (idetview != NULL) idetview->setImage (&loadedImage, gMap);
  if (profileview != NULL) profileview->setImage (&loadedImage, gMap);
  if (strucview != NULL) strucview->setGradientImage (&gradImage);

  return newSize;
}


void BSDetectionWidget::setDefaults ()
{
  ifstream input ("gradient.txt", ios::in);
  if (input)
  {
    int gtval = gMap->getGradientThreshold ();
    input >> gtval;
    if (gtval >= 0 && gtval < 256)
      gMap->incGradientThreshold (gtval - gMap->getGradientThreshold ());
  }
}


int **BSDetectionWidget::getBitmap (const QImage &image)
{
  int w = image.width ();
  int h = image.height ();
  
  int **tabImage = new int*[h];
  for (int i = 0; i < h; i++)
  {
    tabImage[i] = new int[w];
    for(int j = 0; j < w; j++)
    {
      QColor c = QColor (image.pixel (j, h - i - 1));
      tabImage[i][j] = c.value ();
    }
  }
  return tabImage;
}


void BSDetectionWidget::toggleBackground ()
{
  if (background++ == BACK_GRADY) background = BACK_BLACK;
  if (background >= BACK_GRAD) buildGradientImage (background - BACK_GRAD);
}


bool BSDetectionWidget::saveAugmentedImage (const QString &fileName,
                                            const char *fileFormat)
{
  QImage aImage = augmentedImage;
  return (aImage.save (fileName, fileFormat));
}


void BSDetectionWidget::clearImage ()
{
  augmentedImage.fill (qRgb (255, 255, 255));
  update ();
}


void BSDetectionWidget::buildGradientImage (int dir)
{
  int w = gMap->getWidth ();
  int h = gMap->getHeight ();
  double gn[w * h];
  for (int j = 0; j < h; j++)
    for (int i = 0; i < w; i++)
    {
      if (dir == 2)
        gn[j * w + i] = gMap->getValue(i,j).y ();
      else if (dir == 1)
        gn[j * w + i] = gMap->getValue(i,j).x ();
      else
        gn[j * w + i] = gMap->magn (i, j);
    }
  double min = gn[0];
  double max = gn[0];
  for (int i = 1; i < w * h; i++)
  {
    if (max < gn[i]) max = gn[i];
    if (min > gn[i]) min = gn[i];
  }
  gradImage = QImage (w, h, QImage::Format_RGB32);
  for (int j = 0; j < h; j++)
    for (int i = 0; i < w; i++)
    {
      int val = (int) ((gn[(h - 1 - j) * w + i] - min) * 255 / (max - min));
      gradImage.setPixel (i, j, val + val * 256 + val * 256 * 256);
    }
  // gradImage.save ("hgradient.png");
}


void BSDetectionWidget::paintEvent (QPaintEvent *)
{
  QPainter painter (this);
  painter.drawImage (QPoint (0, 0), augmentedImage);
}


void BSDetectionWidget::closeAccuAnalyzer ()
{
  if (accuview != NULL)
  {
    accuview->close ();
    delete accuview;
    accuview = NULL;
  }
}


void BSDetectionWidget::closePixelAnalyzer ()
{
  if (strucview != NULL)
  {
    strucview->close ();
    delete strucview;
    strucview = NULL;
  }
}


void BSDetectionWidget::closeProfileAnalyzer ()
{
  if (profileview != NULL)
  {
    profileview->close ();
    delete profileview;
    profileview = NULL;
  }
}


void BSDetectionWidget::closeIdetAnalyzer ()
{
  if (idetview != NULL)
  {
    idetview->close ();
    delete idetview;
    idetview = NULL;
  }
}


void BSDetectionWidget::switchAccuAnalyzer ()
{
  if (accuview != NULL)
  {
    accuview->close ();
    delete accuview;
    accuview = NULL;
  }
  else
  {
    accuview = new BSAccumulatorView (&detector);
    accuview->show ();
  }
}


void BSDetectionWidget::switchPixelAnalyzer ()
{
  if (strucview != NULL)
  {
    strucview->close ();
    delete strucview;
    strucview = NULL;
  }
  else
  {
    strucview = new BSStructureView (&loadedImage, &detector);
    strucview->setGradientImage (&gradImage);
    strucview->show ();
  }
}


void BSDetectionWidget::switchProfileAnalyzer ()
{
  if (profileview != NULL)
  {
    profileview->close ();
    delete profileview;
    profileview = NULL;
  }
  else
  {
    profileview = new BSProfileView ();
    profileview->setImage (&loadedImage, gMap);
    if (! p1.equals (p2)) profileview->buildScans (p1, p2);
    profileview->show ();
  }
}


void BSDetectionWidget::switchIdetAnalyzer ()
{
  if (idetview != NULL)
  {
    idetview->close ();
    delete idetview;
    idetview = NULL;
  }
  else
  {
    idetview = new BSIdetView (&detector);
    idetview->setImage (&loadedImage, gMap);
    idetview->update ();
    idetview->show ();
  }
}


void BSDetectionWidget::mousePressEvent (QMouseEvent *event)
{
  oldp1.set (p1);
  oldp2.set (p2);
  oldudef = udef;
  p1 = Pt2i (event->pos().x (), height - 1 - event->pos().y());
  if (p1.manhattan (p2) < 10) p1.set (oldp1);
  else if (p1.manhattan (oldp1) < 10) p1.set (p2);
  udef = true;
}


void BSDetectionWidget::mouseReleaseEvent (QMouseEvent *event)
{
  p2 = Pt2i (event->pos().x (), height - 1 - event->pos().y());
  if (p1.equals (p2))
  {
    p1.set (oldp1);
    p2.set (oldp2);
    udef = oldudef;
  }
  else
  {
    alternate = 0;
    cerr << "p1 defined: " << p1.x () << " " << p1.y () << endl;
    cerr << "p2 defined: " << p2.x () << " " << p2.y () << endl;
    detector.setMaxTrials (0);
    extract ();
    nbdettrials = detector.countOfTrials ();
    nbmaxdettrials = 0;
    if (detector.isMultiSelection ())
      cout << detector.getBlurredSegments().size ()
           << " blurred segments detected on "
           << nbdettrials << " essais " << endl;
  }
}


void BSDetectionWidget::mouseMoveEvent (QMouseEvent *event)
{
  p2 = Pt2i (event->pos().x (), height - 1 - event->pos().y ());
  if (verbose) cerr << "(" << p1.x () << ", " << p1.y () << ") ("
                    << p2.x () << ", " << p2.y () << ")" << endl;
  if (p1.manhattan (p2) > 5
      && (width > p2.x() && height > p2.y()
          && p2.x() > 0 && p2.y() > 0))
  {
    nodrag = false;
    extract ();
    nodrag = true;
    detector.setMaxTrials (0);
  }
}


void BSDetectionWidget::keyPressEvent (QKeyEvent *event)
{
  if (isActiveWindow ()) switch (event->key ())
  {
    case Qt::Key_A :
      // Registers the last extracted blurred segment
      saveExtractedSegment ();
      break;

    case Qt::Key_B :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Toggles background image
        toggleBackground ();
        if (p1.equals (p2)) displayBackground ();
        else displayDetectionResult ();
      }
      else
      {
        // Tunes the gradient resolution for gradient local max filtering
        detector.incGradientResolution (
          (event->modifiers () & Qt::ShiftModifier ? -1 : 1));
        cout << "Gradient resolution = "
             << detector.getGradientResolution () << endl;
        extract ();
      }
      break;

    case Qt::Key_C :
      // Clears the registered blurred segments
      clearSavedSegments ();
      break;

    case Qt::Key_D :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches density test at initial step
        detector.switchDensityTest ();
        extract ();
        cout << "Density test : "
             << (detector.isDensityTestOn () ? "on" : "off") << endl;
      }
      break;

    case Qt::Key_E :
      // Handles directed edge or stroke detection
      if (event->modifiers () & Qt::ControlModifier)
        detector.switchEdgeDirectionConstraint ();
      else detector.invertEdgeDirection ();
      switch (detector.edgeDirectionConstraint ())
      {
        case 0 :
          cout << "Stroke detection" << endl;
          break;
        case 1 :
          cout << "Edge detection" << endl;
          break;
        case -1 :
          cout << "Opposite edge detection" << endl;
          break;
      }
      extract ();
      break;

    case Qt::Key_F :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches initial detection filtering
        detector.switchFiltering (BSDetector::STEP_INITIAL);
        cout << "Pre-filtering "
             << (detector.isFiltering (BSDetector::STEP_INITIAL) ? "on" : "off")
             << endl;
        extract ();
      }
      break;

    case Qt::Key_G :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches length test at final step
        detector.switchFinalLengthTest ();
        extract ();
        cout << "Final length test : "
             << (detector.isFinalLengthTestOn () ? "on" : "off") << endl;
      }
      else
      {
        // Tunes the gradient threshold for maximal value detection
        detector.incGradientThreshold (
          (event->modifiers () & Qt::ShiftModifier ? -1 : 1));
        cout << "Gradient threshold = "
             << detector.getGradientThreshold () << endl;
        extract ();
      }
      break;

    case Qt::Key_H :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches final detection filtering
        detector.switchFiltering (BSDetector::STEP_FINAL);
        cout << "Final filtering "
             << (detector.isFiltering (BSDetector::STEP_FINAL) ? "on" : "off")
             << endl;
        extract ();
      }
      break;

    case Qt::Key_J :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the proximity constraint for fast tracks
        detector.switchFastTrackProximityConstraint ();
        cout << "Proximity constraint on fast tracks "
             << (detector.fastTrackProximityConstraintOn () ? "on" : "off")
             << endl;
        extract ();
      }
      else if (detector.fastTrackProximityConstraintOn ())
      {
        // Tunes the proximity threshold for fast tracks
        detector.incFastTrackProximityThreshold (
                    (event->modifiers () & Qt::ShiftModifier) == 0);
        cout << "Proximity threshold for fast tracks = "
             << detector.getFastTrackProximityThreshold () << endl;
        extract ();
      }
      break;

    case Qt::Key_K :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the final step connectivity constraint
        detector.switchConnectivityConstraint ();
        cout << "Connectivity constraint "
             << (detector.isConnectivityConstraintOn () ? "on" : "off")
             << endl;
        extract ();
      }
      else
      {
        // Tunes the minimal size of connected components
        detector.incConnectedComponentMinSize (
                    (event->modifiers () & Qt::ShiftModifier) == 0);
        cout << "Connected components min size = "
             << detector.getConnectedComponentMinSize () << endl;
        extract ();
      }
      break;

    case Qt::Key_L :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches density test at final step
        detector.switchFinalDensityTest ();
        extract ();
        cout << "Final density test : "
             << (detector.isFinalDensityTestOn () ? "on" : "off") << endl;
      }
      else
      {
        // Tunes the output blurred segment minimal size
        detector.setBSminSize (detector.getBSminSize () +
          (event->modifiers () & Qt::ShiftModifier ? -1 : 1));
        cout << "Output BS min size = " << detector.getBSminSize () << endl;
        extract ();
      }
      break;

    case Qt::Key_M :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the multi-detection
        detector.switchMultiSelection ();
        cout << "Multi-selection "
             << (detector.isMultiSelection () ? "on" : "off") << endl;
      }
      else
      {
        // Runs an automatic detection
        udef = false;
        detector.setMaxTrials (-1);
        extract ();
        nbdettrials = detector.countOfTrials ();
        nbmaxdettrials = 0;
//        cout << detector.getBlurredSegments().size ()
//             << " blurred segments detected on "
//             << nbdettrials << " essais " << endl;
      }
      break;

    case Qt::Key_N :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the initial detection extension limitation
        detector.switchInitialBounding ();
        extract ();
        cout << "Initial step max extension = "
             << detector.initialDetectionMaxExtent () << endl;
      }
      else
      {
        // Highlights the next segment in a multi-detection
        if (! detector.getBlurredSegments().empty ())
        {
          nbmaxdettrials += (event->modifiers () & Qt::ShiftModifier ? -1 : 1);
          if (nbmaxdettrials < 0) nbmaxdettrials = nbdettrials;
          else if (nbmaxdettrials > nbdettrials) nbmaxdettrials = 0;
          detector.setMaxTrials (nbmaxdettrials);
          cout << "Selection du segment " << nbmaxdettrials << endl;
          extract ();
        }
      }
      break;

    case Qt::Key_O :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the scan directionality
        detector.switchOrthoScans ();
        cout << (detector.orthoScansOn () ?
                 "Orthographic scans" : "Directional scans") << endl;
        extract ();
      }
      else
      {
        // Outputs the detected segment
        writeDetectionResult ();
        cout << "Detection result output" << endl;
      }
      break;

    case Qt::Key_P :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the preliminary detection
        detector.switchPreliminary ();
        cout << "Preliminary detection "
             << (detector.isPreliminary () ? "on" : "off") << endl;
        extract ();
      }
      else
      {
        // Captures main window
        augmentedImage.save ("capture.png");
        cout << "Main window shot in capture.png" << endl;
      }
      break;

    case Qt::Key_Q :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the scan flexibility
        detector.switchDynamicScans ();
        cout << (detector.dynamicScansOn () ?
                 "Dynamic scans" : "Static scans") << endl;
        extract ();
      }
      else
      {
        // Displays registered blurred segments
        displaySavedSegments ();
      }
      break;

    case Qt::Key_R :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Toggles the occupancy mask dilation type
        gMap->toggleMaskDilation ();
        extract ();
        cout << "Occupancy mask dilation size : "
             << gMap->getMaskDilation () << endl;
      }
      else
      {
        // Tunes the automatic detection grid resolution
        detector.setAutoGridResolution (detector.getAutoGridResolution () +
          (event->modifiers () & Qt::ShiftModifier ? -1 : 1));
        cout << "Auto grid resolution = "
             << detector.getAutoGridResolution () << " pixels" << endl;
      }
      break;

    case Qt::Key_S :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the interruption handling
        detector.switchAutoRestart ();
        extract ();
        cout << "Segment continuation after = "
             << detector.getRestartOnLack () << " pixels" << endl;
      }
      else
      {
        // Tunes the pixel lack tolerence value
        detector.setPixelLackTolerence (detector.getPixelLackTolerence () +
          (event->modifiers () & Qt::ShiftModifier ? -1 : 1));
        extract ();
        cout << "Lack tolerence = " << detector.getPixelLackTolerence ()
             << " pixels" << endl;
      }
      break;

    case Qt::Key_T :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the progressive thinning
        detector.toggleThinning ();
        if (detector.isThinningOn () && detector.isThickenningOn ())
          detector.toggleThickenning ();
        extract ();
        cout << "Thinning "
             << (detector.isThinningOn () ? "on" : "off") << endl;
      }
      break;

    case Qt::Key_U :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the display of the detected blurred segment bounds
        bsBoundsVisible = ! bsBoundsVisible;
        cout << "Segments bounds "
             << (bsBoundsVisible ? "visible" : "hidden") << endl;
        extract ();
      }
      else
      {
        // Replays last extraction
        cerr << "p1 update: " << p1.x () << " " << p1.y () << endl;
        cerr << "p2 update: " << p2.x () << " " << p2.y () << endl;
        extract ();
      }
      break;

    case Qt::Key_V :
      if (event->modifiers () & Qt::ControlModifier)
        // Switches the detection result edition
        switchVerbose ();
      break;

    case Qt::Key_W :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the scan centering on the detected segment
        detector.switchScanRecentering ();
        cout << "Fine tracking centered on " << (detector.isScanRecentering () ?
                "detected segment" : "initial scan") << endl;
        extract ();
      }
      else
      {
        // Tunes the assigned max width margin for fine tracks
        detector.setFastTracksMaxMargin (detector.fastTracksMaxMargin () +
          (event->modifiers () & Qt::ShiftModifier ? -1 : 1));
        extract ();
        cout << "Fast tracks max width margin = "
             << detector.fastTracksMaxMargin () << endl;
      }
      break;

    case Qt::Key_X :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the setting of the assigned width on the detected segment
        detector.switchScanFitting ();
        cout << "Fine tracking fitted to " << (detector.isScanFitting () ?
                "detected segment width" : "assigned width") << endl;
        extract ();
      }
      else
      {
        // Tunes the assigned max width for fast tracks
        detector.setFineTracksMaxWidth (detector.fineTracksMaxWidth () +
          (event->modifiers () & Qt::ShiftModifier ? -1 : 1));
        extract ();
        cout << "Fine tracks max width = "
             << detector.fineTracksMaxWidth () << endl;
      }
      break;

    case Qt::Key_Y :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the display of the detected blurred segment pixels
        bsPointsVisible = ! bsPointsVisible;
        cout << "Segments points "
             << (bsPointsVisible ? "visible" : "hidden") << endl;
        displayDetectionResult ();
      }
      else
      {
        // Tunes the background image black level
        incBlackLevel ((event->modifiers () & Qt::ShiftModifier) ? -1 : 1);
        displayDetectionResult ();
        cout << "Background black level = " << getBlackLevel () << endl;
      }
      break;

    case Qt::Key_Z :
      if (event->modifiers () & Qt::ControlModifier)
      {
        // Switches the thickenning control
        detector.toggleThickenning ();
        if (detector.isThickenningOn () && detector.isThinningOn ())
          detector.toggleThinning ();
        extract ();
        cout << "Thickenning "
             << (detector.isThickenningOn () ? "on" : "off") << endl;
      }
      else
      {
        // Tunes the thickenning limit
        detector.incThickenningLimit (
          (event->modifiers () & Qt::ShiftModifier) ? -1 : 1);
        extract ();
        cout << "Thickenning limit = " << detector.getThickenningLimit ()
             << " pixels" << endl;
      }
      break;

    case Qt::Key_1 :
      switchPixelAnalyzer ();
      break;

    case Qt::Key_2 :
      switchAccuAnalyzer ();
      break;

    case Qt::Key_3 :
      switchProfileAnalyzer ();
      break;

    case Qt::Key_4 :
      switchIdetAnalyzer ();
      break;

    case Qt::Key_5 :
      // Switches the crosswise segment detection
      detector.switchTrackCrosswise ();
      extract ();
      cout << "Crosswise segment detection "
           << (detector.trackCrosswiseOn () ? "on" : "off") << endl;
      break;

    case Qt::Key_6 :
      detector.switchDetector ();
      cout << (detector.oldDetectorOn () ?
               "Old detector set" : "Present detector set") << endl;
      extract ();
      break;

    case Qt::Key_7 :
      storeUserInput ();
      break;

    case Qt::Key_8 :
      alternateTest ();
      break;

    case Qt::Key_9 :
      performanceTest ();
      break;

    case Qt::Key_0 :
      localTest ();
      break;
  }
  else if (strucview != NULL && strucview->isActiveWindow ())
  {
    if (strucview->processKeyEvent (event)) extract ();
  }
  else if (accuview != NULL && accuview->isActiveWindow ())
  {
    if (accuview->processKeyEvent (event)) extract ();
  }
  else if (profileview != NULL && profileview->isActiveWindow ())
  {
    if (profileview->processKeyEvent (event)) extract ();
  }
  else if (idetview != NULL && idetview->isActiveWindow ())
  {
    if (idetview->processKeyEvent (event)) extract ();
  }
}


void BSDetectionWidget::drawPoints (QPainter &painter,
                                    vector<Pt2i> pts, QColor color)
{
  vector<Pt2i>::iterator iter = pts.begin ();
  while (iter != pts.end ())
  {
    Pt2i p = *iter++;
    painter.setPen (QPen (color, DEFAULT_PEN_WIDTH, Qt::SolidLine,
                          Qt::RoundCap, Qt::RoundJoin));
    if (p.x() < width && p.y() < height && p.x() >= 0 && p.y() >= 0)
      painter.drawPoint (QPoint (p.x(), height - 1 - p.y()));  // dec 1
  }
}


void BSDetectionWidget::drawPixels (QPainter &painter, vector<Pt2i> pix)
{
  vector<Pt2i>::iterator iter = pix.begin ();
  while (iter != pix.end ())
  {
    Pt2i p = *iter++;
    painter.setPen (QPen (QBrush (loadedImage.pixel (p.x (),
                                  loadedImage.height () - 1 - p.y ())),
                          DEFAULT_PEN_WIDTH, Qt::SolidLine,
                          Qt::RoundCap, Qt::RoundJoin));
    if (p.x() < width && p.y() < height && p.x() >= 0 && p.y() >= 0)
      painter.drawPoint (QPoint (p.x(), height - 1 - p.y()));  // dec 1
  }
}


void BSDetectionWidget::drawLine (QPainter &painter,
                                  const Pt2i from, const Pt2i to, QColor color)
{
  int n;
  Pt2i *pts = from.drawing (to, &n);
  painter.setPen (QPen (color, DEFAULT_PEN_WIDTH, Qt::SolidLine,
                        Qt::RoundCap, Qt::RoundJoin));
  for (int i = 0; i < n; i++)
    painter.drawPoint (QPoint (pts[i].x (),
                               height - 1 - pts[i].y ()));  // dec 1
  delete [] pts;
}


void BSDetectionWidget::drawSelection (QPainter &painter,
                                       const Pt2i from, const Pt2i to)
{
  drawLine (painter, from, to, selectionColor);
}


void BSDetectionWidget::drawBlurredSegment (QPainter &painter,
                                            BlurredSegment *bs, bool high)
{
  if (bs != NULL)
  {
    if (bsBoundsVisible)
    {
      vector<Pt2i> bnd;
      DigitalStraightSegment *dss = bs->getSegment ();
      if (dss != NULL)
      {
        dss->getBounds (bnd, 0, 0, width, height);
        drawPoints (painter, bnd, high ? boundHighColor : boundColor);
      }
    }
    if (bsPointsVisible)
      drawPoints (painter, bs->getAllPoints (), high ? bsHighColor : bsColor);
  }
}


void BSDetectionWidget::incBlackLevel (int val)
{
  blevel += val * 5;
  if (blevel < 0) blevel = 0;
  if (blevel > 200) blevel = 200;
}


void BSDetectionWidget::lighten (QImage &im)
{
  if (blevel != 0 && background != BACK_BLACK && background != BACK_WHITE)
  {
    for (int i = 0; i < im.height (); i++)
      for(int j = 0; j < im.width (); j++)
      {
        int col = blevel + (QColor (im.pixel(j,i)).value ()
                            * (255 - blevel)) / 255;
        im.setPixel (j, i, col + col * 256 + col * 256 * 256);
      }
  }
}


void BSDetectionWidget::displayBackground ()
{
  if (background == BACK_BLACK) augmentedImage.fill (qRgb (0, 0, 0));
  else if (background == BACK_WHITE) augmentedImage.fill (qRgb (255, 255, 255));
  else if (background == BACK_IMAGE) augmentedImage = loadedImage;
  else augmentedImage = gradImage;
  QPainter painter (&augmentedImage);
  update (QRect (QPoint (0, 0), QPoint (width, height)));
}


void BSDetectionWidget::writeDetectionResult ()
{
  BlurredSegment *bs = detector.getBlurredSegment ();
  if (bs != NULL)
  {
    ofstream outf ("seg.txt", ios::out);
    outf << "(" << bs->antipodalEdgeStart().x ()
         << ", " << bs->antipodalEdgeStart().y ()
         << ") (" << bs->antipodalEdgeEnd().x ()
         << ", " << bs->antipodalEdgeEnd().y ()
         << ") (" << bs->antipodalVertex().x ()
         << ", " << bs->antipodalVertex().y ()
         << ") (" << bs->getLastLeft().x ()
         << ", " << bs->getLastLeft().y ()
         << ") (" << bs->getLastRight().x ()
         << ", " << bs->getLastRight().y ()
         << ")" << endl;
    outf.close ();
  }
}


void BSDetectionWidget::displayDetectionResult ()
{
  if (background == BACK_BLACK) augmentedImage.fill (qRgb (0, 0, 0));
  else if (background == BACK_WHITE) augmentedImage.fill (qRgb (255, 255, 255));
  else if (background == BACK_IMAGE) augmentedImage = loadedImage;
  else augmentedImage = gradImage;
  lighten (augmentedImage);
  QPainter painter (&augmentedImage);
  vector<BlurredSegment *> bss = detector.getBlurredSegments ();
  if (! bss.empty ())
  {
//    cout << bss.size () << " blurred segments detected" << endl;
//    double bsw = 0.;
//    int bsc = 0;
    vector<BlurredSegment *>::const_iterator it = bss.begin ();
    while (it != bss.end ())
    {
//      if ((*it) != NULL)
//      {
//        DigitalStraightSegment *dss = (*it)->getSegment ();
//        bsc++;
//        if (dss == NULL) cout << "DSS null" << endl;
//        else bsw += dss->width () / (double) dss->period ();
//      }
      drawBlurredSegment (painter, *it, nbmaxdettrials == 0 ||
                          (*it == bss.back () && detector.isLastTrialOk ()));
      it++;
    }
//    cout << bsc << " effective blurred segments" << endl;
//    if (bsc != 0) cout << "Mean width is " << bsw / bsc << endl;
  }
  else
    drawBlurredSegment (painter, detector.getBlurredSegment ());
  if (udef) drawSelection (painter, p1, p2);
  update (QRect (QPoint (0, 0), QPoint (width, height)));

  // Update auxiliary view if not dragging
  if (nodrag)
  {
    if (idetview != NULL) idetview->update ();
    if (profileview != NULL)
    {
      profileview->buildScans (p1, p2);
      profileview->scene()->update ();
    }
    if (accuview != NULL) accuview->scene()->update ();
    if (strucview != NULL)
    {
      strucview->scene()->update ();
      strucview->repaint ();
    }
    if (verbose) writeDetectionStatus ();
  }
}


void BSDetectionWidget::displaySavedSegments ()
{
  if (background == BACK_BLACK) augmentedImage.fill (qRgb (0, 0, 0));
  else if (background == BACK_WHITE) augmentedImage.fill (qRgb (255, 255, 255));
  else if (background == BACK_IMAGE) augmentedImage = loadedImage;
  else augmentedImage = gradImage;
  lighten (augmentedImage);
  QPainter painter (&augmentedImage);
  if (! extractedSegments.empty ())
  {
    vector<ExtractedSegment>::iterator it = extractedSegments.begin ();
    while (it != extractedSegments.end ())
    {
      drawBlurredSegment (painter, it->bs);
      drawSelection (painter, it->p1, it->p2);
      it ++;
    }
  }
  update (QRect (QPoint (0, 0), QPoint (width, height)));
}


void BSDetectionWidget::saveExtractedSegment ()
{
  if (detector.isMultiSelection ())
  {
    vector<BlurredSegment *> bss = detector.getBlurredSegments ();
    vector<BlurredSegment *>::const_iterator it = bss.begin ();
    while (it != bss.end ())
    {
      ExtractedSegment es;
      es.bs = *it++;
      es.p1 = p1;
      es.p2 = p2;
      extractedSegments.push_back (es);
    }
    detector.preserveFormerBlurredSegments ();
  }
  else
  {
    BlurredSegment *bs = detector.getBlurredSegment ();
    if (bs != NULL)
    {
      ExtractedSegment es;
      es.bs = bs;
      es.p1 = p1;
      es.p2 = p2;
      extractedSegments.push_back (es);
      detector.preserveFormerBlurredSegment ();
    }
  }
}


void BSDetectionWidget::clearSavedSegments ()
{
  vector<ExtractedSegment>::iterator it = extractedSegments.begin ();
  while (it != extractedSegments.end ())
    delete ((it++)->bs->getSegment ());
  extractedSegments.clear ();
}


void BSDetectionWidget::writeDetectionStatus ()
{
  int res = detector.result ();
  if (res == BSDetector::RESULT_UNDETERMINED)
    cout << "Extraction : undetermined." << endl;
  else if (res == BSDetector::RESULT_OK)
    cout << "Extraction : OK." << endl;
  else if (res == BSDetector::RESULT_PRELIM_NO_DETECTION)
    cout << "Extraction : no preliminary detection (bs0 == NULL)." << endl;
  else if (res == BSDetector::RESULT_PRELIM_TOO_FEW)
    cout << "Extraction : two few points at preliminary detection." << endl;
  else if (res == BSDetector::RESULT_INITIAL_NO_DETECTION)
    cout << "Extraction : no initial detection (bsini == NULL)." << endl;
  else if (res == BSDetector::RESULT_INITIAL_TOO_FEW)
    cout << "Extraction : two few points at initial detection." << endl;
  else if (res == BSDetector::RESULT_INITIAL_TOO_SPARSE)
    cout << "Extraction : unsuccessful density test at initial detection."
         << endl;
  else if (res == BSDetector::RESULT_INITIAL_TOO_MANY_OUTLIERS)
    cout << "Extraction : unsuccessful filter test at initial detection."
         << endl;
  else if (res == BSDetector::RESULT_INITIAL_CLOSE_ORIENTATION)
    cout << "Extraction : initial detection of a too closely oriented segment."
         << endl;
  else if (res == BSDetector::RESULT_FINAL_NO_DETECTION)
    cout << "Extraction : no final detection (bsini == NULL)." << endl;
  else if (res == BSDetector::RESULT_FINAL_TOO_FEW)
    cout << "Extraction : two few points at final detection." << endl;
  else if (res == BSDetector::RESULT_FINAL_TOO_SPARSE)
    cout << "Extraction : unsuccessful density test at final detection."
         << endl;
  else if (res == BSDetector::RESULT_FINAL_TOO_MANY_OUTLIERS)
    cout << "Extraction : unsuccessful filter test at final detection."
         << endl;
}


void BSDetectionWidget::extract ()
{
  if (udef)
  {
    if (p1.equals (p2))
    {
      displayBackground ();
      return;
    }
    detector.detectSelection (p1, p2);
  }
  else detector.detectAll ();
  displayDetectionResult ();
}


void BSDetectionWidget::alternateTest ()
{
  if (p1.equals (p2))
  {
    cout << "Stroke undefined" << endl;
    return;
  }

  if (++alternate == 3) alternate = 0;
  if (alternate == 0)
  {
  }
  else if (alternate == 1)
  {
  }
  else if (alternate == 2)
  {
  }
} 



void BSDetectionWidget::performanceTest ()
{
  if (p1.equals (p2))
  {
    // No stroke -> automatic
    cout << "Automatic extraction test" << endl;
    clock_t start = clock ();
    for (int i = 0; i < 100; i++) detector.detectAll ();
    double diff = (clock () - start) / (double) CLOCKS_PER_SEC;
    cout << "Test run : " << diff << endl;
    extract ();
  }
  else
  {
    udef = true;
    cout << "Run test" << endl;
    clock_t start = clock ();
    for (int i = 0; i < 1000; i++) detector.detect (p1, p2);
    double diff = (clock () - start) / (double) CLOCKS_PER_SEC;
    cout << "Test run : " << diff << endl;
    extract ();
  }
}


void BSDetectionWidget::storeUserInput ()
{
  if (udef && ! p1.equals (p2))
  {
    ofstream output ("test.txt", ios::out);
    output << p1.x () << " " << p1.y () << endl;
    output << p2.x () << " " << p2.y () << endl;
    cout << "New test.txt created" << endl;
  }
  else cout << "Unable to create test.txt : no user input" << endl;
}


void BSDetectionWidget::localTest ()
{
  int val[4], i = 0;
  ifstream input ("test.txt", ios::in);
  bool reading = true;
  if (input)
  {
    while (reading)
    {
      input >> val[i];
      if (input.eof ()) reading = false;
      else if (++i == 4) reading = false;
    }
    if (i == 4)
    {
      udef = true;
      p1 = Pt2i (val[0], val[1]);
      p2 = Pt2i (val[2], val[3]);
      cout << "Run test on (" << val[0] << ", " << val[1]
           << ") (" << val[2] << ", " << val[3] << ")" << endl;
    }
    else cout << "Test file damaged" << endl;
  }
  else cout << "Run autotest" << endl;

  extract ();
  cout << "Test run" << endl;
}