package edu.unl.consystlab.sudoku;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.List;
import edu.unl.consystlab.ftp.FTPClient;
import edu.unl.consystlab.ftp.FTPException;
import edu.unl.consystlab.gui.GameProgressBar;
import edu.unl.consystlab.gui.Dialogs;

// import klaas.util.Ftp;

public class SuFrame extends BFrame implements Game, ActionListener
{
	// statics 	
	private final static String INFO=
		"All kinds of informational messages go here.\n" +
		"Drag the slider (to the right) all way up, to speed" +
		"things up.\nHave fun!\n";
		
	// graphical components to remember
	private JTextArea info;
	private JPanel buttonPanel;
	private JPanel textFieldPanel;
	private JButton buttonSubmit;
	private JComboBox suTypeList;
	private JTextField sourceField, lowerField, upperField;
	private JTextField levelField;
	private JLabel label1, label2, label3, label4, label5;
	private JSlider speedSlider;
	private GameProgressBar gameProgressBar;
	
	// game info
	//private Grid grid;
	private VisualGrid grid;
	private List puzzles;	
	
	// Solver stuff
	private Solver solver;
	private int optimization = OptimizationMenu.DEFAULT;
	
	public SuFrame(String title)
	{
		super(title);
		
		// initialize using first puzzle available
		this.initGame();
		
		// init gui
		this.mkGui();
		info.addKeyListener(grid);
		
		// show it
		this.setSize(600,600);
		this.show();
	}
	
	public Griddable getGrid()
	{
		return grid;
	} 
	
	//public void setGrid(Grid grid)
	//{
	//this.grid = grid;
	//	grid.setGrid(grid);
	//}
	
	private void initGame()
	{
		// create the grid & the visual
		gameProgressBar = new GameProgressBar(this,JProgressBar.VERTICAL);
		grid = new VisualGrid(new Grid(), gameProgressBar);
		this.addKeyListener(grid);
		
		// use the first puzzle available		
		puzzles = Puzzles.getPuzzles();
		Puzzle puzzle = (Puzzle)puzzles.get(0);
		puzzle.fillGrid(grid);
	}
	
	private JPanel newButtonPanel()
	{
		JPanel panel = new JPanel();
		//panel.setLayout(new GridLayout(-1, 4));
		
	   	buttonSubmit = new JButton("Submit");
		buttonSubmit.setToolTipText("Submit to start the CSP solver");
		buttonSubmit.addActionListener(this);
		panel.add(buttonSubmit);
		
		return panel;
	} // newButtonPanel();
	
	
	private JPanel newRightPanel() {
		JPanel panel = new JPanel();
		//panel.setLayout(new GridLayout(-1, 4));
		
	    String[] typeStrings = { "base", "dodeka", "monster", "diagonal", "extra regions", "even-odd", "big-small", "even-odd", "geometry", "magic", "samurai" };
		suTypeList = new JComboBox(typeStrings);
		suTypeList.setSelectedIndex(1);
		suTypeList.addActionListener(this);
		panel.add(suTypeList);
		
		sourceField = new JTextField(20);
		sourceField.addActionListener(this);
		panel.add(sourceField);
		
		lowerField = new JTextField(10);
		lowerField.setText("0");
		lowerField.addActionListener(this);
		
		upperField = new JTextField(10);
		upperField.setText("5");
		upperField.addActionListener(this);
		
		textFieldPanel = new JPanel();
		textFieldPanel.setLayout(new GridLayout(1, 0));
		textFieldPanel.add(lowerField);
		textFieldPanel.add(upperField);
		panel.add(textFieldPanel);
		
	    levelField = new JTextField(10);
	    levelField.setText("1");
	    levelField.addActionListener(this);
	    panel.add(levelField);
	    		
		label1 = new JLabel("Sudoku Type");
		//Set the position of the text, relative to the icon:
        label1.setLabelFor(suTypeList);
		//label1.setVerticalTextPosition(JLabel.BOTTOM);
        //label1.setHorizontalTextPosition(JLabel.CENTER);
        
        label2 = new JLabel("Sudoku Source");		
        label2.setLabelFor(sourceField);
        
        label3 = new JLabel("Sudoku Difficulty Level");		
        label3.setLabelFor(textFieldPanel);
        
        label4 = new JLabel("This sudoku's difficulty level");		
        label4.setLabelFor(levelField);
        
        //label5 = new JLabel("Sudoku Source");		
        //label5.setLabelFor(sourceField);
		
		return panel;
	}

