import java.io.*;
import java.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.net.*;
import javax.sound.sampled.*;

public class Grain extends Applet implements MouseListener, MouseMotionListener
{
  Graphics graphics;

  final static int IMAGE_WIDTH = 400;
  final static int IMAGE_HEIGHT = 200;

  int lastX, lastY;

    TextArea textArea;
  Panel textPanel;
      Slider strokeSlider;
      Slider shadeSlider;
      Slider spraySlider;
      Slider minFreqSlider;
      Slider maxFreqSlider;
      Slider powerSlider;
          Label xCoord1;
          Label xCoord2;
        Panel xCoordPanel;
          Label yCoord1;
          Label yCoord2;
        Panel yCoordPanel;
          Label timeCoord1;
          Label timeCoord2;
        Panel timeCoordPanel;
          Label freqCoord1;
          Label freqCoord2;
        Panel freqCoordPanel;
      Panel positionPanel;
          Label renderLabel;
          Label renderPercentLabel;
        Panel renderLabelPanel;
          Button buttonRenderRun;
          Button buttonRenderAbort;
        Panel buttonPairRenderPanel;
      Panel renderPanel;
          Label playLabel;
        Panel playLabelPanel;
          Button buttonPlay;
        Panel buttonPlayPanel;
      Panel playPanel;
          Label imgToB64Label;
        Panel imgToB64LabelPanel;
          Button buttonImgToB64;
        Panel buttonPairImgToB64Panel;
      Panel imgToB64Panel;
          Label b64ToImgLabel;
        Panel b64ToImgLabelPanel;
          Button buttonB64ToImg;
        Panel buttonPairB64ToImgPanel;
      Panel b64ToImgPanel;
    Panel buttonPanelRow1;
    Panel buttonPanelRow2;
    Panel buttonPanelRow3;
    Panel buttonPanelRow4;
  Panel buttonPanel;

  Color penColor;

  Random random;

  Color panelColor;

  Image image;
  byte[] imageBinary;

  Base64Encode base64Encode;
  Base64Decode base64Decode;

  static final int WAVEFORM_SAMPLES = 250000;
  byte[] soundBinary;

  String text;

  String setLabelFloat(float f) {
    return String.valueOf(f);
  }

  String setLabelInt(int i) {
    return String.valueOf(i);
  }

  void getPenColor() {
    int shade = shadeSlider.getValue();
    penColor = new Color(shade, shade, shade);
  }

  int getTime(int i) {
    return
      (int)((((double)i / (double)IMAGE_WIDTH) *
         (double)WAVEFORM_SAMPLES / 44100.0) * 1000.0);
  }

  int getFreq(int j) {
    double value1 = (double)minFreqSlider.getValue();
    double value2 = (double)maxFreqSlider.getValue();
    return
      (int)((1.0 - ((double)j / (double)IMAGE_HEIGHT)) *
        Math.abs(value2 - value1) + Math.min(value1, value2));
  }

