/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package webcamstudio.media.renderer;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.logging.Level;
import java.util.logging.Logger;
import webcamstudio.mixers.AudioBuffer;
import webcamstudio.mixers.Frame;
import webcamstudio.mixers.ImageBuffer;
import webcamstudio.mixers.MasterMixer;
import webcamstudio.streams.Stream;
import webcamstudio.util.Tools;

/**
 *
 * @author patrick (modified by karl)
 */
public class Exporter implements MasterMixer.SinkListener {
    static Listener listenerEx = null;

    public static void setListenerEx(Listener l) {
        listenerEx = l;
    }

    private boolean cancel = false;
    private ServerSocket videoServer = null;
    private ServerSocket audioServer = null;
    private DataOutputStream videoOutput;    
    private DataOutputStream audioOutput;
    private int aport = 0;
    private int vport = 0;
    private ImageBuffer imageBuffer = null;
    private AudioBuffer audioBuffer = null;
    private long vCounter = 0;
    private long aCounter = 0;
    private Stream stream = null;
    private Socket vConnection = null;
    private Socket aConnection = null;
    @SuppressWarnings("SocketException")
    public Exporter(Stream s) throws SocketException {
        this.stream = s;
        imageBuffer = new ImageBuffer(MasterMixer.getInstance().getWidth(),MasterMixer.getInstance().getHeight());
        audioBuffer = new AudioBuffer(MasterMixer.getInstance().getRate());
        if (!stream.isOnlyAudio()) { // stream.hasVideo()
            try {
                videoServer = new ServerSocket(0);
                videoServer.setReceiveBufferSize(32768);
                vport = videoServer.getLocalPort();
            } catch (IOException ex) {
                Logger.getLogger(Exporter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if (stream.hasAudio()) {
            try {
                audioServer = new ServerSocket(0);
                audioServer.setReceiveBufferSize(32768);
                aport = audioServer.getLocalPort();
            } catch (IOException ex) {
                Logger.getLogger(Exporter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
//        System.out.println("Port used is Video:" + vport+"/Audio:" + aport);
        if (!stream.isOnlyAudio()) { // stream.hasVideo()
            Thread vExCapture = new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        vConnection = videoServer.accept();
                        System.out.println("Video output accepted");
                        videoOutput = new DataOutputStream(new BufferedOutputStream(vConnection.getOutputStream(), 4096));
                        imageBuffer.clear();
                        while (!cancel) {
                            byte[] videoData = imageBuffer.pop().getBytes();
                            if (videoData == null || videoOutput == null) {
                                Tools.sleep(30);
                            } else {
                                videoOutput.write(videoData);                            
                                videoOutput.flush();
                                vCounter++;
                            }
                        } 
                    } catch (IOException ex) {
                        cancel = true;
                        if (imageBuffer != null){
                            imageBuffer.abort();
                        }
                        stream.stop();                   
                        stream.updateStatus();
                        listenerEx.resetFMECount();
                        Logger.getLogger(Exporter.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    System.out.println("Video output stopped");
                }
            });
            vExCapture.setPriority(Thread.MIN_PRIORITY);
            vExCapture.start();
        }
        if (stream.hasAudio()) { //stream.hasVideo()
            Thread aExCapture = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    try {                    
                        aConnection = audioServer.accept();
                        System.out.println("Audio output accepted");
                        audioOutput = new DataOutputStream(new BufferedOutputStream(aConnection.getOutputStream(), 4096));
                        audioBuffer.clear();
                        while (!cancel) {
                            byte[] audioData = audioBuffer.pop();
                            if (audioData == null || audioOutput==null) {
                                Tools.sleep(30);
                            } else {
                                audioOutput.write(audioData);
                                if (audioOutput != null) {
                                    audioOutput.flush();
                                }
                                aCounter++;
                            }                        
                        }

                    } catch (IOException ex) {
                        cancel = true;
                        if (audioBuffer != null){
                            audioBuffer.abort();
                        }
                        stream.stop();
                        stream.updateStatus();
//                        Logger.getLogger(Exporter.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    System.out.println("Audio output stopped");
                }
            });
            aExCapture.setPriority(Thread.MIN_PRIORITY);
            aExCapture.start();
        }
        cancel = false;
        MasterMixer.getInstance().register(this);
    }
    
    public void abort() {
        cancel = true;
        System.out.println("Output aborted...");
        MasterMixer.getInstance().unregister(this);
        if (stream.hasVideo()) {
            if (imageBuffer != null) {
                imageBuffer.abort();
                imageBuffer = null;
//            System.out.println("imageBuffer Aborted!");
            }
        }
        if (stream.hasAudio()) {
            audioBuffer.abort();
            audioBuffer = null;
//            System.out.println("audioBuffer Aborted!");
        }
        System.out.println("V: " +vCounter);
        System.out.println("A: " +aCounter);
        if (videoServer != null && stream.hasVideo()) {
            try {
                videoServer.close();
                if (videoServer!=null){
                    videoOutput.close();
                    videoOutput = null;
                    videoServer = null;
    //                System.out.println("Video Exporter Aborted!");
                }
            } catch (IOException ex) {
                Logger.getLogger(Exporter.class.getName()).log(Level.SEVERE, null, ex);
            }   
        }
        if (audioServer != null && stream.hasAudio()) {
            try {
                audioServer.close();
                if (audioOutput!=null){
                    audioOutput.close();
                    audioOutput = null;
                    audioServer = null;
//                    System.out.println("Audio Exporter Aborted!");
                }
            } catch (IOException ex) {
                Logger.getLogger(Exporter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public int getAudioPort() {
        return aport;
    }

    public int getVideoPort() {
        return vport;
    }
    
    @Override
    public void newFrame(Frame frame) {
            if (stream.hasVideo()){
                imageBuffer.push(frame.getImage());
            }
            if (stream.hasAudio()){
                audioBuffer.push(frame.getAudioData());
            }
        }

    public interface Listener {

        public void resetFMECount();
    }
}
