
// PrintAreas.java
// Andrew Davison, ad@coe.psu.ac.th, May 2023

/* Displays a maximum of MAX_PANES scrolling text panes
   in a window, divided into two rows. 

   The idea is that each thread is assigned its own
   text pane, which it can treat as a mini-output 
   window. This is much easier to read than a mixed-up
   jumble of output all printed to stdout.

   A thread uses PrintAreas.print() to add text to a pane, 
   but must specify the number of the pane or its title.

   If all the text panes are allocated then a thread's output
   is sent to stdout.

   The PrintAreas constructor is supplied with the maximum 
   number of panes required, and it will fill the window with
   that number of panes (up to a maximum of MAX_PANES).

  Usage:
    > compile.bat PrintAreas.java
*/


import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;


public class PrintAreas
{
  private static final int MAX_ROW_PANES = 6;
  private static final int MAX_PANES = MAX_ROW_PANES*2;
     // a max of two rows of panes, 6 panes per row

  private static final int VERT_SPACE = 15;
  private static final int HORIZ_SPACE = 5;
    // spacing between the panes

  private ArrayList<JLabel> titles = new ArrayList<>();
                 // title assigned to each pane
  private ArrayList<JTextArea> textAreas = new ArrayList<>();


  public PrintAreas(int n)
  {
    if ((n <= 0) || (n > MAX_PANES)) {
      System.out.println("No of panes out of range; using the maximum  (" + 
        MAX_PANES + ")");
      n = MAX_PANES;
    }

    JFrame f = new JFrame("PrintAreas");
    Container cp = f.getContentPane();
    cp.setLayout(new BorderLayout());
     
    // build first row of panes
    int numInRow1 = (n > MAX_ROW_PANES) ? MAX_ROW_PANES : n;
    n = n - numInRow1;   // remaining number of panes to draw

    JPanel p1 = new JPanel();
    p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
                            // a horizontal layout
    p1.add(Box.createRigidArea(new Dimension(VERT_SPACE, 0)));

    for (int i=0; i < numInRow1; i++) {
      p1.add( buildTextArea(""+i));
      p1.add(Box.createRigidArea(new Dimension(VERT_SPACE, 0))); 
    }
    cp.add(p1, BorderLayout.CENTER);

    // second row of panes (if necessary)
    if (n > 0) {
      JPanel p2 = new JPanel();
      p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
      p2.add(Box.createRigidArea(new Dimension(VERT_SPACE, 0)));
   
      for (int i=0; i < n; i++) {
        p2.add( buildTextArea("" + (i+MAX_ROW_PANES)));
        p2.add(Box.createRigidArea(new Dimension(VERT_SPACE, 0))); 
      }
      cp.add(p2, BorderLayout.SOUTH);
    }

    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setResizable(false); 
    f.pack(); 
    f.setVisible(true);
  }  // end of PrintAreas()



  private JPanel buildTextArea(String title)
  /* a text area is a scrollable JTextArea with
     a title above it */
  {
    JPanel p = new JPanel();
    p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
    p.add(Box.createRigidArea(new Dimension(0, HORIZ_SPACE)));

    JLabel jl = new JLabel(title);
    jl.setAlignmentX(Component.CENTER_ALIGNMENT);
    titles.add(jl);
    p.add(jl);

    p.add(Box.createRigidArea(new Dimension(0, HORIZ_SPACE)));

    JTextArea ta = new JTextArea("", 15, 20);
    ta.setLineWrap(true);
    ta.setEditable(false);
    ta.setFont(new Font("Arial", Font.PLAIN, 14));
    textAreas.add(ta);

    JScrollPane sp = new JScrollPane(ta);
    sp.setVerticalScrollBarPolicy(
                 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    p.add(sp);
    p.add(Box.createRigidArea(new Dimension(0, HORIZ_SPACE))); 

    return p;
  }  // end of buildTextArea()



  public void setTitle(int pos, String title)
  /* assign a title to pane number pos. This is
     optional, since a thread can refer to a pane 
     using the pos number or a title.
  */
  { if ((pos < 0) || (pos >= titles.size()))
      System.out.println("Title at [" + pos + "]: " + title);
    else {
      JLabel jl = titles.get(pos);
      jl.setText(title);
      jl.repaint();
    }
  }  // end of setTitle()


  public void println(int pos, String s)
  {  print(pos, s + "\n");  }


  public void print(int pos, String s)
  // add s to the end of the 'pos' text pane
  {
    if ((pos < 0) || (pos >= textAreas.size())) {
      System.out.print("[" + pos + "]: " + s);
      System.out.flush();
    }
    else {
      JTextArea ta = textAreas.get(pos);
      ta.append(s);
      int len = ta.getDocument().getLength();
      ta.setCaretPosition(len);
      ta.repaint();
    }
  }  // end of print()



  public void println(String title, String s)
  {  print(title, s + "\n");  }


  public void print(String title, String s)
  // add s to the end of the text pane with the specified title
  {
    int pos = findPos(title);
    if (pos != -1)
      print(pos, s);
  }

  private int findPos(String title)
  {
    for (int i=0; i < titles.size(); i++) {
      if (titles.get(i).getText().equals(title))
        return i;
    }
    System.out.println(title + " not found in title labels");
    return -1;
  }  // end of findPos()


    
  // ------------------ test rig ---------------

  public static void main(String args[])
  {  
    PrintAreas s = new PrintAreas(6);

    s.setTitle(1, "One");
    s.println(0, "dog");
    s.print(2, "gone");
    s.println(0, "cat");

    for (int i=0; i < 30; i++) {
      s.println(0, "dog " + i);
      SpaceUtils.pause(500);
    }

  }  // end of main()

} // end of PrintAreas class
