package com.softgraf.chat.servidor;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.LinkedHashSet;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

import com.softgraf.chat.Mensagem;


public class ChatServidor extends JFrame {

	private static final long serialVersionUID = -6325691950183167940L;
	
	// variáveis estáticas da classe
	private static final Color COR_AREA_MENSAGEM, COR_AREA_USUARIOS;
	private static ServerSocket servidor;
	private static String idServidor;     // nome + " " + IP
	private static LinkedHashSet<String> listaUsuariosOnline;
	private static JTextArea areaMensagem, areaUsuarios;
	
	// variáveis não estáticas
	private Socket cliente;
	private ObjectInputStream entrada;
	private ObjectOutputStream saida;
	private Mensagem msgEntrada, msgSaida;
	private String idUsuario, nome, msg;  // idUsuario = nome + " " + IP
	
	// gerencia a comunicação com o cliente até a desconexão
	private Thread gerenciador;

	
	// inicializador estático
	static {
		COR_AREA_MENSAGEM = new Color(230, 230, 230);
		COR_AREA_USUARIOS = new Color(200, 200, 200);
		listaUsuariosOnline = new LinkedHashSet<String>();
	}
	
	// construtor
	public ChatServidor() {
		inicializaInterface();
		iniciaComunicacaoClientes();
	}

	// cria a tela do programa
	private void inicializaInterface() {
		setSize(800, 600);
		setLocationRelativeTo(null);
		setDefaultCloseOperation(EXIT_ON_CLOSE);

		areaMensagem = new JTextArea("Chat iniciado.");
		areaMensagem.setBackground(COR_AREA_MENSAGEM);
		areaMensagem.setBorder(BorderFactory.createLoweredBevelBorder());
		areaMensagem.setEditable(false);
		areaMensagem.setHighlighter(null);

		areaUsuarios = new JTextArea();
		areaUsuarios.setBackground(COR_AREA_USUARIOS);
		areaUsuarios.setBorder(BorderFactory.createLoweredBevelBorder());
		areaUsuarios.setPreferredSize(new Dimension(300, areaUsuarios.getHeight()));
		areaUsuarios.setEditable(false);
		areaUsuarios.setHighlighter(null);

		getContentPane().add(new JScrollPane(areaMensagem), BorderLayout.CENTER);
		getContentPane().add(areaUsuarios, BorderLayout.EAST);

		mostraUsuariosOnline();
		setVisible(true);
	}

	// cria a conexão do servidor e as Threads dos clientes
	private void iniciaComunicacaoClientes() {

		try {
			// a porta deve ser acima de 1024 (até 65535)
			servidor = new ServerSocket(7001); // porta 7001
			idServidor = "<<SERVIDOR>> " + InetAddress.getLocalHost().getHostAddress();
			setTitle("Chat Servidor - " + idServidor);

			// servidor fica rodando "para sempre"
			while (true) {
				
				// recebe solicitacao do cliente
				cliente = servidor.accept();
				
				// abre canal para receber dados
				entrada = new ObjectInputStream(cliente.getInputStream());
				
				// abre canal para enviar dados
				saida = new ObjectOutputStream(cliente.getOutputStream());
				
				// recebe objeto Mensagem enviado pelo cliente
				msgEntrada = (Mensagem) entrada.readObject();
				idUsuario = msgEntrada.getIDusuario();  // dados do usuário
				nome = msgEntrada.getNomeUsario();
				msg = msgEntrada.getMensagem();         // String mensagem
				
				// adiciona usuario na lista de usuarios online do servidor
				addUsuarioOnline(idUsuario);
				
				// mostra informações do cliente na area de mensagens
				anexaMensagem("\n\n" + idUsuario + " conectou. Mensagem: " + msg);
				
				// mostra usuários online na área de usuários
				mostraUsuariosOnline();
				
				// cria objeto Mensagem para ser enviado ao cliente
				msgSaida = new Mensagem(idServidor, Mensagem.CONECTAR, "Bem vindo ao chat " + nome + "!");
				
				// envia objeto Mensagem ao cliente 
				saida.writeObject(msgSaida);
				
				// força o envio do objeto (esvazia buffer de saida)
				saida.flush();
				
				// cria uma thread para gerenciar a comunicação com o cliente
				gerenciador = new GerenciadorCliente(cliente, entrada, saida, idServidor);
				
				// inicia comunicação com o cliente e  depois volta ao inicio do loop -> servidor.accept()
				gerenciador.start();
			}

		} catch (UnknownHostException e) {
			JOptionPane.showMessageDialog(null, e.getMessage(), "ChatServidor Erro Fatal 1", JOptionPane.ERROR_MESSAGE);
			
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null, e.getMessage(), "ChatServidor Erro Fatal 2", JOptionPane.ERROR_MESSAGE);
			
		} catch(ClassNotFoundException e){
			JOptionPane.showMessageDialog(null, e.getMessage(), "ChatServidor Erro Fatal 3", JOptionPane.ERROR_MESSAGE);
		}

	}
	
	
	
	protected synchronized static void anexaMensagem(String msg){ 
		areaMensagem.append(msg);
	}
	
	
	// exibe todos os usuários online em areaUsuarios
	protected synchronized static void mostraUsuariosOnline() {
		Iterator<String> cursor = listaUsuariosOnline.iterator();
		areaUsuarios.setText("  ============== Usuários online ============");
		while (cursor.hasNext()) {
			areaUsuarios.append("\n\n" + (String) cursor.next());
		}
	}
	
	// retorna a lista de usuários online
	protected synchronized static LinkedHashSet<String> getUsuariosOnline(){
		return listaUsuariosOnline;
	}
	
	// adiciona um usuario na lista de usuarios online
	protected synchronized static void addUsuarioOnline(String idUsuario){
		listaUsuariosOnline.add(idUsuario);
	}
	
	// remove um usuario da lista de usuarios online
	protected synchronized static void removeUsuarioOnline(String idUsuario){
		listaUsuariosOnline.remove(idUsuario);
	}
	
	public static void main(String[] args) {
		new ChatServidor();
	}
}
