// MultiDoubleVisComp.java,v 1.1 2000/05/24 16:45:19 cdgill Exp
// 
// = FILENAME
//    MultiDoubleVisComp.java
//
// = AUTHOR
//    Chris Gill (core graph functionality), based on
//    DoubleVisComp.java by Seth Widoff, and Michael Kircher
//    (mk1@cs.wustl.edu)
//
// = DESCRIPTION
//   This is a Visualization Component for displaying multiple
//   plots of double precision integer values.
//
// ============================================================================


import java.awt.*;
import java.util.*;

public class MultiDoubleVisComp extends Canvas implements VisComp
{
  private static int plot_count_default_ = 5;

  private static final int MIN_SPACING = 2;
  private static final int POINT_HISTORY = 200;
  private static final Font FONT = new Font ("Dialog", Font.PLAIN, 10);
  
  private Queue plots_ [];
  private int plot_count_ = 0;
  private String title_;
  private Graphics offgraphics_;
  private Image offscreen_;
  private Dimension offscreensize_;
  private int max_value_;
  private int old_max_value_;
  private int spacing_;
  private boolean max_increased_ = false;
  private static boolean monotonic_scale_ = false;

  private float local_max_[];
  private static float local_max_values_ [];
  private static int local_max_value_count_ = 0;
  private int local_max_value_index_[];

  public MultiDoubleVisComp ()
  {
    super ();

    // Re-initialize the global and local arrays of local maxima.
    local_max_init (plot_count_default_);

    // Set up the array of plot queues
    plot_count_ =  plot_count_default_;
    plots_ = new Queue [plot_count_];
    for (int i = 0; i < plot_count_; ++i)
      {
        plots_ [i] = new Queue ();
      }

    spacing_ = MIN_SPACING;
    title_ = "";
    max_value_ = 1;
    old_max_value_ = max_value_;
    
    java.util.Random rand = new java.util.Random (System.currentTimeMillis ());
    float hue_ = rand.nextFloat ();
    float brightness = rand.nextFloat ();

    hue_ += .075;
    
    if (hue_ > 1.0)
      hue_ -= 1.0;
    
    if (brightness > 0.75)
      brightness -= 0.25;
    
    Color new_color = Color.getHSBColor (hue_, 1, brightness);

    this.setBackground (new_color);
    this.setForeground (Color.white);
  }

  public static synchronized void plot_count_default (int d) {
    plot_count_default_ = d;
  }

  public static synchronized int plot_count_default () {
    return plot_count_default_;
  }

  public static synchronized void monotonic_scale (boolean b) {
    monotonic_scale_ = b;
  }

  public static synchronized boolean monotonic_scale () {
    return monotonic_scale_;
  }

  public void resize (int new_plot_count)
  {
    if (new_plot_count > plot_count_)
      {
        // Re-initialize the global and local arrays of local maxima.
        local_max_init (new_plot_count);

        // Set up the array of plot queues
        Queue plots_temp [] = new Queue [new_plot_count];
        for (int i = 0; i < new_plot_count; ++i)
          {
            if (i < plot_count_)
	      {
                plots_temp [i] = plots_ [i];
	      }
            else
	      {
                plots_temp [i] = new Queue ();
	      }
          }

        plots_ = plots_temp;
      }

    plot_count_ = new_plot_count;
  }



  public void setName (String title) {
      title_ = title;
  }
  
  public int getProperty () {
      return Properties.MULTIDOUBLE;
    }

  public Dimension getMinimumSize () {
      return new Dimension (75, 75);
    }

  public Dimension getPreferredSize () {
      return new Dimension (175, 175);
    }
  
  public String getName () {
    return title_;
  }

  public int getMax () {
      return old_max_value_;
    }

  public void update (java.util.Observable observable, java.lang.Object obj)
  {
    Double double_array[];
    try {
      double_array = (Double[]) obj;  
    }
    catch (Exception excp) {
      double_array = new Double [plot_count_];
      for (int j = 0; j < plot_count_; ++j)
	{
          double_array [j] = new Double (0.0);
	} 
      System.out.println (excp);
      System.out.println ("Multi Double Visualization Component received wrong data type!");
    }

    for (int i = 0; i < double_array.length && i < plot_count_; ++i)
      {
        float new_point = double_array [i].floatValue ();
        Float temp = (Float) plots_ [i].dequeue_tail ();
        plots_ [i].enqueue_head (new Float (new_point));
    
        if (new_point > local_max_ [i])
          {
            local_max_ [i] = new_point;
            local_max_values_ [local_max_value_index_ [i]] = local_max_ [i];
          }
     
        if (monotonic_scale_)
          {
            float global_max = 0;
            global_max = global_max_value ();

            while (global_max > max_value_)
              max_value_ *= 2;
    
            while ((global_max < max_value_/2) && (max_value_ > old_max_value_))
              max_value_ /= 2;
          }
        else
          {
            while (local_max_ [i] > max_value_)
              max_value_ *= 2;
    
            while ((local_max_ [i] < max_value_/2) && (max_value_ > old_max_value_))
              max_value_ /= 2;
          }
      }

    repaint ();
  }


