/* Percolation animation (version 2.02 - Windows)
 *
 * Copyright (C) 2005 N.R. Moloney 
 *
 * Distributed under the Qt Non-Commercial license version 1.0
 */

#include <qpainter.h>
#include <qpixmap.h>

#include "lcg.h"
#include "percolation.h"

extern long seed;

Percolation::Percolation( QWidget *parent, const char *name ) : 
  QWidget( parent, name )
{
  isolate = FALSE;
  p = 0.5;
  L = 600;

  lattice = new int [max+1];
  cat = new int [max+1];
  lar_clu = new int [max+1];

  occ = initialise_lattice( p, lattice, cat, L );
  largest = largest_cluster( lattice, cat, lar_clu, occ, L );
}

///////////////////////////////////////////////////

void Percolation::slotIsolate( bool toggle )
{
  isolate = toggle;
  repaint();

  return;
} 

///////////////////////////////////////////////////

void Percolation::pChanged( double p_typed )
{
  p = p_typed;
}

///////////////////////////////////////////////////

void Percolation::startNew()
{
  occ = initialise_lattice( p, lattice, cat, L ); 
  largest = largest_cluster( lattice, cat, lar_clu, occ, L );

  repaint();

  return;
}

///////////////////////////////////////////////////

void Percolation::startL600()
{
  L = 600;
  startNew();

  return;
} 

///////////////////////////////////////////////////

void Percolation::startL300()
{
  L = 300;
  startNew();

  return;
} 

///////////////////////////////////////////////////

void Percolation::startL150()
{
  L = 150;
  startNew();

  return;
} 

///////////////////////////////////////////////////

void Percolation::callLargest()
{
  emit largestChanged( largest );

  return;
}

///////////////////////////////////////////////////

void Percolation::callSize()
{
  emit sizeChanged( L );

  return;
}

///////////////////////////////////////////////////

void Percolation::paintEvent( QPaintEvent * )
{
  const int size = 600;
  const int pixel300 = 2;
  const int pixel150 = 4;

  int i;

  QPixmap buffer( size,size );

  buffer.fill( Qt::blue );

  QPainter bufferpainter( &buffer );

  switch( L )
  {
    case 600:

      if( !isolate )
      {
	bufferpainter.setPen( Qt::yellow );
  
	for( i = 0; i < occ; i++ )
	{
	  bufferpainter.drawPoint( ((cat[i+1]-1)%L),((cat[i+1]-1)/L) );
	}
      }

      bufferpainter.setPen( Qt::red );
      
      for( i = 0; i < largest; i++ )
      {
	bufferpainter.drawPoint( ((lar_clu[i+1]-1)%L),((lar_clu[i+1]-1)/L) );
      }

      break;

    case 300:

      bufferpainter.setPen( NoPen );
      
      if( !isolate )
      {
	bufferpainter.setBrush( Qt::yellow );

	for( i = 0; i < occ; i++ )
	{ 
	  bufferpainter.drawRect( pixel300*((cat[i+1]-1)%L),
				  pixel300*((cat[i+1]-1)/L), 
				  pixel300,pixel300 );
	}
      }
	
      bufferpainter.setBrush( Qt::red );
      
      for( i = 0; i < largest; i++ )
      {
	bufferpainter.drawRect( pixel300*((lar_clu[i+1]-1)%L),
				pixel300*((lar_clu[i+1]-1)/L), 
				pixel300,pixel300 );
      }
      
      break;

    case 150:

      bufferpainter.setPen( NoPen );
      
      if( !isolate )
      {
	bufferpainter.setBrush( Qt::yellow );
	
	for( i = 0; i < occ; i++ )
	{ 
	  bufferpainter.drawRect( pixel150*((cat[i+1]-1)%L),
				  pixel150*((cat[i+1]-1)/L), 
				  pixel150,pixel150 );
	}
      }
      
      bufferpainter.setBrush( Qt::red );
      
      for( i = 0; i < largest; i++ )
      {
	bufferpainter.drawRect( pixel150*((lar_clu[i+1]-1)%L),
				pixel150*((lar_clu[i+1]-1)/L), 
				pixel150,pixel150 );
      }
      
      break;
  }

  bufferpainter.end();
  bitBlt( this, 0,0, &buffer );

  return;
}

///////////////////////////////////////////////////

int Percolation::initialise_lattice( double p, int * lattice, 
				     int * cat, int L )
{
  int i, s = 0;
  
  for( i = 1; i <= L*L; i++ )
  {
    if( lcg() < p ) 
    { 
      lattice[i] = 1; 
      cat[++s] = i; 
    } 
    else 
    {
      lattice[i] = 0;
    }                                     
  }

  return s;
}

///////////////////////////////////////////////////

int Percolation::largest_cluster( int * lattice, int * cat, int * lar_clu, 
				  int num_occ, int L )
{
  int k;
  int i, s, s_large, j;
  
  int * cp_cat;
  int * cp_grid;

  cp_cat = new int [max+1];
  cp_grid = new int [max+1];

  s = s_large = 0;
  
  for( i = 1; i <= L*L; i++ ) { cp_grid[i] = lattice[i]; cp_cat[i] = 0; }

  for( k = 1; k <= num_occ; k++ )
  {
    for( i = 1; i <= s; i++ ) { cp_grid[cp_cat[i]] = 2; }
    
    if( s > s_large )
    {
      for( i = 1; i <= s; i++ ) { lar_clu[i] = cp_cat[i]; }
      s_large = s;
    }
    
    s = j = 1;

    cp_cat[j] = cat[k];
    if( cp_grid[cp_cat[j]] == 2 ) { continue; }

    do 
    {
      i = cp_cat[j];
      
      if( i - L > 0 )
      {
	if( cp_grid[i-L] == 1 )
	{ cp_grid[i] = 0; cp_grid[i-L] = 2; cp_cat[++s] = i - L; }
      }

      if( i%L != 0 )  
      {
	if( cp_grid[i+1] == 1 )
	{ cp_grid[i] = 0; cp_grid[i+1] = 2; cp_cat[++s] = i + 1; }
      }
      
      if( i + L <= L*L )
      {
	if( cp_grid[i+L] == 1 )
	{ cp_grid[i] = 0; cp_grid[i+L] = 2; cp_cat[++s] = i + L; }
      }

      if( (i-1)%L != 0 )
      {
        if( cp_grid[i-1] == 1 )
	{ cp_grid[i] = 0; cp_grid[i-1] = 2; cp_cat[++s] = i - 1; }
      }

      j++;
    }
    while( j <= s );
  }

  emit largestChanged( s_large );
  emit sizeChanged( L );

  delete cp_cat;
  delete cp_grid;

  return s_large;
}
