package com.videonext.mplayer;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Date;
import java.util.LinkedList;
import java.util.Map;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;

import com.videonext.mplayer.api.IControllerListener;
import com.videonext.mplayer.api.IPlayer;
import com.videonext.mplayer.api.IPlayerController;
import com.videonext.mplayer.api.events.BufferChangedEvent;
import com.videonext.mplayer.api.events.ChangeStateEvent;
import com.videonext.mplayer.api.events.NewFrameEvent;
import com.videonext.mplayer.api.events.UIEvent;
import com.videonext.mplayer.internal.MediaPlayer;
import com.videonext.mplayer.internal.MediaPlayerListener;
import com.videonext.mplayer.internal.ScalablePane;

public class Player extends JPanel implements IPlayer  {

	private static final long serialVersionUID = 1L;
	private MediaPlayer impl;
	private LinkedList<IControllerListener> listeners = new LinkedList<IControllerListener>();
	private Object id;
	private Map<String, Object> config;
	private JPanel previewPanel;
	private byte[] stillImage;
	private JLabel statusLabel;
	private JLabel statusIcon;
	
	
	public Player(Object id, Map<String, Object> config)
	{
		this.setBackground(new Color(0, 0, 0));
		this.id     = id;
		this.config = config;
		
		try {
			impl = new MediaPlayer(id.toString());
			//impl.setEnableLogging(false);
			impl.setCacheSize(0);
			impl.setBackground(Color.BLACK);
			impl.setMediaPlayerListener(new MediaPlayerListenerImpl());
			if (config != null) {
				for (Map.Entry<String, Object> entry : this.config.entrySet()) {
					impl.setParameter(entry.getKey(), entry.getValue().toString());				 
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		this.setLayout(new BorderLayout());
		impl.setBounds(new Rectangle(0, 0, 300, 200)); // important!
	}
	
	private void updateOverlay(final boolean show) {
//		if (!SwingUtilities.isEventDispatchThread()) {
//		     SwingUtilities.invokeLater(new Runnable() {
//		       @Override
//		       public void run() {
//		          updateOverlay(show);
//		       }
//		     });
//		
//		     return;
//		}
//		
		synchronized (this) {
			if (show)
		       	  setupStatusOverlay();
		        else if (previewPanel != null)
		       	  removeStatusOverlay();			
		}
		
	}
	
	private void setupStatusOverlay() {
		synchronized (this) {
		Image image = null;

		previewPanel = new JPanel();
		previewPanel.setBackground(Color.DARK_GRAY);
		previewPanel.setLayout(new BorderLayout());
		
		JComponent contentPanel = previewPanel;
		this.removeAll();
		this.add(previewPanel, BorderLayout.CENTER);
		//previewPanel.setBounds(this.getBounds());
		
		if (stillImage != null) {
	
			image = Toolkit.getDefaultToolkit().createImage(stillImage);		

			// hack, to fill image height/width
			JLabel picLabel = new JLabel();			
			picLabel.setIcon(new ImageIcon(image));
			
			ScalablePane sp = new ScalablePane(image);
			sp.setBackground(Color.DARK_GRAY);
			previewPanel.add(sp, BorderLayout.CENTER);
			//sp.setBounds(previewPanel.getBounds());
			contentPanel = sp;
		}
		
		BoxLayout layoutMgr = new BoxLayout(contentPanel, BoxLayout.X_AXIS);
		contentPanel.setLayout(layoutMgr);

	    ClassLoader cldr = this.getClass().getClassLoader();
	    java.net.URL imageURL   = cldr.getResource("img/spinner_white.gif");
	    ImageIcon imageIcon = new ImageIcon(imageURL);
	    statusIcon = new JLabel();
	    statusIcon.setIcon(imageIcon);
	    imageIcon.setImageObserver(statusIcon);

	    statusLabel = new JLabel("  " + "Connecting...");
	    statusLabel.setForeground(Color.WHITE);
	    
	    contentPanel.add(Box.createHorizontalGlue());
	    contentPanel.add(statusIcon);
	    contentPanel.add(statusLabel);
	    contentPanel.add(Box.createHorizontalGlue());
	    
	    this.revalidate();
		}
	    
	}
	
	
	private void removeStatusOverlay() {
		synchronized (this) {
		this.removeAll();
		this.add(impl, BorderLayout.CENTER);
		impl.setBounds(this.getBounds()); // important!
		this.invalidate();
		this.repaint();
		previewPanel = null;
		}
	}
	
	
	@Override
	public void open(String url) {	
		synchronized (this) {
		updateOverlay(true);
		
		impl.setParameter("videoURL", url);
		impl.sendCommand("open");  
		}
	}

	@Override
	public void play(int direction) {
		synchronized (this) {
		impl.setParameter("direction", Integer.toString(direction));	
		impl.sendCommand("play");
		}
	}

	@Override
	public void pause() {
		synchronized (this) {
		impl.sendCommand("pause");
		}
	}
	
	@Override
	public void stop() {
		synchronized (this) {
		impl.sendCommand("stop");
		}
	}

	@Override
	public void close() {
		synchronized (this) {
		this.removeAll();
		impl.sendCommand("stop");
		}
	}

	@Override
	public Container getOverlay() {		
		return impl.getOverlay();
	}

	@Override
	public int getState() {
		return impl.getStatus();
	}


	@Override
	public void addControllerListener(IControllerListener listener) {
		synchronized (listeners) {
			listeners.add(listener);
		}		
	}

	@Override
	public void removeControllerListener(IControllerListener listener) {
		synchronized (listeners) {
			listeners.remove(listener);
		}		
	}


	@Override
	public void changeSpeed(float value) {
		synchronized (this) {
		impl.setParameter("streamSpeed", Float.toString(value));
		}
	}

	@Override
	public void changeJitterBufferLen(int value) {
		synchronized (this) {
			impl.setParameter("jitterBuffer", Integer.toString(value));
		}
		
	}

	@Override
	public void jumpTo(Date date) {
		synchronized (this) {
		impl.setParameter("jump2timestamp", Long.toString(date.getTime() / 1000));
		}
	}


	@Override
	public void setStepMode(boolean flag) {
		synchronized (this) {
		impl.setParameter("stepMode", flag ? "1" : "0");
		}
	}


	@Override
	public void nextStep(int direction) {
		// TODO Auto-generated method		
	}


	@Override
	public void setBrightness(float value) {
	}
	
	@Override
	public void setAspectRation(int value) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Object getId() {
		return id;
	}

	
	class MediaPlayerListenerImpl implements MediaPlayerListener {
		
		@Override
		public void statusChanged(int status, int resultCode, String resultMessage) {
			//System.err.println("------------statusChanged(int status) " + status);
			if (status == IPlayerController.Stopped) {
				statusLabel.setForeground(Color.RED);
				statusLabel.setText("  " + impl.getResultMsg());
				
				 ClassLoader cldr = this.getClass().getClassLoader();
				 java.net.URL imageURL   = cldr.getResource("img/alarm_red.png");
				 ImageIcon imageIcon = new ImageIcon(imageURL);
				 statusIcon.setIcon(imageIcon);
				 imageIcon.setImageObserver(statusIcon);
				
			} else if (status == IPlayerController.Opening) {
				statusLabel.setText("  " + "Openning...");
			} else if (status == IPlayerController.Buffering) {
				statusLabel.setText("  " + "Buffering...");
			}
			
			
			synchronized (listeners) {
				ChangeStateEvent event = new ChangeStateEvent(Player.this, status, resultCode, resultMessage);
				for (IControllerListener listener : listeners) {
					listener.controllerUpdate(event);
				}
			}	
		}

		@Override
		public void newFrame(long frameTimestamp, long bufStartTimestamp,
				long bufEndTimestamp, BufferedImage img) {	
			
			if (previewPanel != null)
				updateOverlay(false);
			
		
			synchronized (listeners) {
				NewFrameEvent event = new NewFrameEvent(Player.this, frameTimestamp, bufStartTimestamp, bufEndTimestamp);
				for (IControllerListener listener : listeners) {
					listener.controllerUpdate(event);
				}
			}	
			
			
		}

		@Override
		public void bufferChanged(long startTimestamp, long endTimestamp) {
			synchronized (listeners) {
				BufferChangedEvent event = new BufferChangedEvent(Player.this, startTimestamp, endTimestamp);
				for (IControllerListener listener : listeners) {
					listener.controllerUpdate(event);
				}
			}	
		}

		@Override
		public void mouseClicked(MouseEvent e, boolean isPlayerActive) {
			synchronized (listeners) {
				UIEvent event = new UIEvent(Player.this, UIEvent.MOUSE_CLICKED);
				event.setMouseEvent(e);
				for (IControllerListener listener : listeners) {
					listener.controllerUpdate(event);
				}
			}
		}

		@Override
		public void snapshotFinished() {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mouseMoved(MouseEvent e, boolean isPlayerActive) {
			//System.err.println("Player mouseMoved: " + e );
			synchronized (listeners) {
				UIEvent event = new UIEvent(Player.this, UIEvent.MOUSE_MOVED);
				event.setMouseEvent(e);
				for (IControllerListener listener : listeners) {
					listener.controllerUpdate(event);
				}
			}
			
			
		}

		@Override
		public void mouseExit(MouseEvent e, boolean isPlayerActive) {
			//System.err.println("Player mouseExit: " + e );		
			synchronized (listeners) {
				UIEvent event = new UIEvent(Player.this, UIEvent.MOUSE_EXIT);
				event.setMouseEvent(e);
				for (IControllerListener listener : listeners) {
					listener.controllerUpdate(event);
				}
			}
			
		}

		@Override
		public boolean canDisplayFrame(Date date) {
			for (IControllerListener listener : listeners) {
				if (!listener.canDisplayFrame(date))
					return false; 
			}
			
			return true;
		}
	}


	@Override
	public void setStillImage(byte[] img) {
		synchronized (this) {
		this.stillImage = img;
		}
		
	}

	@Override
	public void setMute(boolean val) {
		// TODO Auto-generated method stub
		impl.setParameter("mute", val ? "on" : "off");	
	}
	
}