  public void update (Graphics g)
  {
    int x1, y1, x2, y2;
    float tmp, value_1, value_2;
    Dimension d = getSize ();
    FontMetrics fm = g.getFontMetrics ();
    int fheight = fm.getHeight ();
    String value =  "Scale: " + max_value_ ;
    
    if ((offscreen_ == null) ||
	(offscreensize_.width != d.width - 8) ||
	(offscreensize_.height != d.height - 8))
      {
	offscreen_ = createImage (d.width - 8, d.height - 8);
	offscreensize_ = new Dimension (d.width - 8, d.height - 8);
	offgraphics_ = offscreen_.getGraphics ();
	offgraphics_.setFont (FONT);
      }

    g.setColor (Color.lightGray);
    g.draw3DRect (0, 0, d.width - 1, d.height - 1, true);
    g.draw3DRect (1, 1, d.width - 3, d.height - 3, true);
    g.draw3DRect (2, 2, d.width - 5, d.height - 5, true);
    
    offgraphics_.setColor (getBackground ());
    offgraphics_.fillRect (0, 0, offscreensize_.width, offscreensize_.height);
    offgraphics_.setColor (getForeground ());    
    offgraphics_.drawString (title_, 5, fheight);
    offgraphics_.drawString (value, 5, offscreensize_.height - 5);

    for (int i = 0; i < plot_count_; ++i)
      {
        local_max_ [i] = 0;
        Enumeration queue_iter = plots_ [i].forward_iterator ();
        value_1 = ((Float) queue_iter.nextElement ()).floatValue ();
        x1 = d.width - 8;
        while (queue_iter.hasMoreElements ())
          {
	    value_2 = ((Float) queue_iter.nextElement ()).floatValue ();
	
	    if (value_1 > local_max_ [i])
	      local_max_ [i] = value_1;
	
            y1 = normalize (offscreensize_.height - fheight, value_1, i);
            y2 = normalize (offscreensize_.height - fheight, value_2, i);
	
	    tmp = value_2;
	    value_2 = value_1;
	    value_1 = tmp;

            x2 = x1 - spacing_;
            offgraphics_.drawLine (x1, y1, x2, y2);
            x1 = x2;
	    if (x1 <= 5)
	      break;
          }

        local_max_values_ [local_max_value_index_ [i]] = local_max_ [i];
      }

    g.drawImage (offscreen_, 3, 3, null);
  }
  
  public void paint (Graphics g)
  {
    Dimension d = getSize ();

    for (int i = 0; i < plot_count_; ++i)
      {
        int plot_length = plots_ [i].length ();
        int num_points = d.width / spacing_;
    
        if (plots_[i].length () < num_points)
          {
            for (int j = 0; j < num_points - plot_length; j++)
	      plots_ [i].enqueue_tail (new Float (0));
          }
        else if (plots_ [i].length () > num_points)
          {
	    for (int j = 0; j < plot_length - num_points; j++)
	      plots_ [i].dequeue_tail ();
          }
      }

    update (g);
  }

  private static synchronized float global_max_value () {
    float result = 0;

    for (int i = 0; i < local_max_value_count_; ++i)
      {
        if (result < local_max_values_ [i])
          {
            result = local_max_values_ [i];
          }
      }

    return result;
  }

  private synchronized void local_max_init (int new_count) {

    if (new_count > plot_count_)
      {
        // New larger static global array of all local maxima values
        float local_max_values_temp [] = 
          new float [local_max_value_count_ + new_count - plot_count_];

        // Copy the previously stored maxima (if any) into the new array.
        for (int i = 0;
             i < local_max_value_count_ + new_count - plot_count_;
             ++i)
          {
            if (i < local_max_value_count_)
	      {
                local_max_values_temp [i] = local_max_values_ [i];
	      }
            else
              {
                local_max_values_temp [i] = 0;
	      }
          }

        // Create new, larger, non-static arrays for the local maxima, indices
        float local_max_temp [] = new float [new_count];
        int local_max_value_index_temp [] = new int [new_count];

        for (int j = 0; j < new_count; ++j)
          {
            if (j < plot_count_)
 	      {
                local_max_temp [j] = local_max_ [j];
                local_max_value_index_temp [j] = local_max_value_index_ [j];
	      }
            else
 	      {
                local_max_temp [j] = 0;
                local_max_value_index_temp [j] =
                  local_max_value_count_ + j - plot_count_;
	      }
          }

        // Store the newly (re)allocated arrays, set the new global array count
        local_max_values_ = local_max_values_temp;
        local_max_ = local_max_temp;
        local_max_value_index_ = local_max_value_index_temp;
        local_max_value_count_ += new_count - plot_count_;
      }
  }


  private int normalize (int height, float coord, int plot_index)
  {
    // First, figure out the ratio of the value to the max value
    float ratio = (float) coord/max_value_;

    // Second, compute pixel location based on which plot we're drawing
    float pixels = (float) (height*(plot_index + ratio))/plot_count_;

    // Then, subtract the pixel location
    float location = (float) height - pixels;
    
    return Math.round (location);
  }
}


