// Copyright (c) 2003  Utrecht University (The Netherlands),
// ETH Zurich (Switzerland), Freie Universitaet Berlin (Germany),
// INRIA Sophia-Antipolis (France), Martin-Luther-University Halle-Wittenberg
// (Germany), Max-Planck-Institute Saarbruecken (Germany), RISC Linz (Austria),
// and Tel-Aviv University (Israel).  All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; version 2.1 of the License.
// See the file LICENSE.LGPL distributed with CGAL.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL: svn+ssh://scm.gforge.inria.fr/svn/cgal/branches/CGAL-3.5-branch/Robustness/demo/Robustness/robustness.cpp $
// $Id: robustness.cpp 45454 2008-09-09 21:42:42Z lrineau $
//
//
// Author(s)     : Stefan Schirra, Radu Ursu

#include <CGAL/basic.h>

#include "cgal_types.h"
#include <CGAL/IO/Qt_widget.h>
#include <CGAL/IO/Qt_widget_standard_toolbar.h>
#include <CGAL/IO/Qt_help_window.h>
#include <CGAL/IO/Qt_widget_layer.h>
#include <CGAL/IO/pixmaps/demoicon.xpm>

#include <qplatinumstyle.h>
#include <qapplication.h>
#include <qmainwindow.h>
#include <qstatusbar.h>
#include <qfiledialog.h>
#include <qmessagebox.h>
#include <qpopupmenu.h>
#include <qmenubar.h>
#include <qtoolbutton.h>
#include <qtoolbar.h>
#include <qfiledialog.h>
#include <qtimer.h>
#include <qprogressdialog.h>


#include <fstream>
#include <stack>
#include <set>
#include <string>

const QString my_title_string("Robustness Demo with"
			      " CGAL Qt_widget");

//global flags and variables
int                           current_state;
std::vector<double_Segment>   double_segments, double_segments2;
std::vector<real_Segment>     real_segments;

std::vector<CartesianFloat::Segment_2>    float_segments, float_segments2;
std::vector<HomogeneousFloat::Segment_2>  hfloat_segments, hfloat_segments2;
std::vector<HomogeneousDouble::Segment_2> hdouble_segments, hdouble_segments2;

std::vector<double_Point >    double_intersection_points;
std::vector<real_Point >      real_intersection_points;
std::vector<double_Point >    double_convex_hull;
std::vector<real_Point >      real_convex_hull;

class show_intersection_points : public CGAL::Qt_widget_layer{
public:
  void draw(){
    widget->lock();
    *widget << CGAL::LineWidth(1) << CGAL::GREEN;
    std::vector<double_Segment>::iterator dit =
      double_segments.begin();
    while(dit!=double_segments.end()){
      *widget << (*dit);
      dit++;
    }
    *widget << CGAL::LineWidth(1) << CGAL::BLUE;
    std::vector<double_Segment>::iterator dit2 =
      double_segments2.begin();
    while(dit2!=double_segments2.end()){
      *widget << (*dit2);
      dit2++;
    }

    widget->unlock();
  }
};

class show_hull_of_intersection_points : public CGAL::Qt_widget_layer{
public:
  void draw(){
    widget->lock();
    *widget << CGAL::LineWidth(1) << CGAL::GREEN;
    std::vector<double_Segment>::iterator dit =
      double_segments.begin();
    while(dit!=double_segments.end()){
      *widget << (*dit);
      dit++;
    }

    std::list<double_Segment>	Sl;
    if( double_convex_hull.size() > 1 ) {
      double_Point pakt,prev,pstart;

      std::vector<double_Point>::iterator it;
      it=double_convex_hull.begin();
      prev= *it; pstart=prev;
      it++;

      for(; it != double_convex_hull.end(); ++it) {
      	pakt = *it;
        Sl.push_back(double_Segment(prev,pakt));
        prev=pakt;
      }
      Sl.push_back(double_Segment(pakt,pstart));

      *widget << CGAL::BLUE;
      std::list<double_Segment>::iterator its = Sl.begin();
      while(its!=Sl.end())
      {
        *widget << (*its++);
      }
    }

    std::vector<double_Point>::iterator dip =
      double_convex_hull.begin();
    *widget << CGAL::PointStyle(CGAL::CROSS) << CGAL::LineWidth(2);
    *widget << CGAL::WHITE << CGAL::PointSize(8);
    while(dip!=double_convex_hull.end()){
      *widget << (*dip);
      dip++;
    }

    if ( real_convex_hull.size() != double_convex_hull.size() )
    {
      *widget <<CGAL::PointSize(11) << CGAL::PointStyle(CGAL::CIRCLE)
	      << CGAL::RED;
      std::vector<double_Point >::iterator  dble_it;
      std::vector<real_Point >::iterator    real_it;
      real_it = real_convex_hull.begin();
      for ( dble_it  = double_convex_hull.begin();
          dble_it != double_convex_hull.end();
          ++dble_it )
      {
        if (   (real_it == real_convex_hull.end())
            || (( CGAL::squared_distance(
                      *dble_it,
                      double_Point( CGAL::to_double(real_it->x()),
                                    CGAL::to_double(real_it->y()) ))
               ) > 0.125 )
           )
        {
          *widget << *dble_it;
        } else {
          if ( real_it != real_convex_hull.end() ){
	    ++real_it;
	  }
        }
      }
    }
    widget->unlock();
  }
};


