import java.applet.*; 
import java.awt.*; 
import java.util.*; 
import java.net.*; 
import java.io.*; 
import com.alulabs.wsaudio.*;

public final class Player extends Applet implements WSAudioListener {
 WSAudio  wsaudio;
 PStop  bStop; 
 PPlay  bStart; 
 PPause  bPause; 
 Scrollbar sVolume; 
 Scrollbar      sFreq; 
 Button  lFreq; 
 PGaugeBar gPosition; 
 int   stallCount=0; 
 boolean  forceFreq=false; 
 String   host; 
 Vector  urls=new Vector(); 
 Choice  cStreams; 
 Checkbox cbLowSpeed=null; 
 URL   streamURL=null;

 public void init() { 
  System.out.println("WebSynth Player\nAlu o'Neal http://www.alulabs.com");
  wsaudio=new WSAudio();
  wsaudio.setListener(this);
  setBackground(new Color(128,128,128)); 
  setForeground(Color.yellow);
  setLayout(new BorderLayout());
  PPanel panel=new PPanel(); 
  panel.setInsets(new Insets(1,1,1,1)); 
  panel.setForeground(Color.black); 
  panel.setLayout(new FlowLayout(FlowLayout.CENTER,0,0)); 
  panel.add(bStart=new PPlay()); 
  panel.add(bPause=new PPause()); 
  panel.add(bStop=new PStop()); 
  add("North",panel); 
  bPause.enable(false); 
  bStop.enable(false);
  panel=new PPanel(); 
  panel.setInsets(new Insets(8,8,8,8)); 
  panel.setLayout(new GridLayout(3,1,0,5)); 
  Panel p=new Panel(); 
  p.setLayout(new BorderLayout(5,0)); 
  p.add("North",new Label("Track selection")); 
  p.add("Center",cStreams=new Choice()); 
  cStreams.setForeground(Color.black); 
  p.add("East",cbLowSpeed=new Checkbox("Low Speed")); 
  cbLowSpeed.setState(false); 
  panel.add(p); 
  p=new Panel(); 
  p.setLayout(new BorderLayout(10,0)); 
  p.add("North",new Label("Volume")); 
  p.add("Center",sVolume=new Scrollbar(Scrollbar.HORIZONTAL,100,1,0,100)); 
  panel.add(p); 
  p=new Panel(); 
  p.setLayout(new BorderLayout(10,0)); 
  p.add("North",new Label("Frequency")); 
  p.add("Center",sFreq=new Scrollbar(Scrollbar.HORIZONTAL,22050,1,2000,32000)); 
  sFreq.enable(false); 
  p.add("East",lFreq=new Button("Original")); 
  panel.add(p); 
  add("Center",panel);
  add("South",gPosition=new PGaugeBar()); 
  gPosition.setForeground(new Color(100,0,0));
  try { 
   int i=0; 
   while(i>=0) { 
    String param=getParameter("STREAM"+(i++)); 
    if (param!=null) { 
     StringTokenizer st=new StringTokenizer(param,":"); 
     String desc=st.nextToken(); 
     String surl=st.nextToken(); 
     if (desc!=null && surl!=null) { 
      cStreams.addItem(desc); 
      urls.addElement(new URL(getCodeBase(),surl)); 
     } 
    }  
    else i=-1; 
   } 
   cStreams.select(0); 
  } catch(MalformedURLException exc) { 
   exc.printStackTrace(); 
  } 
 }

 public void start() { 
  setMessage("Ready."); 
 }

 public void stop() { 
  stopPlaying(); 
 }

 private void startPlaying() { 
  streamURL=(URL)urls.elementAt(cStreams.getSelectedIndex()); 
  if (streamURL!=null) { 
   wsaudio.stopPlaying(); 
   try { 
    wsaudio.setVolume((double)sVolume.getValue()/100.0); 
    if (forceFreq) wsaudio.setFrequency((long)sFreq.getValue()); 
    else wsaudio.setFrequency(0); 
    sFreq.enable(false); 
    lFreq.enable(false); 
    cStreams.enable(false); 
    bPause.enable(true); 
    bPause.repaint(); 
    bStop.enable(true); 
    bStop.repaint();
    bStart.enable(false);
    bStart.repaint();
    if (cbLowSpeed.getState()) wsaudio.setPreferredSection(0);
    else wsaudio.setPreferredSection(1);
    wsaudio.startPlaying(streamURL.toString()); 
   } catch(Exception exc) { 
    exc.printStackTrace(); 
   } 
  } 
 }

