package prolog.ui;

import java.lang.reflect.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.text.*;
import prolog.demonstration.*;
import prolog.implementation.*;
import prolog.model.*;
import prolog.syntax.*;
import prolog.treeview.*;
import prolog.util.*;

public class MainFrame
	extends
		JFrame
{
	ProFileFilter fileFilter = new ProFileFilter();
	JFileChooser openChooser = null;
	JFileChooser saveChooser = null;
	JFileChooser exportChooser = null;

	Tree tree = new Tree();
	FactDbRenderer fdbRenderer = null;
	boolean animationViewInited = false;
	AnimationView graphicalResultView = new AnimationView();
	JScrollPane viewScrollPane = new JScrollPane();
	JSplitPane splitPaneVertical = new JSplitPane();

	JTabbedPane jTabbedPane1 = new JTabbedPane();
	Console consoleOutput = new Console();
	Console consoleLog = new Console();
	Console consoleXml = new Console();

	JMenuBar jMenuBar = new JMenuBar();
	JMenu jMenuFile = new JMenu();
	JMenuItem jMenuItemOpen = new JMenuItem();
	JMenuItem jMenuItemSave = new JMenuItem();
	JMenuItem jMenuItemSaveAs = new JMenuItem();
	JMenuItem jMenuItemExport = new JMenuItem();
	JMenuItem jMenuItemExit = new JMenuItem();
	JToolBar jToolBar1 = new JToolBar();
	JButton jButtonRun = new JButton();

	Program programm = null;
	Scanner scanner = null;
	Parser parser = null;
	File selectedFile = null;

	DefaultHighlighter highlighter = new  DefaultHighlighter();
	DefaultHighlighter.DefaultHighlightPainter highlighterPainter = new DefaultHighlighter.DefaultHighlightPainter(new Color(198,198,250));
	Object highlight = null;

	JSplitPane splitPaneHorizontal = new JSplitPane();
	JScrollPane jScrollPane = new JScrollPane();
	JTextComponent sourceCodeView = new JTextArea();

	JMenu jMenuHelp = new JMenu();
	JMenuItem jMenuItemAbout = new JMenuItem();

	public MainFrame()
	{
		try
		{
			jbInit();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			System.exit( -1 );
		}

		prolog.model.SingletonFactory.getInstance().registerBuilder(
			new DemoProgramBuilder( this.tree ) );

		programm = (Program) SingletonFactory.getBuilder().createProgram();
		scanner = new Scanner( SingletonFactory.getBuilder().getSymbolTable() );
		parser = new Parser( scanner, programm, new Logger( consoleLog ) );
		programm.setLogger( new Logger( consoleOutput ) );
		Node.registerRenderer( StatementResult.class, new StatementNodeRenderer( SingletonFactory.getBuilder().getSymbolTable() ) );
	}

	private void jbInit() throws Exception
	{
		jMenuFile.setText("File");
		jMenuItemOpen.setText("Open...");
		jMenuItemOpen.setAccelerator(javax.swing.KeyStroke.getKeyStroke(79, java.awt.event.KeyEvent.CTRL_MASK, false));
		jMenuItemSave.setText("Save");
		jMenuItemSave.setAccelerator(javax.swing.KeyStroke.getKeyStroke(83, java.awt.event.KeyEvent.CTRL_MASK, false));
		jMenuItemSaveAs.setText("Save as...");
		jMenuItemExit.setText("Exit");
		jMenuItemExit.setAccelerator(javax.swing.KeyStroke.getKeyStroke(115, java.awt.event.KeyEvent.ALT_MASK, false));
		jMenuItemExport.setText("Export...");
		jMenuHelp.setText("Help");
		jMenuItemAbout.setText("About...");
		jMenuBar.add(jMenuFile);
		jMenuBar.add(jMenuHelp);
		jMenuFile.add(jMenuItemOpen);
		jMenuFile.add(jMenuItemSave);
		jMenuFile.add(jMenuItemSaveAs);
		jMenuFile.add(jMenuItemExport);
		jMenuFile.addSeparator();
		jMenuFile.add(jMenuItemExit);

		jButtonRun.setToolTipText("Starts the execution of the prolog program");
		jButtonRun.setHorizontalTextPosition(SwingConstants.CENTER);

		jToolBar1.add(jButtonRun, null);

		sourceCodeView.setFont(new java.awt.Font("Monospaced", 0, 12));
		sourceCodeView.setHighlighter(this.highlighter);

		this.setSize( new Dimension( 640, 480 ) );
		this.setDefaultCloseOperation(3);
		this.setJMenuBar(jMenuBar);
		this.setTitle("PrologIDE");

		jButtonRun.setMargin(new Insets(2, 2, 2, 2));
		jButtonRun.setText("run");

		jTabbedPane1.add(consoleOutput,  "output");
		jTabbedPane1.add(consoleLog, "parser log");
		jTabbedPane1.add(consoleXml, "parser xml");

		splitPaneVertical.setOrientation(JSplitPane.VERTICAL_SPLIT);
		splitPaneVertical.add(splitPaneHorizontal, JSplitPane.TOP);
		splitPaneVertical.add(jTabbedPane1, JSplitPane.BOTTOM);

		splitPaneHorizontal.add(jScrollPane, JSplitPane.TOP);
		splitPaneHorizontal.add(viewScrollPane, JSplitPane.BOTTOM);
		viewScrollPane.getViewport().add( graphicalResultView );
		jScrollPane.getViewport().add( sourceCodeView );
		jToolBar1.setFloatable( false );
		getContentPane().add(jToolBar1, BorderLayout.NORTH);
		getContentPane().add(splitPaneVertical, BorderLayout.CENTER);
		jMenuHelp.add(jMenuItemAbout);

		jMenuItemExport.addActionListener( new GenericActionListener( this, "onExport" ) );
		jMenuItemOpen.addActionListener( new GenericActionListener( this, "onOpen" ) );
		jMenuItemSave.addActionListener( new GenericActionListener( this, "onSave" ) );
		jMenuItemSaveAs.addActionListener( new GenericActionListener( this, "onSaveAs" ) );
		jMenuItemExit.addActionListener( new GenericActionListener( this, "onExit" ) );
		jMenuItemAbout.addActionListener( new GenericActionListener( this, "onAbout" ) );
		jButtonRun.addActionListener( new GenericActionListener( this, "onRun" ) );

		splitPaneVertical.setDividerLocation(250);
		splitPaneHorizontal.setDividerLocation(250);
	}

	public void onOpen()
	{
		if( this.openChooser == null )
		{
			openChooser = new JFileChooser();
			openChooser.setFileFilter( fileFilter );
			openChooser.setDialogTitle( "Open prolog file..." );
			openChooser.setDialogType(JFileChooser.OPEN_DIALOG);
			openChooser.setMultiSelectionEnabled(false);
			openChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		}

		if( openChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION )
		{
			selectedFile = openChooser.getSelectedFile();
			if( selectedFile != null )
			{
				if( selectedFile.getName().toLowerCase().endsWith( ".pro" ) )
					openFile();
				else
					selectedFile = null;
			}
		}
	}

	public void onSave()
	{
		if( selectedFile != null )
			saveFile();
		else
			onSaveAs();
	}

	public void onSaveAs()
	{
		if( this.saveChooser == null )
		{
			saveChooser = new JFileChooser();
			saveChooser.setFileFilter( fileFilter );
			saveChooser.setDialogTitle( "Save prolog file..." );
			saveChooser.setDialogType(JFileChooser.SAVE_DIALOG);
			saveChooser.setMultiSelectionEnabled(false);
			saveChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		}

		if( saveChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION )
		{
			selectedFile = saveChooser.getSelectedFile();
			if( selectedFile != null )
			{
				if( selectedFile.getName().toLowerCase().endsWith( ".pro" ) )
					saveFile();
				else
					selectedFile = null;
			}
		}
	}

	public void onExport()
	{
		if( exportChooser == null )
		{
			exportChooser = new JFileChooser();
			exportChooser.setDialogTitle( "Export program..." );
			exportChooser.setDialogType(JFileChooser.SAVE_DIALOG);
			exportChooser.setMultiSelectionEnabled(false);
			exportChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		}

		if( exportChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION )
		{
			File f = exportChooser.getSelectedFile();
			if( f != null )
			{
				try
				{
					FileWriter out = new FileWriter( f );
					programm.genXml( out );
					out.close();
				}
				catch( IOException ae )
				{
					ae.printStackTrace();
					JOptionPane.showMessageDialog(
						this,
						ae.getMessage(),
						"Error",
						JOptionPane.ERROR_MESSAGE );
				}
			}
		}
	}

	public void saveFile()
	{
		try
		{
			FileWriter out = new FileWriter( selectedFile );
			out.write( this.sourceCodeView.getText() );
			out.close();
		}
		catch( Exception ae )
		{
			ae.printStackTrace();
			JOptionPane.showMessageDialog(
				this,
				ae.getMessage(),
				"Error",
				JOptionPane.ERROR_MESSAGE );
		}
	}

	protected void openFile()
	{
		tree.setRoot( null );

		try
		{
			this.programm.clear();
			int len = (int) selectedFile.length();
			byte[] data = new byte[ len ];
			FileInputStream in = new FileInputStream( selectedFile );
			in.read( data );
			in.close();
			this.sourceCodeView.setText( new String( data ) );
		}
		catch( Exception ae )
		{
			ae.printStackTrace();
			JOptionPane.showMessageDialog(
				this,
				ae.getMessage(),
				"Error",
				JOptionPane.ERROR_MESSAGE );
		}
	}

	public void onRun()
	{
			tree.setRoot( null );
			if( ! animationViewInited )
			{
				fdbRenderer = new FactDbRenderer();
				fdbRenderer.setContainer( graphicalResultView );
				fdbRenderer.setDb( programm.getFacts() );
				fdbRenderer.setStatementMap( (StatementMap) programm.getStatements() );
				fdbRenderer.setSymbolTable( programm.getSymbolTable() );
				fdbRenderer.setQueryList( (QueryList)programm.getQuerys() );
				graphicalResultView.setTree( tree );
				graphicalResultView.setFactDbRenderer( fdbRenderer );
				((DefaultNodeRenderer)Node.getDefaultRenderer()).setComponent( graphicalResultView );
				animationViewInited = true;
			}

			programm.clear();
			run();
	}

	public void onAbout()
	{
		AboutDialog dlg = new AboutDialog( this );
		dlg.setSize( 500, 400 );
		dlg.setTitle( "MProlog release 1.1" );
		try
		{
			dlg.setText( prolog.resource.Resource.getSharedInstance().getTextResource( "about.txt" ));
		}
		catch( Exception ae )
		{
		}
		dlg.setVisible( true );
	}

	public void onExit()
	{
		System.exit( 0 );
	}

	public void setVisible( boolean value )
	{
		if( value )
			centerWindow();
		super.setVisible( value );
	}

	public void centerWindow()
	{
		int width = getSize().width;
		int height = getSize().height;
		Dimension ss = Toolkit.getDefaultToolkit().getScreenSize();
		setBounds(
			(ss.width - width) / 2,
			(ss.height - height) / 2,
			width,
			height  );
	}

	public void run()
	{
		try
		{
			this.scanner.reset();
			this.scanner.openDocument( this.sourceCodeView.getText() );
		}
		catch( Throwable ae)
		{
			ae.printStackTrace();
			JOptionPane.showMessageDialog(
				this,
				ae.getMessage(),
				"Error opening document",
				JOptionPane.ERROR_MESSAGE );
			finished();
			return;
		}

		try
		{
			parser.parse();
		}
		catch( Exception ae)
		{
			ae.printStackTrace();
			JOptionPane.showMessageDialog(
				this,
				ae.getMessage(),
				"Error parsing",
				JOptionPane.ERROR_MESSAGE );
			finished();
			return;
		}

		try
		{
			StringWriter tmp = new StringWriter();
			this.programm.genXml( tmp );
			tmp.close();
			this.consoleXml.printThreadSafe( tmp.toString() );
		}
		catch( IOException ae )
		{
			ae.printStackTrace();
			JOptionPane.showMessageDialog(
				this,
				ae.getMessage(),
				"Error generating xml",
				JOptionPane.ERROR_MESSAGE );
		}

		try
		{
			programm.run();
		}
		catch( Throwable ae )
		{
			ae.printStackTrace();
		}
		finished();
	}

	private void finished()
	{
		SwingUtilities.invokeLater(
			new Runnable()
			{
				public void run()
				{
					Dimension d = graphicalResultView.getPreferredSize();
					viewScrollPane.setPreferredSize( d );
					viewScrollPane.invalidate();
					viewScrollPane.doLayout();
					viewScrollPane.repaint();
				}
			});
	}

	public synchronized void charScanned( int index )
	{
		try
		{
			if( index < this.sourceCodeView.getText().length() - 2 )
			{
				if( highlight != null )
					this.highlighter.removeHighlight( highlight );
				highlight = this.highlighter.addHighlight( index, index+1, highlighterPainter );
				this.sourceCodeView.repaint();
			}
		}
		catch( Exception ae )
		{
			ae.printStackTrace();
		}
	}

	public void wordScanned( int index1, int index2 )
	{
		try
		{
			if( index2 < this.sourceCodeView.getText().length() )
			{
				if( highlight != null )
					this.highlighter.removeHighlight( highlight );
				highlight = this.highlighter.addHighlight( index1, index2, highlighterPainter );
				this.sourceCodeView.repaint();
			}
		}
		catch( Exception ae )
		{
			ae.printStackTrace();
		}
	}

	class Logger implements ILogger
	{
		protected Console console = null;

		public Logger( Console t )
		{
			this.console = t;
		}

		public void msg( String msg )
		{
			console.printlnThreadSafe( msg );
		}

		public void msg( String msg, int depth )
		{
			console.printlnThreadSafe( StringBuilder.buildTabs( depth ) + msg );
		}
	}

	class GenericActionListener
		implements ActionListener
	{
		protected Object target = null;
		protected Method targetMethod = null;

		public GenericActionListener( Object target, String methodName )
			throws NoSuchMethodException
		{
			this.target = target;
			this.targetMethod = target.getClass().getMethod( methodName, new Class[]{} );
		}

		public void actionPerformed( ActionEvent anEvent )
		{
			try
			{
				targetMethod.invoke( target, null );
			}
			catch( InvocationTargetException ae )
			{
				ae.printStackTrace();
				System.exit( -1 );
			}
			catch( IllegalAccessException ae )
			{
				ae.printStackTrace();
				System.exit( -1 );
			}
		}
	}
}