class MyWindow : public QMainWindow
{
  Q_OBJECT
public:
  MyWindow(int w, int h){
    widget = new CGAL::Qt_widget(this);
    setCentralWidget(widget);

    //create a timer for checking if somthing changed
    QTimer *timer = new QTimer( this );
    connect( timer, SIGNAL(timeout()),
           this, SLOT(timer_done()) );
    timer->start( 200, FALSE );

    // file menu
    QPopupMenu * file = new QPopupMenu( this );
    menuBar()->insertItem( "&File", file );
    file->insertItem("&New", this, SLOT(new_instance()), CTRL+Key_N);
    file->insertItem("New &Window", this, SLOT(new_window()), CTRL+Key_W);
    file->insertSeparator();
    file->insertItem("Print", widget, SLOT(print_to_ps()), CTRL+Key_P);
    file->insertSeparator();
    file->insertItem( "&Close", this, SLOT(close()), CTRL+Key_X );
    file->insertItem( "&Quit", qApp, SLOT( closeAllWindows() ), CTRL+Key_Q );

    // drawing menu
    QPopupMenu * draw = new QPopupMenu( this );
    menuBar()->insertItem( "&Draw", draw );
    draw->insertItem("Convex &Hull of intersection points", this,
		     SLOT(hull_points()), CTRL+Key_H );

    draw->insertItem("&Intersection points on segments", this,
		     SLOT(intersection_points()), CTRL+Key_I );

    // help menu
    QPopupMenu * help = new QPopupMenu( this );
    menuBar()->insertItem( "&Help", help );
    help->insertItem("How To", this, SLOT(howto()), Key_F1);
    help->insertSeparator();
    help->insertItem("&About", this, SLOT(about()), CTRL+Key_A );
    help->insertItem("About &Qt", this, SLOT(aboutQt()) );

    //the standard toolbar
    stoolbar = new CGAL::Qt_widget_standard_toolbar (widget, this, "ST");

    *widget << CGAL::LineWidth(2) << CGAL::BackgroundColor (CGAL::BLACK);

    resize(w,h);
    widget->set_window(-1, 1, -1, 1);
    widget->setMouseTracking(TRUE);

    //application flag stuff
    old_state = 0;

    //layers
    widget->attach(&hull_layer);
    widget->attach(&int_points_layer);

    qte = new QTextBrowser(NULL, "INFO");
    qte->setCaption("Information Window");

  };

private:
  void something_changed(){current_state++;};

public slots:
  void new_instance()
  {
    widget->lock();
    stoolbar->clear_history();
    double_segments.clear();
    real_segments.clear();
    double_convex_hull.clear();
    real_convex_hull.clear();
    double_intersection_points.clear();
    real_intersection_points.clear();
    widget->set_window(-1.1, 1.1, -1.1, 1.1);
    // set the Visible Area to the Interval
    widget->unlock();
    something_changed();
  }

private slots:

  void howto(){
    QString home;
    home = "help/index.html";
    CGAL::Qt_help_window *help = new
      CGAL::Qt_help_window(home, ".", 0, "help viewer");
    help->resize(400, 400);
    help->setCaption("Demo HowTo");
    help->show();
  }

  void about()
  {
    QMessageBox::about( this, my_title_string,
		"This is a demo for Robustness\n"
  		"Copyright CGAL @2003");
  };

  void aboutQt()
  {
    QMessageBox::aboutQt( this, my_title_string );
  }

  void new_window(){
    MyWindow *ed = new MyWindow(500, 500);
    ed->setCaption("Layer");
    ed->stoolbar->clear_history();
    ed->widget->set_window(-1.1, 1.1, -1.1, 1.1);
    ed->show();
    something_changed();
  }

  void timer_done()
  {
    if(old_state!=current_state){
      widget->redraw();
      old_state = current_state;
    }
  }