 private void stopPlaying() { 
  wsaudio.stopPlaying(); 
  wsaudio.enable(true); 
  if (forceFreq) sFreq.enable(true); 
  cStreams.enable(true); 
  bPause.enable(false); 
  bPause.repaint(); 
  bStop.enable(false); 
  bStop.repaint(); 
  bStart.enable(true);
  bStart.repaint();
  gPosition.setValue(0); 
  gPosition.repaint(); 
  setMessage("Ready."); 
  repaint(); 
 }

 private void pausePlaying() { 
  wsaudio.setPaused(true); 
  bPause.enable(false); 
  bPause.repaint(); 
  bStart.enable(true);
  bStart.repaint();
 }

 private void restartPlaying() { 
  wsaudio.setPaused(false); 
  bPause.enable(true); 
  bPause.repaint(); 
  bStart.enable(false);
  bStart.repaint();
 }

 private void setMessage(String s) { 
  showStatus(s); 
  gPosition.setMessage(s); 
  gPosition.repaint(); 
 }

 public boolean handleEvent(Event e) { 
  switch(e.id) { 
   case Event.SCROLL_LINE_UP: 
   case Event.SCROLL_PAGE_UP: 
   case Event.SCROLL_LINE_DOWN: 
   case Event.SCROLL_PAGE_DOWN: 
   case Event.SCROLL_ABSOLUTE: 
    if (e.target==sVolume) wsaudio.setVolume((double)sVolume.getValue()/100.0); 
    else if (e.target==sFreq) lFreq.setLabel(""+sFreq.getValue()); 
    return true; 
  } 
  return super.handleEvent(e); 
 }

 public boolean action(Event e, Object what) { 
  if (e.target==lFreq) { 
   if (forceFreq) { 
    forceFreq=false; 
    lFreq.setLabel("Original"); 
    sFreq.enable(false); 
    repaint(); 
   } 
   else { 
    forceFreq=true; 
    sFreq.enable(true); 
    if (cbLowSpeed.getState()) sFreq.setValue(8012); 
    else sFreq.setValue(22050); 
    lFreq.setLabel(""+sFreq.getValue()); 
    repaint(); 
   } 
  } 
  else if (e.target==cbLowSpeed) { 
   forceFreq=false; 
   lFreq.setLabel("Original"); 
   sFreq.enable(false); 
  } 
  else if (e.target==bStop) stopPlaying(); 
  else if (e.target==bStart) { 
   if (wsaudio.getStatus()==wsaudio.STATUS_PAUSED) restartPlaying(); 
   else startPlaying(); 
  } 
  else if (e.target==bPause) pausePlaying(); 
  return true; 
 }

 //WSAudioListener Implementataion
 public void streamOpening() {
  setMessage("...contacting host "+streamURL.getHost()); 
  repaint();
 }

 public void connectionRefused() {
  lFreq.enable(true); 
  if (forceFreq) sFreq.enable(true); 
  cStreams.enable(true); 
  bPause.enable(false); 
  bPause.repaint(); 
  bStop.enable(false); 
  bStop.repaint(); 
  bStart.enable(true);
  bStart.repaint();
  gPosition.setValue(0); 
  gPosition.repaint(); 
  setMessage("Connection refused"); 
  repaint();
 }

 public void streamOpened() {
  setMessage("Host contacted: parsing content..."); 
  repaint();
 }

 public void invalidStream() {
  lFreq.enable(true); 
  if (forceFreq) sFreq.enable(true); 
  cStreams.enable(true); 
  bPause.enable(false); 
  bPause.repaint(); 
  bStop.enable(false); 
  bStop.repaint(); 
  bStart.enable(true);
  bStart.repaint();
  gPosition.setValue(0); 
  gPosition.repaint(); 
  setMessage("Invalid content..."); 
  repaint();
 }

 public void sectionSelected(int section) {
  setMessage("Content accepted. Starting stream..."); 
  repaint();
 }

 public void streamStarted() {
  setMessage("Stream started. Buffering...");
  repaint();
 }

 public void audioStarted() {
  setMessage("...playing ("+wsaudio.getFrequency()+"Hz)...");
  repaint();
 }

 public void streamStopped() {
  lFreq.enable(true); 
  if (forceFreq) sFreq.enable(true); 
  cStreams.enable(true); 
  bPause.enable(false); 
  bPause.repaint(); 
  bStop.enable(false); 
  bStop.repaint(); 
  bStart.enable(true);
  bStart.repaint();
  gPosition.setValue(0); 
  gPosition.repaint(); 
  setMessage("Ready.");
  repaint();
 }