	public void mkGui()
	{
		this.setJMenuBar(new SuMenuBar(this));
		Container content = this.getContentPane();
		/*
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
			// close anon inner class
		});
		*/

		content.setLayout(new BorderLayout());

		// North: info 
		info = new JTextArea(6, 20);
		inform(INFO);
		
		info.setAutoscrolls(true);
		info.setEditable(false);
		info.setToolTipText(
			"All kinds of informational messages can be found here");
		content.add("North", new JScrollPane(info));

		// South: JButton area
		content.add("South", newButtonPanel());	

		// Center holds displayable field
		content.add(grid, BorderLayout.CENTER);

		// East: the speed JSlider
		speedSlider = new JSlider(JSlider.VERTICAL,0,100,50);
		speedSlider.setToolTipText("Configure the speed of the solving process");
		content.add(speedSlider, BorderLayout.EAST);
		
		// West: the progressBar
		content.add(gameProgressBar, BorderLayout.WEST);
		
		// Resize the frame.
		content.setSize(600, 600);
	} // mkGui()

	public int getOptimization()
	{
		return optimization;
	} // getOptimization()
	
	public void actionPerformed(ActionEvent event)
	{
		Object o = event.getSource();
		if(o == buttonSubmit)
		{
			this.solve();

		}	
//		if(o == buttonStop)
//		{
//			this.stop();
//
//		}		
//		if(o == buttonQuit)
//		{
//			//Debug.debug("SuFrame(): quitting");
//			this.quit();
//		}	
//		if(o == buttonClear)
//		{
//			this.clear();
//		}	
//		if(o == buttonLock)
//		{
//			this.lockToggle();
//		}	
	}// actionPerformed
	
	public void solve()
	{
		stop();
		grid.setShowSelectedCell(false);
		solver = new Solver(grid,this);
		
		// make sure the initialization is seen:
		grid.setUpdateLife(true);
		grid.setShowSelectedCell(false);
		solver.initialize();
		
		// fix optimization- get is from the menu,
		// tell the solver and the visualizer
		solver.setOptimization(getOptimization());

		if(getOptimization()==OptimizationMenu.WITHXTRA)
			grid.setUpdateLife(false);
		else
			grid.setUpdateLife(true);
		solver.start();
	}
	
	public void stop()
	{
		//Debug.debug("SuFrame(): stop solving");
		if(solver!=null) 
		{
			solver.stop();
			solver=null;
		} 
		//grid.setShowSelectedCell(false);
	}
	
	public void solverDone(boolean ok)
	{
		grid.setUpdateLife(true); grid.calc();
		//grid.setShowSelectedCell(true);
		solver=null;
		if(ok)
			inform("Solved!");
		else
			inform("Could not solve.");
	}
	
	public void clear()
	{
		inform("Clearing the playing field.");
		grid.clear();
		repaint();
	}
	
	public void inform(String msg)
	{
		info.append(msg+"\n");
		info.setCaretPosition(info.getText().length());
	}
	
	public int getSpeedPct()
	{
		return 100-speedSlider.getValue();
	}

	/* (non-Javadoc)
	 * @see klaas.sudoku.Game#setOptimization(int)
	 */
	public void setOptimization(int level)
	{
		this.optimization = level;		
		if(solver!=null)
			solver.setOptimization(level); // TODO
	}
	