  void intersection_points(){
    stoolbar->clear_history();
    widget->set_window(-1.1, 1.1, -1.1, 1.1);
    gen_segments();

    //computing using Cartesian<float>
    CartesianFloat::Point_2      p;
    CartesianFloat::Intersect_2  intersection =
      CartesianFloat().intersect_2_object();
    CGAL::Timer watch;
    int is_count = 0;
    int ol_count = 0;
    int bl_count = 0;
    watch.start();
    for( std::vector<CartesianFloat::Segment_2>::const_iterator i
	   = float_segments.begin(); i != float_segments.end(); ++i)
        for( std::vector<CartesianFloat::Segment_2>::const_iterator j
	       = float_segments2.begin(); j != float_segments2.end(); ++j)
        {
            if ( CGAL::assign(p, intersection(*i,*j)) )
            {
                is_count++;
                int ok1 = ( i->has_on(p)) ? 1 : 0;
                int ok2 = ( j->has_on(p)) ? 1 : 0;
                bl_count += ok1*ok2;
                ol_count += ok1+ok2;
            }
        }
    watch.stop();
    QString s("%1"), s1("%1");
    qte->resize(400, 300);
    qte->show();
    qte->setText("INFORMATION Cartesian<float>:");
    s.setNum(is_count);
    qte->append("Intersection points found: " + s);
    s.setNum(bl_count); s1.setNum((double)bl_count/is_count * 100);
    qte->append(s + " of them (" + s1 + "%) lie on both segments.");
    s.setNum(2*is_count); s1.setNum(ol_count);
    qte->append("Out of the " + s + " point-on-segment tests, ");
    s.setNum((double)ol_count/is_count * 50);
    qte->append(s1 + "(" + s + "%) are positive.");
    s.setNum(watch.time());
    qte->append("Computation time = " + s + " sec.");


    //computing using Cartesian<double>
    CartesianDouble::Intersect_2  double_intersection =
      CartesianDouble().intersect_2_object();
    double_Point pd;
    is_count = 0;
    ol_count = 0;
    bl_count = 0;
    watch.reset();
    watch.start();
    for( std::vector<CartesianDouble::Segment_2>::const_iterator i
	   = double_segments.begin(); i != double_segments.end(); ++i)
        for( std::vector<CartesianDouble::Segment_2>::const_iterator j
	       = double_segments2.begin(); j != double_segments2.end(); ++j)
        {
            if ( CGAL::assign(pd, double_intersection(*i,*j)) )
            {
                is_count++;
                int ok1 = ( i->has_on(pd)) ? 1 : 0;
                int ok2 = ( j->has_on(pd)) ? 1 : 0;
                bl_count += ok1*ok2;
                ol_count += ok1+ok2;
            }
        }
    watch.stop();
    qte->append("_____");
    qte->append("INFORMATION Cartesian " + QString::null + "double>:");
    s.setNum(is_count);
    qte->append("Intersection points found: " + s);
    s.setNum(bl_count); s1.setNum((double)bl_count/is_count * 100);
    qte->append(s + " of them (" + s1 + "%) lie on both segments.");
    s.setNum(2*is_count); s1.setNum(ol_count);
    qte->append("Out of the " + s + " point-on-segment tests, ");
    s.setNum((double)ol_count/is_count * 50);
    qte->append(s1 + "(" + s + "%) are positive.");
    s.setNum(watch.time());
    qte->append("Computation time = " + s + " sec.");


    //computing using Homogeneous<float>
    HomogeneousFloat::Intersect_2  hfloat_intersection =
      HomogeneousFloat().intersect_2_object();
    HomogeneousFloat::Point_2 phf;
    is_count = 0;
    ol_count = 0;
    bl_count = 0;
    watch.reset();
    watch.start();
    for( std::vector<HomogeneousFloat::Segment_2>::const_iterator i
	   = hfloat_segments.begin(); i != hfloat_segments.end(); ++i)
        for( std::vector<HomogeneousFloat::Segment_2>::const_iterator j
	       = hfloat_segments2.begin(); j != hfloat_segments2.end(); ++j)
        {
            if ( CGAL::assign(phf, hfloat_intersection(*i,*j)) )
            {
                is_count++;
                int ok1 = ( i->has_on(phf)) ? 1 : 0;
                int ok2 = ( j->has_on(phf)) ? 1 : 0;
                bl_count += ok1*ok2;
                ol_count += ok1+ok2;
            }
	}
    watch.stop();
    qte->append("_____");
    qte->append("INFORMATION Homogeneous float>:");
    s.setNum(is_count);
    qte->append("Intersection points found: " + s);
    s.setNum(bl_count); s1.setNum((double)bl_count/is_count * 100);
    qte->append(s + " of them (" + s1 + "%) lie on both segments.");
    s.setNum(2*is_count); s1.setNum(ol_count);
    qte->append("Out of the " + s + " point-on-segment tests, ");
    s.setNum((double)ol_count/is_count * 50);
    qte->append(s1 + "(" + s + "%) are positive.");
    s.setNum(watch.time());
    qte->append("Computation time = " + s + " sec.");


    hull_layer.deactivate();
    int_points_layer.activate();
    something_changed();
  }