 public void streamRepositioning() {
 }

 public void timeEvent(String message) {
 }

 public void positionChanged(long position) {
    gPosition.setMax((int)(wsaudio.getPlayLength()<<8)); 
    gPosition.setValue((int)(position<<8)); 
    gPosition.repaint(); 
 }

 public void appletStarted() {
 }

 public void appletStopped() {
 }

 public void buffering() {
  setMessage("Net congestion: buffering...");
  repaint();
 }

}

class PGaugeBar extends Panel { 
 
 
 int value=0;
 int max=100;
 String message=""; 
 
 public PGaugeBar() {
  super();
 } 
 
 
 void setMax(int m) {
  max=m;
 } 
 
 
 void setValue(int v) {
  value=v;
 } 
 
 
 public void update(Graphics g) {
  paint(g);
 } 
 
 void setMessage(String s) { 
  message=s; 
 }
 public Dimension preferredSize() { 
  return new Dimension(100,16); 
 }
 public Dimension minimumSize() { 
  return preferredSize(); 
 } 
 
 public void paint(Graphics g) {
  int w=(int)(size().width*((double)value/(double)max));
  int h=size().height;
  g.setColor(getForeground());
  g.fill3DRect(0,0,w,h,true);
  g.setColor(getBackground());
  g.fill3DRect(w,0,size().width-w,h,true);
  g.setColor(Color.white); 
  FontMetrics fm=getFontMetrics(g.getFont()); 
  g.drawString(message,6,fm.getAscent()); 
 }
} 
 
abstract class PButton extends Panel {
 boolean pressed=false; 
 boolean rised=false;

 PButton() { 
  super(); 
  setForeground(new Color(100,0,0)); 
 }

 public boolean mouseDown(Event e, int x, int y) { 
  pressed=true; 
  repaint(); 
  return false; 
 }

 public boolean mouseUp(Event e, int x, int y) { 
  Dimension d=size(); 
  pressed=false; 
  repaint(); 
  if (x>0 && y>0 && x<d.width && y<d.height) 
   deliverEvent(new Event(this,0,Event.ACTION_EVENT,x,y,e.key,e.modifiers,null)); 
  return false; 
 }

 public boolean mouseEnter(Event e, int x, int y) { 
  rised=true; 
  repaint(); 
  return false; 
 }

 public boolean mouseExit(Event e, int x, int y) { 
  rised=false; 
  repaint(); 
  return false; 
 }

 public Dimension preferredSize() { 
  return new Dimension(40,20); 
 }

 public Dimension minimumSize() { 
  return new Dimension(40,20); 
 }

 public void update(Graphics g) { 
  paint(g); 
 }

 public void paint(Graphics g) { 
  g.setColor(getBackground()); 
  if (rised || pressed) g.fill3DRect(0,0,size().width,size().height,!pressed); 
  else g.fillRect(0,0,size().width,size().height); 
  if (isEnabled()) g.setColor(getForeground()); 
  else g.setColor(getBackground().darker()); 
  paintImage(g); 
 }

 abstract void paintImage(Graphics g); 
}

class PPlay extends PButton {
 void paintImage(Graphics g) { 
  Dimension d=size(); 
  Polygon p=new Polygon(); 
  p.addPoint(10,4); 
  p.addPoint(10,d.height-4); 
  p.addPoint(d.width-10,d.height>>1); 
  g.fillPolygon(p); 
 } 
}

class PPause extends PButton { 
 void paintImage(Graphics g) { 
  Dimension d=size(); 
  int x=d.width>>1; 
  g.drawLine(x-3,4,x-3,d.height-4); 
  g.drawLine(x-2,4,x-2,d.height-4); 
  g.drawLine(x-1,4,x-1,d.height-4); 
  g.drawLine(x+1,4,x+1,d.height-4); 
  g.drawLine(x+2,4,x+2,d.height-4); 
  g.drawLine(x+3,4,x+3,d.height-4); 
 } 
}

class PStop extends PButton { 
 void paintImage(Graphics g) { 
  Dimension d=size(); 
  g.fillRect(10,4,d.width-20,d.height-8); 
 } 
}

class PPanel extends Panel {
 Insets insets=new Insets(1,1,1,1);
 public Insets insets() { 
  return insets; 
 }
 void setInsets(Insets i) { 
  insets=i; 
 }
 public void update(Graphics g) { 
  paint(g); 
 }
 public void paint(Graphics g) { 
  Dimension d=size(); 
  g.setColor(getBackground()); 
  g.fill3DRect(0,0,d.width,d.height,true); 
 }
}
