/*
 * MPlayerGUI.java
 *
 * Created on 2009/01/29, 19:08:08
 */
package peca;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Timer;

/**
 *
 * @author falloutboy
 */
public class MPlayerGUI extends javax.swing.JFrame {

    // mms stream url
    String mms_url;
    private static final String mplayerPath =
            "/opt/local/bin/mplayer";
    private static final String[] flags = {"-slave",
        "-quiet",
        "-idle",
        "-double"};

    /** Creates new form MPlayerGUI */
    public MPlayerGUI(String url) {
        initComponents();
        setVisible(true);
        play(url);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        playButton = new javax.swing.JButton();
        volumeSlider = new javax.swing.JSlider();
        pauseButton = new javax.swing.JButton();
        jProgressBar1 = new javax.swing.JProgressBar();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        playButton.setText("Play");
        playButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                playButtonActionPerformed(evt);
            }
        });

        volumeSlider.setValue(25);
        volumeSlider.setName(""); // NOI18N
        volumeSlider.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                volumeSliderStateChanged(evt);
            }
        });

        pauseButton.setText("Pause");
        pauseButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                pauseButtonActionPerformed(evt);
            }
        });

        jLabel1.setText("buff");

        jLabel2.setText("0");

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .add(playButton)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(pauseButton))
            .add(volumeSlider, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
            .add(jProgressBar1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 190, Short.MAX_VALUE)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(jLabel1)
                .add(18, 18, 18)
                .add(jLabel2)
                .addContainerGap(118, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(playButton)
                    .add(pauseButton))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(volumeSlider, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jProgressBar1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jLabel1)
                    .add(jLabel2)))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed
        play(this.mms_url);
    }//GEN-LAST:event_playButtonActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        timer.stop();
        if (playerPrc != null) {
            send("quit\n");
            try {
                playerPrc.waitFor();
            } catch (InterruptedException ex) {
                Logger.getLogger(MPlayerGUI.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                this.outputstream.close();
                this.mplayerOutErr.close();
            } catch (IOException ex) {
                Logger.getLogger(MPlayerGUI.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                this.outputstream = null;
                this.mplayerOutErr = null;
                this.playerPrc = null;
            }
        }
    }//GEN-LAST:event_formWindowClosing

    private void volumeSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_volumeSliderStateChanged
        if (playerPrc != null) {
            int v = volumeSlider.getValue();
            String cmd = "set_property volume " + v + "\n";
            send(pause ? "pausing " + cmd : cmd);
        }
    }//GEN-LAST:event_volumeSliderStateChanged
    private boolean pause = false;
    private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
        if (playerPrc != null) {
            pause = !pause;
            send("pause\n");
        }
    }//GEN-LAST:event_pauseButtonActionPerformed

    /**
     * A thread that reads from an input stream and outputs to another line by line.
     * @author Adrian BER
     * @author falloutboy
     */
    private static class LineRedirecter extends Thread {

        /** The input stream to read from. */
        private InputStream in;
        /** The output stream to write to. */
        private OutputStream out;
        /** The prefix used to prefix the lines when outputting to the logger. */
        private String prefix;

        /**
         * @param in the input stream to read from.
         * @param out the output stream to write to.
         * @param prefix the prefix used to prefix the lines when outputting to the logger.
         */
        LineRedirecter(InputStream in, OutputStream out, String prefix) {
            this.in = in;
            this.out = out;
            this.prefix = prefix;
        }

        @Override
        public void run() {
            try {
                // creates the decorating reader and writer
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                PrintStream printStream = new PrintStream(out);
                String line;
                // read line by line
                while ((line = reader.readLine()) != null) {
                    printStream.println(line);
                }
            } catch (IOException ex) {
                Logger.getLogger(MPlayerGUI.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    Process playerPrc;
    OutputStream outputstream;
    BufferedReader mplayerOutErr;
    Timer timer;

    public void play(String mms_url) {
        this.mms_url = mms_url;
        if (playerPrc == null) {
            ArrayList<String> cmd = new ArrayList<String>();
            cmd.add(mplayerPath);
            cmd.addAll(Arrays.asList(flags));
            cmd.add(mms_url);
            try {
                playerPrc = Runtime.getRuntime().exec(cmd.toArray(new String[]{}));
                // create the piped streams where to redirect the standard output and error of MPlayer
                // specify a bigger pipesize
                PipedInputStream readFrom = new PipedInputStream();
                PipedOutputStream writeTo = new PipedOutputStream(readFrom);
                mplayerOutErr = new BufferedReader(new InputStreamReader(readFrom));

                // create the threads to redirect the standard output and error of MPlayer
                new LineRedirecter(playerPrc.getInputStream(), writeTo, "MPlayer says: ").start();
                new LineRedirecter(playerPrc.getErrorStream(), writeTo, "MPlayer encountered an error: ").start();

                outputstream = playerPrc.getOutputStream();
            } catch (IOException ex) {
                Logger.getLogger(MPlayerGUI.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            send("loadfile \"" + mms_url + "\"\n");
        }
        waitForAnswer("Starting playback...");
        timer = new Timer(2000, new ActionListener() {

            private static final String ans_pos = "ANS_stream_pos=";
            private static final String ans_end = "ANS_stream_end=";

            public void actionPerformed(ActionEvent e) {
                String cmd = pause ? "pausing get_property " : "get_property ";
//                long pos = Long.parseLong(send(cmd + "stream_pos\n", ans_pos).replace(ans_pos, ""));
//                System.out.println("pos:" + pos);
//                long size = Long.parseLong(send(cmd + "stream_end\n", ans_end).replace(ans_end, ""));
//                System.out.println("size:" + size);
//                long dif = size - pos;
//                jLabel2.setText(Long.toString(dif));

            }
        });
        timer.setInitialDelay(5000);
        timer.start();
    }

    private synchronized void send(String cmd) {
        if (outputstream != null) {
            try {
                outputstream.write(cmd.getBytes());
                outputstream.flush();
            } catch (IOException ex) {
                formWindowClosing(null);
            //Logger.getLogger(MPlayerGUI.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    private String send(String cmd, String expected) {
        send(cmd);
        if (expected != null) {
            String response = waitForAnswer(expected);
            return response;
        }
        return null;
    }

    private synchronized String waitForAnswer(String expected) {
        String line = null;
        if (expected != null) {
            try {
                while ((line = mplayerOutErr.readLine()) != null) {
                    if (line.startsWith(expected)) {
                        return line;
                    }
                }
            } catch (IOException e) {
            }
        }
        return line;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                //new MPlayerGUI("/Users/falloutboy/Desktop/smile.flv");
                new MPlayerGUI("mms://192.168.24.51:7144/stream/209DB6B57B98B53CED4AAC28BD0B79C2.wmv");
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JProgressBar jProgressBar1;
    private javax.swing.JButton pauseButton;
    private javax.swing.JButton playButton;
    private javax.swing.JSlider volumeSlider;
    // End of variables declaration//GEN-END:variables
}