  void hull_points(){
    stoolbar->clear_history();
    widget->set_window(-1.1, 1.1, -1.1, 1.1);
    QString s("%1");
    qte->resize(400, 300);
    qte->show();
    qte->setText("INFORMATION:");
    qte->append("We compute the intersection points of segments using exact"
                " and double arithmetic. Then we compute the convex hull of"
                " those. If there are points that are different in different"
                " arithmetic, we mark them by a red circle.");

    QProgressDialog progress( "Generating segments...",
      "Cancel computing", 60, NULL, "Compute random segments ...", true );
    progress.setCaption("Progress bar");
    progress.resize(200, 50);
    progress.setTotalSteps(60);
    progress.setProgress(10);
    progress.setMinimumDuration(0);
    progress.setLabelText("Compute random segments...");

    gen_segments();
    progress.setLabelText("Compute intersection points...");
    progress.setProgress(20);
    double_convex_hull.clear();
    real_convex_hull.clear();
    double_intersection_points.clear();
    real_intersection_points.clear();
    CGAL::segment_intersection_points_2(
          double_segments.begin(),
          double_segments.end(),
          std::back_inserter( double_intersection_points),
          C_double() );
    progress.setLabelText("Compute intersection points exact...");
    progress.setProgress(30);
    CGAL::segment_intersection_points_2(
          real_segments.begin(),
          real_segments.end(),
          std::back_inserter( real_intersection_points),
          C_real() );
    progress.setLabelText("Compute hull of intersection points...");
    progress.setProgress(40);
    CGAL::convex_hull_points_2(
          double_intersection_points.begin(),
          double_intersection_points.end(),
          std::back_inserter( double_convex_hull));
    progress.setProgress(50);
    progress.setLabelText("Compute hull of intersection points exact...");
    CGAL::convex_hull_points_2(
          real_intersection_points.begin(),
          real_intersection_points.end(),
          std::back_inserter( real_convex_hull));
    progress.setProgress(60);

    hull_layer.activate();
    int_points_layer.deactivate();
    something_changed();
  }

  void gen_segments()
  {
    Source RS(1);
    Segment_iterator g( RS, RS);
    double_segments.clear();
    real_segments.clear();
    double_segments2.clear();
    float_segments.clear();
    float_segments2.clear();
    hfloat_segments.clear();
    hfloat_segments2.clear();
    hdouble_segments.clear();
    hdouble_segments2.clear();

    CGAL::copy_n( g, 100, std::back_inserter( double_segments) );
    CGAL::copy_n( g, 100, std::back_inserter( double_segments2) );

    CGAL::Cartesian_converter<C_double, C_real> converter;
    std::transform( double_segments.begin(),
                    double_segments.end(),
                    std::back_inserter( real_segments),
                    converter );

    CGAL::Cartesian_converter<C_double, CartesianFloat> fconverter;
    std::transform(double_segments.begin(), double_segments.end(),
		    std::back_inserter(float_segments), fconverter);
    std::transform(double_segments2.begin(), double_segments2.end(),
		    std::back_inserter(float_segments2), fconverter);

    CGAL::Cartesian_double_to_Homogeneous< HomogeneousFloat::RT >
                                                        hfconverter;
    std::transform(double_segments.begin(), double_segments.end(),
		   std::back_inserter(hfloat_segments), hfconverter);
    std::transform(double_segments2.begin(), double_segments2.end(),
		   std::back_inserter(hfloat_segments2), hfconverter);

    CGAL::Cartesian_double_to_Homogeneous<HomogeneousDouble::RT>
                                                        hdconverter;
    std::transform(double_segments.begin(), double_segments.end(),
		   std::back_inserter(hdouble_segments), hdconverter);
    std::transform(double_segments2.begin(), double_segments2.end(),
		   std::back_inserter(hdouble_segments2), hdconverter);


  }

private:
  CGAL::Qt_widget           *widget;
  CGAL::Qt_widget_standard_toolbar
                            *stoolbar;
  int                       old_state;
  show_hull_of_intersection_points
                            hull_layer;
  show_intersection_points  int_points_layer;
  QTextBrowser              *qte;
};

#include "robustness.moc"


int
main(int argc, char **argv)
{
  QApplication app( argc, argv );
  MyWindow widget(500,500); // physical window size
  app.setMainWidget(&widget);
  widget.setCaption(my_title_string);
  widget.setMouseTracking(TRUE);
  QPixmap cgal_icon = QPixmap((const char**)demoicon_xpm);
  widget.setIcon(cgal_icon);
  widget.show();
  current_state = -1;
  return app.exec();
}

