// (C) Copyright Jack Culpepper 1997
// This code may not be distributed without permission.
package mandel;

import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.*;
import gestalt.Connection;
import gestalt.Client;
import gestalt.TaskID;
import gestalt.msg.ServerUpdate;
import mandel.MandelTask;
import mandel.MandelFrame;

public class MandelClient extends Client {
  private static final int x_tasks = 8;
  private static final int y_tasks = 8;
  private static final Point res = new Point( 60, 60 );
  private static final Boundary area = new Boundary( -1.5, 1.5, 1.5, -1.5 );
  protected Date start;
  protected Date finish;
  protected byte[][] data;

  public MandelClient( String hostname ) {
    super( hostname, "MandelClient" );
  }

  public void begin( Connection c ) {
    Vector tasks = new Vector();
    start = new Date();

    report( "Entering MandelClient begin method." );

    // create an array to save all the data that comes in
    data = new byte[ x_tasks * res.x ][ y_tasks * res.y ];

    // create a frame to draw in
    MandelFrame f = new MandelFrame( "Mandel", data );

    // generate the MandelTasks, and put them in the outgoing queue
    report( "Generating " + ( x_tasks * y_tasks ) + " tasks." );

    double area_width = area.right - area.left;
    double area_height = area.top - area.bottom;
    double area_width_step = area_width / x_tasks;
    double area_height_step = area_height / y_tasks;

    report( "area_width=" + area_width );
    report( "area_height=" + area_height );
    report( "area_width_step=" + area_width_step );
    report( "area_heigh_step=" + area_height_step );

    for ( int i = 0 ; i < x_tasks ; i++ ) {
      for ( int j = 0 ; j < y_tasks ; j++ ) {
        double task_left = area.left + ( i * area_width_step );
        double task_right = task_left + area_width_step;
        double task_bottom = area.bottom + ( j * area_height_step );
        double task_top = task_bottom + area_height_step;
        Boundary task_area = new Boundary( task_left, task_top,
          task_right, task_bottom );
        Point loc = new Point( i * res.x, j * res.y );
        MandelTask t = new MandelTask( c, res, task_area, loc );
        tasks.addElement( t.id );
        c.send( t );
      }
    }

    // loop, pulling objects off the incoming queue of the connection until
    // we have gotten the results of all the tasks we submitted
    while ( ! tasks.isEmpty() ) {
      if ( ! c.isAlive() ) {
        report( "My connection died!" );
        break;
      }
      while ( c.avail() ) {
        Object o = c.recv();
        if ( o instanceof MandelTask ) {
          MandelTask t = ( MandelTask )o;
          boolean found = false;
          for ( int i = 0 ; i < tasks.size() ; i++ ) {
            TaskID test = ( TaskID )tasks.elementAt( i );
            if ( test.equals( t.id ) ) {
              found = true;
              tasks.removeElementAt( i );
              report( "Result: " + t );
              input( t );
              f.input( t );
              break;
            }
          }
        }
        else if ( o instanceof ServerUpdate ) {
          ServerUpdate su = ( ServerUpdate )o;
          report( "Server update: tasks_pending=" + su.tasks_pending +
            ", num_slaves=" + su.num_slaves );
        }
        else report( "Found unknown object: " + o );
      }

      // sleep for a while so that this is not a tight loop
      try {
        sleep( 500 );
      }
      catch (InterruptedException e) {
        report( "What!!?!?  My sleep was interrupted: " + e );
      }
    }
    if ( tasks.isEmpty() ) {
      finish = new Date();
      report( "All results have been received." );
      long time = finish.getTime() - start.getTime();
      report( "Time: " + time + " milliseconds" );
    }
  }

  public void input( MandelTask t ) {
    // copy the data into the client array
    for ( int i = 0 ; i < t.res.x ; i++ ) {
      for ( int j = 0 ; j < t.res.y ; j++ ) {
        if ( data[ i + t.loc.x ][ j + t.loc.y ] != 0 ) {
          report( "Overwriting client data at location: (" +
            ( i + t.loc.x ) + ", " + ( j + t.loc.y ) + ")" );
        }
        data[ i + t.loc.x ][ j + t.loc.y ] = t.data[ i ][ j ];
      }
    }
  }

  public static void main( String[] args ) {
    MandelClient c = new MandelClient( args[0] );
  }
}

