package controllers;
import boardGenerator.Pair;
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.URI;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import javax.swing.AbstractButton;
import models.Configuration;
import models.Jugador;
import models.Partida;
import models.TopTen;
import view.*;
/**
* Clase SudokuController implementa el control del juego a partir
* de la logica (models.Partida) y a partir de la interfaz Grafica
* (view.IMainView). Implementa un Observer y un ActionListener
* Esta clase es la encargada de observar a la partida y a la vista y
* responde antes sus notificaciones de cambios (metodo update) , a su vez esta misma clase
* implementará el acionListener para los botones de las interfaces
* (metodo actionPerformed) a travez del cual ejecutara los diferentes comandos solicitados
*/
public class SudokuController implements Observer,ActionListener{
private Partida partida;
private IMainView vista;
private Configuration configuration;
private boolean pista;
private static SudokuController instance = null;
private boolean gameStarted;
private boolean vistaAlternativa;
private boolean resueltoPorSistema;
private TopTen topTen;
private Jugador player;
/**
* Obtiene la instancia unica del Controlador
* nota: la instancia debe ser creada una vez
* con createInstance()
* @return SudokuController asosiado a la partida Actual
*/
public static SudokuController getInstance(){
if (instance == null){
throw new NullPointerException();
}
return instance;
}
/**
* Crea la instancia unica del controlador
* @param modelo Modelo de Partida Sudoku
*/
public synchronized static void createInstance(Partida modelo){
if (instance == null){
instance = new SudokuController(modelo);
}
}
/**
* Constructor privado de la clase
* @param modelo Modelo partida Sudoku
*/
private SudokuController(Partida modelo) {
this.partida = modelo;
this.configuration = modelo.getConfig();
this.partida.addObserver(this);
this.partida.getCronometro().addObserver(this);
this.pista = false;
this.gameStarted = false;
this.resueltoPorSistema = false;
this.topTen = new TopTen();
}
/**
* Asosia una vista con el Controlador
* @param view vista concreta del juego
*/
public void setView(IMainView view){
this.vista = view;
this.vista.addObserver(this);
this.vistaAlternativa = false;
}
/**
* Obtiene la configuracion de la partida actual
* @return gameconfiguration
*/
public Configuration getConfiguration(){
return this.configuration;
}
/**
* Accion del Observer, cuando un elemnto observado
* cambia y notifica al observador ejecuta este metodo
* @param o clase observable
* @param arg1 argumentos de la llamada
*/
@Override
public void update(Observable o, Object arg1) {
try {
String tmp = arg1.toString();
switch (tmp) {
case "CHANGE_POINTS" : actualizarPuntuacion();break;
case "TIME_CHANGED" : actualizarTiempo();break;
case "NUMBERCOUNT_CHANGED" : actualizaContador();break;
}
}catch (Exception ex){}
try {
Pair<Pair<Integer,Integer>,Integer> tmp2 = (Pair<Pair<Integer,Integer>,Integer>) arg1;
cambiaValor(tmp2);
} catch (Exception ex2){}
}
/**
* actionPerformed del Controller como listener de botones
* @param arg ActionCommand
*/
@Override
public void actionPerformed(ActionEvent e) {
try{
String arg = ((AbstractButton)e.getSource()).getActionCommand();
if (arg.equals("CHECK_GAME")){
this.verificarJugada();
}
if (arg.equals("START_GAME")){
this.nuevoJuego();
}
if (arg.equals("SOLVE_GAME")){
this.resolver();
}
if (arg.equals("DIF_HARD")){
this.dificultadDificil();
}
if (arg.equals("DIF_MODERATE")){
this.dificultadMedia();
}
if (arg.equals("DIF_EASY")){
this.dificultadFacil();
}
if (arg.equals("GET_HINT")){
this.sugerirJugada();
}
if (arg.equals("RESET_GAME")){
this.resetTablero();
}
if (arg.equals("STOP_GAME")){
this.detenerJuego();
}
if (arg.equals("SUDOKU_WEB")){
this.irAWeb();
}
if (arg.equals("OTHER_VIEW")){
this.changeView();
}
if (arg.equals("EXIT")){
this.systemExit();
}
if (arg.equals("TOP_TEN")){
this.showTopTen();
}
if (arg.equals("CONFIG")){
this.showConfig();
}
if (arg.equals("RULES")){
this.showRules();
}
} catch (Exception ex){
System.out.println("ERROR: Mapear AbstractButton a Listener SudokuController");
ex.printStackTrace();
}
}
/**
* Metodos Privados de la Clase
*/
/**
* Inicia un nuevo Juego
*/
private void nuevoJuego(){
if (this.gameStarted){
vista.showWarningMessage("Detener Partida Actual");
return;
}
vista.habilitarTablero();
partida.nuevaPartida();
this.actualizarMatriz();
vista.habilitarControles(true);
this.gameStarted = true;
this.pista = false;
}
/**
* Reinicia el tablero con el problema original
* con la puntuacion y tiempo actual
*/
private void resetTablero(){
if (!this.gameStarted){
vista.showWarningMessage("Partida No Inciada");
return;
}
if (vista.showConfirmationMessage("¿Reiniciar Tablero?")){
this.partida.resetBoard();
vista.setMatriz(partida.getTablero());
}
}
/**
* Cambia Dificulta Facil
*/
private void dificultadFacil() {
configuration.setDifficulty("EASY");
}
/**
* Cambia Dificulta Media
*/
private void dificultadMedia() {
configuration.setDifficulty("MODERATE");
}
/**
* Cambia Dificulta Dificil
*/
private void dificultadDificil() {
configuration.setDifficulty("HARD");
}
/**
* Cambia el valor de una celda en la matriz del juego
* Si es una pista lo que se pasa como parametro tambien
* actualiza la vista grafica
* @param celda
*/
private void cambiaValor(Pair<Pair<Integer,Integer>,Integer> celda){
if (pista){
vista.setPista(celda.getFirst().getFirst(),celda.getFirst().getSecond(),celda.getSecond());
}
if (celda.getSecond() == 0){
partida.BorrarValor(celda.getFirst().getFirst(),celda.getFirst().getSecond());
} else {
partida.InsertarValor(celda.getFirst().getFirst(),celda.getFirst().getSecond(),celda.getSecond());
}
}
/**
* Permite Obtener una pista y mostrarla por pantalla
*/
private void sugerirJugada(){
if (!this.gameStarted){
vista.showWarningMessage("Partida No Inciada");
return;
}
this.pista = true;
partida.sugerirJugada();
this.pista = false;
}
/**
* Verifica si el juego actual es correcto
* @return true/false:correcto,incorrecto
*/
private boolean verificarJugada() {
if (!this.gameStarted){
vista.showWarningMessage("Partida No Inciada");
return false;
}
List<Pair<Integer,Integer>> lista = partida.verificarJugada();
for (Pair<Integer, Integer> pair : lista) {
vista.setWrong(pair.getFirst(),pair.getSecond());
}
return lista.isEmpty();
}
/**
* Permite detener la partida actual
* siempre y cuando el juego finalize
* correctamente, agregar la puntuacion
* actual al topten de jugadas
*/
private void detener(){
if (!this.gameStarted) return;
if (verificarJugada()){
partida.getCronometro().pararTiempo();
vista.habilitarControles(false);
vista.deshabilitarTablero();
this.gameStarted = false;
if (!resueltoPorSistema){
vista.showInfoMessage("Felicitaciones a finalizado el juego Correctamente");
configuration.setNamePlayer(vista.getPlayerName());
player = new Jugador(configuration,partida);
topTen.addJugada(player);
}
this.resueltoPorSistema = false;
}
}
/**
* Detiene el juego actual en cualquier momento dado
*/
private void detenerJuego(){
if (!this.gameStarted){
vista.showWarningMessage("Partida No Inciada");
return;
}
if (vista.showConfirmationMessage("¿Desea Terminar la Partida Actual?")){
partida.getCronometro().pararTiempo();
vista.deshabilitarTablero();
vista.habilitarControles(false);
this.gameStarted = false;
}
}
/**
* Resuelve la partida actual en la logica
*/
private void resolver() {
if (!this.gameStarted){
vista.showWarningMessage("Partida No Inciada");
return;
}
if (vista.showConfirmationMessage("¿Desea Resolver Partida Actual?")){
this.resueltoPorSistema = true;
partida.resolver();
this.actualizarMatriz();
}
}
/**
* Mete la matriz de la partida en la vista actual
*/
private void actualizarMatriz(){
vista.setMatriz(partida.getTablero());
}
/**
* Actualiza el contador de numeros restantes
*/
private void actualizaContador(){
vista.refreshContador(partida.getCantNumbersToPut()+"");
if (partida.getCantNumbersToPut() == 0){
this.detener();
}
}
/**
* Actualiza el contador de puntuacion
*/
private void actualizarPuntuacion(){
vista.refreshPuntuacion(partida.getPuntuacion()+"");
}
/**
* Actualiza el contador de tiempo transcurrido
*/
private void actualizarTiempo(){
vista.refreshTime(partida.getTiempoPartida());
partida.calcularPuntuacion();
}
/**
* Cambia de Vista
*/
private void changeView(){
if (this.gameStarted) {
vista.showWarningMessage("Primero detener Partida Actual");
return;
}
if (!vistaAlternativa){
IMainView tmp = new AlternativeView();
tmp.addObserver(this);
configuration.setNamePlayer(vista.getPlayerName());
vista.dispose();
vista = tmp;
vistaAlternativa = true;
} else {
IMainView tmp = new MainView();
tmp.addObserver(this);
tmp.setPlayerName(configuration.getNamePlayer());
vista.dispose();
vista = tmp;
vistaAlternativa = false;
}
}
/**
* Finaliza el Programa
*/
private void systemExit() {
if (vista.showConfirmationMessage("Salir de Sudoku?")){
System.exit(0);
}
}
/**
* Muestra la ventana top-10 y evita la interaccion
* con el tablero hasta que se cierre dicha ventana
*/
private void showTopTen(){
if (this.gameStarted) {
vista.showWarningMessage("Primero detener Partida Actual");
return;
}
vista.disable(true);
TopTenView tt = new TopTenView(topTen);
tt.addWindowListener(new WindowAdapter(){
public void windowClosed(WindowEvent e){
vista.disable(false);
vista.requestFocus();
}
});
}
/**
* Muestra el panel de configuracion y obtiene
* los datos ingresados por el usuario
*/
private void showConfig(){
if (this.gameStarted) {
vista.showWarningMessage("Primero detener Partida Actual");
return;
}
vista.disable(true);
ConfigView tt = new ConfigView(configuration);
tt.addWindowListener(new WindowAdapter(){
public void windowClosed(WindowEvent e){
vista.disable(false);
vista.requestFocus();
}
});
}
/**
* Muesta la ventana de Reglas
*/
private void showRules(){
if (this.gameStarted) {
vista.showWarningMessage("Primero detener Partida Actual");
return;
}
vista.disable(true);
RulesView rr = new RulesView();
rr.addWindowListener(new WindowAdapter(){
public void windowClosed(WindowEvent e){
vista.disable(false);
vista.requestFocus();
}
});
}
/**
* Abre un navegador web con pagina de sudoku
*/
private void irAWeb() {
try {
Desktop.getDesktop().browse(new URI("http://www.sudoku-solutions.com"));
} catch (Exception ex) {
System.out.println("No se puede abrir navegador");
}
}
}