/****************************************************************************
** Author: J.-O. Lachaud, University Savoie Mont Blanc
** (adapted from Qt colliding mices example)
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
****************************************************************************/

#include <cmath>
#include <cassert>
#include <cstdlib>
#include <QGraphicsScene>
#include <QRandomGenerator>
#include <QPainter>
#include <QStyleOption>
#include "objects.hpp"

//static const double Pi = 3.14159265358979323846264338327950288419717;
//static double TwoPi = 2.0 * Pi;

// Global variables for simplicity.
LogicalScene* logical_scene = 0;

double rand01() { return double( rand() ) / double( RAND_MAX ); }

///////////////////////////////////////////////////////////////////////////////
// class Disk
///////////////////////////////////////////////////////////////////////////////

Disk::Disk( qreal r, const MasterShape* master_shape )
  : _r( r ), _master_shape( master_shape ) {}

QPointF
Disk::randomPoint() const
{
  QPointF p;
  do {
    p = QPointF( ( rand01() * 2.0 - 1.0 ),
                 ( rand01() * 2.0 - 1.0 ) );
  } while ( ( p.x() * p.x() + p.y() * p.y() ) > 1.0 );
  return p * _r;
}

bool
Disk::isInside( const QPointF& p ) const
{
  return QPointF::dotProduct( p, p ) <= _r * _r;
}

QRectF
Disk::boundingRect() const
{
  return QRectF( -_r, -_r, 2.0 *_r, 2.0 * _r );
}

void
Disk::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
  painter->setBrush( _master_shape->currentColor() );
  painter->drawEllipse( QPointF( 0.0, 0.0 ), _r, _r );
}


///////////////////////////////////////////////////////////////////////////////
// class MasterShape
///////////////////////////////////////////////////////////////////////////////

MasterShape::MasterShape( QColor cok, QColor cko )
  : _f( 0 ), _state( Ok ), _cok( cok ), _cko( cko )
{
}

void
MasterShape::setGraphicalShape( GraphicalShape* f )
{
  _f = f;
  if ( _f != 0 )  _f->setParentItem( this );
}
  
QColor
MasterShape::currentColor() const
{
  if ( _state == Ok ) return _cok;
  else                return _cko;
}

MasterShape::State
MasterShape::currentState() const
{
  return _state;
}

void
MasterShape::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
{
  // nothing to do, Qt automatically calls paint of every QGraphicsItem
}

void
MasterShape::advance(int step) 
{
  if ( !step ) return;

  // (I) Garde les objets dans la scene.  
  auto p = scenePos(); // pareil que pos si MasterShape est bien à la racine.
  // pos() est dans les coordonnées parent et setPos aussi.
  if ( p.x() < -SZ_BD ) {
    auto point = parentItem() != 0
      ? parentItem()->mapFromScene( QPointF( IMAGE_SIZE + SZ_BD - 1, p.y() ) )
      : QPointF( IMAGE_SIZE + SZ_BD - 1, p.y() );
    setPos(point);
  } else if ( p.x() > IMAGE_SIZE + SZ_BD ) {
    auto point = parentItem() != 0
      ? parentItem()->mapFromScene( QPointF( -SZ_BD + 1, p.y() ) )
      : QPointF( -SZ_BD + 1, p.y() );
    setPos(point);
  }
  if ( p.y() < -SZ_BD ) {
    auto point = parentItem() != 0 ?
      parentItem()->mapFromScene( QPointF( p.x(), IMAGE_SIZE + SZ_BD - 1 ) )
      : QPointF( p.x(), IMAGE_SIZE + SZ_BD - 1 );
    setPos(point);
  } else if ( p.y() > IMAGE_SIZE + SZ_BD ) {
    auto point = parentItem() != 0
      ? parentItem()->mapFromScene( QPointF( p.x(), -SZ_BD + 1 ) )
      : QPointF( p.x(), -SZ_BD + 1 );
    setPos(point);
  }

  // (II) regarde les intersections avec les autres objets.
  if ( logical_scene->intersect( this ) )
    _state = Collision;
  else
    _state = Ok;
}

QPointF
MasterShape::randomPoint() const
{  
  assert( _f != 0 );
  return mapToParent( _f->randomPoint() );
}

bool
MasterShape::isInside( const QPointF& p ) const
{
  assert( _f != 0 );
  return _f->isInside( mapFromParent( p ) );
}

QRectF
MasterShape::boundingRect() const
{
  assert( _f != 0 );
  return mapRectToParent( _f->boundingRect() );
}


///////////////////////////////////////////////////////////////////////////////
// class MasterShape
///////////////////////////////////////////////////////////////////////////////

Asteroid::Asteroid( QColor cok, QColor cko, double speed, double r )
  : MasterShape( cok, cko ), _speed( speed )
{
  // This shape is very simple : just a disk.
  Disk* d = new Disk( r, this );
  // Tells the asteroid that it is composed of just a disk.
  this->setGraphicalShape( d );
}

void
Asteroid::advance(int step)
{
  if (!step) return;
  setPos( mapToParent( _speed, 0.0 ) );
  MasterShape::advance( step );
}



///////////////////////////////////////////////////////////////////////////////
// class LogicalScene
///////////////////////////////////////////////////////////////////////////////

LogicalScene::LogicalScene( int n )
  : nb_tested( n ) {}
    
bool
LogicalScene::intersect( MasterShape* f1, MasterShape* f2 )
{
  for ( int i = 0; i < nb_tested; ++i )
    {
      if ( f2->isInside( f1->randomPoint() )
	   || f1->isInside( f2->randomPoint() ) )
	return true;
    }
  return false;
}

bool
LogicalScene::intersect( MasterShape* f1 )
{
  for ( auto f : formes )
    if ( ( f != f1 ) && intersect( f, f1 ) )
      return true;
  return false;
}