  public void init() {
    random = new Random();

    graphics = getGraphics();
    
    this.addMouseListener(this);
    this.addMouseMotionListener(this);
    this.setSize(740, 407);

    panelColor = new Color(200, 200, 200);

    textArea = new TextArea("", 15, 80, TextArea.SCROLLBARS_VERTICAL_ONLY);
    textArea.setEditable(true);
    textArea.setFont(new Font("Courier", Font.PLAIN, 6));
    textPanel = new Panel();
    textPanel.setBackground(Color.white);

    strokeSlider = new Slider("Stroke", 5, 1, 12, 2, panelColor);

    shadeSlider = new Slider("Shade", 0, 0, 305, 50, panelColor);
    getPenColor();

    spraySlider = new Slider("Spray", 0, 0, 48, 8, panelColor);

    minFreqSlider = new Slider("Min Freq", 100, 20, 24000, 4000, panelColor);

    maxFreqSlider = new Slider("Max Freq", 15000, 20, 24000, 4000, panelColor);

    powerSlider = new Slider("Power", 6, 0, 12, 2, panelColor);

    renderLabel = new Label("Render", Label.RIGHT);
    renderPercentLabel = new Label(0 + "%", Label.LEFT);
    renderLabelPanel = new Panel();
    renderLabelPanel.setLayout(new FlowLayout());
    renderLabelPanel.setBackground(panelColor);
    renderLabelPanel.add(renderLabel);
    renderLabelPanel.add(renderPercentLabel);

    buttonRenderRun = new Button("Run");
    buttonRenderRun.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        RenderWave renderWave =
          new RenderWave(
            image, IMAGE_WIDTH, IMAGE_HEIGHT, WAVEFORM_SAMPLES, (short)1,
            Math.min((double)minFreqSlider.getValue(), 
              (double)maxFreqSlider.getValue()),
            Math.max((double)minFreqSlider.getValue(), 
              (double)maxFreqSlider.getValue()),
            powerSlider.getValue(),
            renderPercentLabel);
        renderPercentLabel.setText(0 + "%");
        soundBinary = renderWave.getBinary();
      }
    });

    buttonRenderAbort = new Button("Abort");
    buttonRenderAbort.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
      }
    });

    buttonPairRenderPanel = new Panel();
    buttonPairRenderPanel.setLayout(new FlowLayout());
    buttonPairRenderPanel.setBackground(panelColor);
    buttonPairRenderPanel.add(buttonRenderRun);
    buttonPairRenderPanel.add(buttonRenderAbort);

    renderPanel = new BorderPanel();
    renderPanel.setLayout(new BorderLayout());
    renderPanel.setBackground(panelColor);
    renderPanel.add(renderLabelPanel, "North");
    renderPanel.add(buttonPairRenderPanel, "South");

    playLabel = new Label("Sound Wav To Base64", Label.CENTER);
    playLabelPanel = new Panel();
    playLabelPanel.setLayout(new FlowLayout());
    playLabelPanel.setBackground(panelColor);
    playLabelPanel.add(playLabel);

    buttonPlay = new Button("Play");
    buttonPlay.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        playSound();
      }
    });

    buttonPlayPanel = new Panel();
    buttonPlayPanel.setLayout(new FlowLayout());
    buttonPlayPanel.setBackground(panelColor);
    buttonPlayPanel.add(buttonPlay);

    playPanel = new BorderPanel();
    playPanel.setLayout(new BorderLayout());
    playPanel.setBackground(panelColor);
    playPanel.add(playLabelPanel, "North");
    playPanel.add(buttonPlayPanel, "South");

    imgToB64Label = new Label("Image To Bmp Base64", Label.CENTER);
    imgToB64LabelPanel = new Panel();
    imgToB64LabelPanel.setLayout(new FlowLayout());
    imgToB64LabelPanel.setBackground(panelColor);
    imgToB64LabelPanel.add(imgToB64Label);

    buttonImgToB64 = new Button("Run");
    buttonImgToB64.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        ImageToBmp imageToBmp =
          new ImageToBmp(image, IMAGE_WIDTH, IMAGE_HEIGHT);
        imageBinary = imageToBmp.getBinary();
        base64Encode = new Base64Encode(imageBinary);
        textArea.setText(""); // clear out text
        text = base64Encode.getText();
        textArea.append(text);
      }
    });

    buttonPairImgToB64Panel = new Panel();
    buttonPairImgToB64Panel.setLayout(new FlowLayout());
    buttonPairImgToB64Panel.setBackground(panelColor);
    buttonPairImgToB64Panel.add(buttonImgToB64);

    imgToB64Panel = new BorderPanel();
    imgToB64Panel.setLayout(new BorderLayout());
    imgToB64Panel.setBackground(panelColor);
    imgToB64Panel.add(imgToB64LabelPanel, "North");
    imgToB64Panel.add(buttonPairImgToB64Panel, "South");

    b64ToImgLabel = new Label("Base64 Bmp To Image", Label.CENTER);
    b64ToImgLabelPanel = new Panel();
    b64ToImgLabelPanel.setLayout(new FlowLayout());
    b64ToImgLabelPanel.setBackground(panelColor);
    b64ToImgLabelPanel.add(b64ToImgLabel);

    buttonB64ToImg = new Button("Run");
    buttonB64ToImg.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        text = textArea.getText();
        if (!text.equals("")) {
          Base64Decode base64Decode = new Base64Decode(text);
          imageBinary = base64Decode.getBinary();
          BmpToImage bmpToImage = new BmpToImage(imageBinary, image);
          paint(graphics);
        }
      }
    });

    buttonPairB64ToImgPanel = new Panel();
    buttonPairB64ToImgPanel.setLayout(new FlowLayout());
    buttonPairB64ToImgPanel.setBackground(panelColor);
    buttonPairB64ToImgPanel.add(buttonB64ToImg);

    b64ToImgPanel = new BorderPanel();
    b64ToImgPanel.setLayout(new BorderLayout());
    b64ToImgPanel.setBackground(panelColor);
    b64ToImgPanel.add(b64ToImgLabelPanel, "North");
    b64ToImgPanel.add(buttonPairB64ToImgPanel, "South");

    xCoord1 = new Label("   x", Label.RIGHT);
    xCoord2 = new Label("     ", Label.LEFT);
    xCoordPanel = new Panel();
    xCoordPanel.setLayout(new BorderLayout());
    xCoordPanel.setBackground(panelColor);
    xCoordPanel.add(xCoord1, "West");
    xCoordPanel.add(xCoord2, "East");

    yCoord1 = new Label("   y", Label.RIGHT);
    yCoord2 = new Label("     ", Label.LEFT);
    yCoordPanel = new Panel();
    yCoordPanel.setLayout(new BorderLayout());
    yCoordPanel.setBackground(panelColor);
    yCoordPanel.add(yCoord1, "West");
    yCoordPanel.add(yCoord2, "East");

    timeCoord1 = new Label("  ms", Label.RIGHT);
    timeCoord2 = new Label("     ", Label.LEFT);
    timeCoordPanel = new Panel();
    timeCoordPanel.setLayout(new BorderLayout());
    timeCoordPanel.setBackground(panelColor);
    timeCoordPanel.add(timeCoord1, "West");
    timeCoordPanel.add(timeCoord2, "East");

    freqCoord1 = new Label("freq", Label.RIGHT);
    freqCoord2 = new Label("     ", Label.LEFT);
    freqCoordPanel = new Panel();
    freqCoordPanel.setLayout(new BorderLayout());
    freqCoordPanel.setBackground(panelColor);
    freqCoordPanel.add(freqCoord1, "West");
    freqCoordPanel.add(freqCoord2, "East");

    positionPanel = new BorderPanel();
    positionPanel.setLayout(new GridLayout(2, 2, 0, 0));
    positionPanel.setBackground(panelColor);
    positionPanel.add(xCoordPanel);
    positionPanel.add(timeCoordPanel);
    positionPanel.add(yCoordPanel);
    positionPanel.add(freqCoordPanel);

    buttonPanelRow1 = new Panel();
    buttonPanelRow1.setLayout(new FlowLayout());
    buttonPanelRow1.setBackground(panelColor);
    buttonPanelRow1.add(strokeSlider);
    buttonPanelRow1.add(shadeSlider);
    buttonPanelRow1.add(spraySlider);
    buttonPanelRow1.add(minFreqSlider);
    buttonPanelRow1.add(maxFreqSlider);
    buttonPanelRow1.add(powerSlider);

    buttonPanelRow2 = new Panel();
    buttonPanelRow2.setLayout(new FlowLayout());
    buttonPanelRow2.setBackground(panelColor);
    buttonPanelRow2.add(renderPanel);
    buttonPanelRow2.add(playPanel);
    buttonPanelRow2.add(positionPanel);

    buttonPanelRow4 = new Panel();
    buttonPanelRow4.setLayout(new FlowLayout());
    buttonPanelRow4.setBackground(panelColor);
    buttonPanelRow4.add(imgToB64Panel);
    buttonPanelRow4.add(b64ToImgPanel);

    buttonPanelRow3 = new Panel();
    buttonPanelRow3.setLayout(new BorderLayout());
    buttonPanelRow3.setBackground(panelColor);
    buttonPanelRow3.add(buttonPanelRow2, "North");
    buttonPanelRow3.add(buttonPanelRow4, "South");

    buttonPanel = new Panel();
    buttonPanel.setLayout(new BorderLayout());
    buttonPanel.setBackground(panelColor);
    buttonPanel.add(buttonPanelRow1, "North");
    buttonPanel.add(buttonPanelRow3, "South");

    setLayout(new BorderLayout());
    setBackground(Color.white);
    setFont(new Font("Courier", Font.PLAIN, 10));
    add(textArea, "East");
    add(buttonPanel, "South");

    image = createImage(IMAGE_WIDTH, IMAGE_HEIGHT);
    initImage(Color.white);
  }

  void initImage(Color penColor) {
    int i;
    for (i = 0; i < IMAGE_HEIGHT; i++)
      drawLine(i, 0, i, IMAGE_WIDTH, penColor);
  }

  public void paint(Graphics g) {
    g.drawImage(image, 0, 0, this);
  }

  public String getAppletInfo() {
    return "Grain by Rick Miskowski";
  }

  public void mousePressed(MouseEvent e) {
    lastX = e.getX();
    lastY = e.getY();
  }

  private int getRandomSpray() {
    int spray = spraySlider.getValue();
    if (spray == 0)
      return 0;
    return random.nextInt() % spray;
  }

  private void drawLine(int lastX, int lastY, int x, int y, Color penColor) {
    int randX = getRandomSpray();
    int randY = getRandomSpray();

    x += Math.abs(randX);
    y += randY;

    Graphics g11 = this.getGraphics();
    Graphics2D g12 = (Graphics2D)g11;
    g12.setStroke(
      new BasicStroke(
        (float)strokeSlider.getValue(),
        BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    g12.setColor(penColor);
    g12.drawLine(x, y, x, y);

    Graphics g21 = image.getGraphics();
    Graphics2D g22 = (Graphics2D)g21;
    g22.setStroke(
      new BasicStroke(
        (float)strokeSlider.getValue(),
        BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    g22.setColor(penColor);
    g22.drawLine(x, y, x, y);
  }

  public void mouseDragged(MouseEvent e) {
    int x = e.getX();
    int y = e.getY();

    if (x < IMAGE_WIDTH && x >= 0 && y < IMAGE_HEIGHT && y >= 0) {
      xCoord2.setText(setLabelInt(x));
      yCoord2.setText(setLabelInt(y));
      timeCoord2.setText(setLabelInt(getTime(x)));
      freqCoord2.setText(setLabelInt(getFreq(y)));
    }

    getPenColor();
    drawLine(lastX, lastY, x, y, penColor);

    lastX = x;
    lastY = y;
  }
  
  public void mouseReleased(MouseEvent e) {
  }

  public void mouseClicked(MouseEvent e) {
  }

  public void mouseEntered(MouseEvent e) {
  }

  public void mouseExited(MouseEvent e) {
  }

  public void mouseMoved(MouseEvent e) {
    int x = e.getX();
    int y = e.getY();

    if (x < IMAGE_WIDTH && x >= 0 && y < IMAGE_HEIGHT && y >= 0) {
      xCoord2.setText(setLabelInt(x));
      yCoord2.setText(setLabelInt(y));
      timeCoord2.setText(setLabelInt(getTime(x)));
      freqCoord2.setText(setLabelInt(getFreq(y)));
    }
  }

  public void playSound() {
    if (soundBinary == null)
      return;
    try {
      ByteArrayInputStream in = new ByteArrayInputStream(soundBinary);
      AudioInputStream ain =
        AudioSystem.getAudioInputStream((InputStream)in);
      DataLine.Info info =
        new DataLine.Info(Clip.class, ain.getFormat());
      Clip clip;
      clip = (Clip)AudioSystem.getLine(info);
      clip.open(ain);
      clip.start();
//    clip.close();

      base64Encode = new Base64Encode(soundBinary);
      textArea.setText(""); // clear out text
      text = base64Encode.getText();
      textArea.append(text);
    }
    catch (IOException e) {}
    catch (LineUnavailableException e) {}
    catch (UnsupportedAudioFileException e) {}
  }
}