	public void quit()
	{
		//stopThread();
		this.setSize(100,100);
		this.setVisible(false);
		this.setEnabled(false);
		this.dispose();
		// frame=null;
		// System.exit(0);
	}
	
	public int getProgressPct()
	{
		if(grid!=null)
			return grid.getProgressPct();
		else
			return 0;
	}

	public void lockToggle()
	{
		inform("Locking/unlocking current cells.");
		grid.setShowSelectedCell(false);
		grid.lockToggle();
	}
	
	public String secureFileName(String s)
	{
		if(s==null) return null;
		StringBuffer res = new StringBuffer();
		s=s.trim();
		for(int i=0;i<s.length();i++)
		{
			char c = s.charAt(i);
			if(c>='A' && c<='Z') { c = (char)(c - 'A' + 'a'); } 
			if(c>='a' && c<='z') res.append(c);
			if(c=='_' || c=='-' || c=='+' || c=='@') res.append(c);
			if(c>='0' && c<='9') res.append(c);
		}
		return res.toString();
	}
	
	public void save()
	{
		String name = Dialogs.input(this,"Saving Puzzle",	
			"Enter a unique name to store the puzzle as.\n" +
			"You can use a-z, A-Z, numbers\n" +
			"and some special characters.\n");
		if(name==null) return; // cancel??
		String orig = name;
		name=secureFileName(name);		
		if(name.length()==0)
		{
			Dialogs.error(this, "Invalid Puzzle Name",
				"Use a-z, A-Z, 0-9 for the puzzle name");
			return;
		}
		if(!orig.equals(name))
		{
			Dialogs.info(this,
				"Puzzle Name Simplified by Solver",
				"NOTICE: the puzzle name is changed to: \n\n"+name+"\n\n");
		}
		inform("Puzzle will be saved as '"+name+"'.");
		inform("IMPORTANT: please write this down for later.");
		
		try 
		{
			FTPClient ftp = new FTPClient("sudoku.klaas.nl");
			ftp.login("ftp", "sudoku applet");			
			BufferedWriter bw = ftp.putStart("/pub/sudoku/"+name);
			grid.save(name,bw);
			ftp.putEnd(bw);
			Dialogs.info(this,"Saving Successfull",
				"Just saved your puzzle as '"+name+"'");
		}
		catch(IOException e)
		{
			inform("IOError saving: "+e);
		}
		catch(FTPException e)
		{
			//inform("FTP Error saving: "+e);
			Dialogs.error(this, "File not Found", 
						"Sorry - cannot store puzzle '"+name+"'");
		}
	} // save()
	
	public void load()
	{
		String name = Dialogs.input(this, "Loading Puzzle",
			"Enter your puzzle name");
		String orig = name;
		name=secureFileName(name);
		if(!orig.equals(name))
		{
			Dialogs.info(this,
				"Puzzle Name Simplified by Solver",
				"NOTICE: before even trying to load the puzzle,\n"+
				"the puzzle name is changed to: \n\n"+name+"\n\n");
		}
		if(name==null) return; // cancel??
		if(name.length()==0)
		{
			Dialogs.error(this, "Invalid Puzzle Name", 
						"Use a-z, A-Z, 0-9 for the puzzle name");
			return;
		}
		inform("Puzzle will be loaded from '"+name+"'...");
		
		try 
		{
			FTPClient ftp = new FTPClient("sudoku.klaas.nl");
			ftp.login("ftp", "sudoku applet");			
			BufferedReader br = ftp.getStart("/pub/sudoku/"+name);
			grid.load(name,br);
			ftp.getEnd(br);
			Dialogs.info(this, "Puzzle Loaded", 
						"Succesfully loaded puzzle '"+name+"'");
		}
		catch(IOException e)
		{
			inform("IOError loading: "+e);
		}
		catch(FTPException e)
		{
			//inform("FTP Error loading: "+e);
			Dialogs.error(this, "File Not Found",
						"Sorry - cannot find puzzle '"+name+"'");
		}
		
	} // load()
}
