/*
 *  Draco Econometrics
 *  Copyright (C) 2006-2009 Approximatrix, LLC
 *  http://www.approximatrix.com
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.approximatrix.draco.editor.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;

import com.approximatrix.draco.application.ApplicationManager;
import com.approximatrix.draco.application.ChildFrame;
import com.approximatrix.draco.application.ChildFrameRegistrar;
import com.approximatrix.draco.application.icons.VectorIcon;
import com.approximatrix.draco.editor.events.EditorFileStatusEvent;
import com.approximatrix.draco.editor.events.EditorFileStatusListener;
import com.approximatrix.draco.io.filters.PlainTextFilter;
import com.approximatrix.draco.io.filters.RubyFileFilter;
import com.approximatrix.draco.main.ui.DynamicMainMenuBar;
import com.approximatrix.draco.main.ui.DynamicToolbar;
import com.approximatrix.draco.main.ui.MainWindow;

public class RubyEditorWindow extends ChildFrame implements ActionListener {
	
	private static final String DIALOG_NAME = "Editor";
	
	private static final String CLOSE_ICON = "FILECLOSE";
	private static VectorIcon icon = null;
	
	private static final String TITLE = "Editor";
	private static final String HELP_KEY = "editor";
	
	private static int TAB_PLACEMENT = JTabbedPane.TOP;
	
	private JTabbedPane tabPane = null;
	
	private JMenuItem m_cut, m_copy, m_paste, m_undo, m_redo, m_comment;
	private JMenuItem m_new, m_save, m_saveas, m_find, m_open;
	
	private JButton saveAsButton = null;
	private JButton saveButton = null;
	private JButton commentButton = null;
	private JButton undoButton = null;
	private JButton redoButton = null;
	private JButton renameButton = null;
	private JButton findButton = null;
	
	public static RubyEditorWindow GetEditorWindow(ChildFrameRegistrar mw) {
		// Find existing editors in the main window
		List<RubyEditorWindow> editors = mw.getWindows(RubyEditorWindow.class);
		if(editors.size() == 0)
			return (RubyEditorWindow)mw.openWindow(new RubyEditorWindow());
		else
			return editors.get(0);
	}
	
	public static void OpenEditorTab(String contents, ChildFrameRegistrar mw) {
		RubyEditorWindow rew = GetEditorWindow(mw);
		rew.openTab(contents);
	}
	
	public static void OpenEditorTab(String contents, boolean isRuby, ChildFrameRegistrar mw) {
		RubyEditorWindow rew = GetEditorWindow(mw);
		rew.openTab(contents,isRuby);
	}
	
	public static void OpenEditorTab(File file, ChildFrameRegistrar mw) {
		RubyEditorWindow rew = GetEditorWindow(mw);
		rew.openTab(file);
	}
	
	public static void OpenEditorTab(String contents, String name, String mimeType, ChildFrameRegistrar mw) {
		RubyEditorWindow rew = GetEditorWindow(mw);
		rew.openTab(contents,name,mimeType);
	}
	
	public RubyEditorWindow() {
		super(DIALOG_NAME, 
				true, //resizable
				true, //closable
				true, //maximizable
				true);
		
		initialize();
	}
	
	private void initialize() {
		this.setLayout(new BorderLayout());
		
		tabPane = new JTabbedPane(TAB_PLACEMENT);
		this.add(tabPane,BorderLayout.CENTER);
	
		this.setSize(500, 350);
		super.setTitle(DIALOG_NAME);
		
	}
	
	
	
	private int getComponentIndex(Component comp) {
		for(int i=0;i<tabPane.getTabCount();i++) {
			if(tabPane.getComponentAt(i) == comp)
				return i;
		}
		return -1;
	}
	
	public void openTab(String contents) {
		RubyEditorPanel rep = new RubyEditorPanel(contents);
		tabPane.add(rep);
		tabPane.setTabComponentAt(this.getComponentIndex(rep), new TabComponent(rep));
		tabPane.setSelectedComponent(rep);
	}
	
	public void openTab(String contents, boolean isRuby) {
		RubyEditorPanel rep = new RubyEditorPanel(contents,isRuby);
		tabPane.add(rep);
		tabPane.setTabComponentAt(this.getComponentIndex(rep), new TabComponent(rep));
		tabPane.setSelectedComponent(rep);
	}
	
	public void openTab(File file) {
		RubyEditorPanel rep = new RubyEditorPanel(file);
		tabPane.add(rep);
		tabPane.setTabComponentAt(this.getComponentIndex(rep), new TabComponent(rep));
		tabPane.setSelectedComponent(rep);
	}
	
	public void openTab(String contents, String name, String mimeType) {
		RubyEditorPanel rep = new RubyEditorPanel(contents, name, mimeType);
		tabPane.add(rep);
		tabPane.setTabComponentAt(this.getComponentIndex(rep), new TabComponent(rep));
		tabPane.setSelectedComponent(rep);
	}
	
	@Override
	public String getHelpKey() {
		return HELP_KEY;
	}

	@Override
	public JMenu[] getMenuItems() {
		if(menuItems == null) {
			ArrayList<JMenu> menus = new ArrayList<JMenu>();
			
			JMenu file = DynamicMainMenuBar.GetGenericFileMenu();
			
			m_new = new JMenuItem("New Editor Tab");
			m_new.addActionListener(this);
			file.add(m_new);
			
			m_open = new JMenuItem("Open in Editor...");
			m_open.addActionListener(this);
			file.add(m_open);
			
			m_save = new JMenuItem("Save Current Editor Tab");
			m_save.addActionListener(this);
			file.add(m_save);
			
			m_saveas = new JMenuItem("Save Current Editor Tab As...");
			m_saveas.addActionListener(this);
			file.add(m_saveas);
			
			file.addSeparator();
			
			menus.add(file);
			
			JMenu edit = DynamicMainMenuBar.GetGenericEditMenu();
			
			m_undo = new JMenuItem("Undo");
			m_undo.addActionListener(this);
			edit.add(m_undo);
			
			m_redo = new JMenuItem("Redo");
			m_redo.addActionListener(this);
			edit.add(m_redo);
			
			edit.addSeparator();
			
			m_cut = DynamicMainMenuBar.GetGenericCutMenuItem();
			m_cut.addActionListener(this);
			edit.add(m_cut);
			
			m_copy = DynamicMainMenuBar.GetGenericCopyMenuItem();
			m_copy.addActionListener(this);
			edit.add(m_copy);
			
			m_paste = DynamicMainMenuBar.GetGenericPasteMenuItem();
			m_paste.addActionListener(this);
			edit.add(m_paste);
			
			edit.addSeparator();
			
			m_find = new JMenuItem("Find");
			m_find.addActionListener(this);
			edit.add(m_find);
			
			m_comment = new JMenuItem("Toggle Comment");
			m_comment.addActionListener(this);
			edit.add(m_comment);
			
			menus.add(edit);
			
			menuItems = menus.toArray(new JMenu[0]);
		}
		return menuItems;
	}

	@Override
	public Component[] getToolbarButtons() {
		if(toolbarButtons == null) {
			ArrayList<Component> buttons = new ArrayList<Component>();
			
			saveButton = new JButton();
			saveButton.setToolTipText("Save this text to a file under current filename");
			saveButton.setIcon(ApplicationManager.GetIcon("SAVE"));
			saveButton.setBackground(java.awt.Color.YELLOW);
			saveButton.addActionListener(this);
			buttons.add(saveButton);
			
			saveAsButton = new JButton();
			saveAsButton.setToolTipText("Save this text to a file");
			saveAsButton.setIcon(ApplicationManager.GetIcon("SAVEAS"));
			saveAsButton.setBackground(java.awt.Color.YELLOW);
			saveAsButton.addActionListener(this);
			buttons.add(saveAsButton);
			
			buttons.add(DynamicToolbar.GetGenericCutButton(this.getMainWindow()));
			buttons.add(DynamicToolbar.GetGenericCopyButton(this.getMainWindow()));
			buttons.add(DynamicToolbar.GetGenericPasteButton(this.getMainWindow()));
			
			undoButton = new JButton();
			undoButton.setToolTipText("Undo");
			undoButton.setIcon(ApplicationManager.GetIcon("EDITUNDO"));
			undoButton.addActionListener(this);
			buttons.add(undoButton);
			
			redoButton = new JButton();
			redoButton.setToolTipText("Redo");
			redoButton.setIcon(ApplicationManager.GetIcon("EDITREDO"));
			redoButton.addActionListener(this);
			buttons.add(redoButton);
			
			findButton = new JButton();
			findButton.setToolTipText("Find...");
			findButton.setIcon(ApplicationManager.GetIcon("EDITFIND"));
			findButton.addActionListener(this);
			buttons.add(findButton);
			
			commentButton = new JButton();
			commentButton.setToolTipText("(Un)Comment this block of code");
			commentButton.setIcon(ApplicationManager.GetIcon("TOGGLECOMMENT"));
			commentButton.addActionListener(this);
			buttons.add(commentButton);
			
			renameButton = new JButton();
			renameButton.setToolTipText("Rename the current tab...");
			renameButton.setIcon(ApplicationManager.GetIcon("RENAME"));
			renameButton.addActionListener(this);
			buttons.add(renameButton);
			
			toolbarButtons = buttons.toArray(new JButton[0]);
		}
		return toolbarButtons;
	}

	@Override
	public boolean isRenameable() {
		return false;
	}
	
	private class TabComponent extends JPanel implements ActionListener, EditorFileStatusListener {
		
		private JLabel tabLabel = null;
		private RubyEditorPanel panel = null;
		
		public TabComponent(RubyEditorPanel holding) {
			super();
			this.panel = holding;
			
			this.setLayout(new BorderLayout());
			this.setOpaque(false);
			
			tabLabel = new JLabel(holding.getName());
			tabLabel.setOpaque(false);
			this.setUpLabel(holding);
			
			this.add(tabLabel, BorderLayout.CENTER);
			
			JButton button = new JButton();
		
			if(icon == null) {
				try {
					icon = ApplicationManager.GetIcon(CLOSE_ICON);
					icon.setDimension(new Dimension(tabLabel.getPreferredSize().height/2,tabLabel.getPreferredSize().height/2));
					icon.setFixedSize(true);
				} catch(NullPointerException npe) {
					
				}
				
			}
			
			if(icon != null)
				button.setIcon(icon);
			else
				button.setText("x");
			
			button.setSize(new Dimension(tabLabel.getPreferredSize().height,tabLabel.getPreferredSize().height));
			button.setPreferredSize(new Dimension(tabLabel.getPreferredSize().height,tabLabel.getPreferredSize().height));
			button.addActionListener(this);
			
			JPanel bPanel = new JPanel();
			bPanel.setOpaque(false);
			bPanel.setBorder(BorderFactory.createEmptyBorder(0,4,0,0));
			bPanel.add(button,BorderLayout.CENTER);
			bPanel.add(button);
			this.add(bPanel,BorderLayout.EAST);
			
			holding.addEditorFileStatusListener(this);
		}

		private void setUpLabel(RubyEditorPanel panel) {
			StringBuffer value = new StringBuffer(panel.getName());
			if(panel.isModified() && ! panel.isSavedWithWorksheet())
				value.append(" *");
			
			tabLabel.setText(value.toString());
		}
		
		public void actionPerformed(ActionEvent arg0) {
			int i = tabPane.indexOfTabComponent(TabComponent.this);
			if (i != -1)
				tabPane.remove(i);
		}

		public void editorFileStatusChanged(EditorFileStatusEvent efse) {
			this.setUpLabel(efse.getPanel());
		}
		
		public void editLabel() {
			if(panel.isSavedWithWorksheet()) {
				String current_name = panel.getName();
				
				JTextField entry = new JTextField(current_name);
				
				Object[] buttons = {"Accept", "Cancel"};
				
				int result = JOptionPane.showOptionDialog(getMainWindow(),
						                                  entry,
						                                  "Rename Editor Tab",
						                                  JOptionPane.YES_NO_OPTION,
						                                  JOptionPane.PLAIN_MESSAGE,
						                                  null,
						                                  buttons,
						                                  buttons[0]);
				if(result == 0)
					panel.setVisibleName(entry.getText());
				
				panel.fireEditorFileStatusEvent(EditorFileStatusEvent.FILE_NAME_CHANGE);
			}
		}

		@Override
		public synchronized void addMouseListener(MouseListener l) {
			tabLabel.addMouseListener(l);
			super.addMouseListener(l);
		}

		@Override
		public synchronized void removeMouseListener(MouseListener l) {
			tabLabel.removeMouseListener(l);
			super.removeMouseListener(l);
		}
		
	}

	/** Returns the current topmost editing panel
	 * 
	 * @return the topmost editing panel, or null if none is present
	 */
	private RubyEditorPanel getCurrentEditor() {
		try {
			return (RubyEditorPanel)tabPane.getSelectedComponent();
		} catch(ClassCastException cce) {
			return null;
		}
	}
	
	private TabComponent getSelectedTabComponent() {
		int i = tabPane.getSelectedIndex();
		if(i < 0 || i >= tabPane.getTabCount())
			return null;
		else {
			try {
				return (TabComponent)tabPane.getTabComponentAt(i);
			} catch(ClassCastException cce) {
				return null;
			}
		}
			
	}
	
	
	public void actionPerformed(ActionEvent e) {
		RubyEditorPanel panel = this.getCurrentEditor();

		if(panel != null) {
			if(e.getSource() == m_cut)
				panel.cut();
			else if(e.getSource() == m_copy)
				panel.copy();
			else if(e.getSource() == m_paste)
				panel.paste();
			else if(e.getSource() == commentButton || e.getSource() == m_comment)
				panel.toggleCommentSelected();
			else if(e.getSource() == undoButton || e.getSource() == m_undo)
				panel.undo();
			else if(e.getSource() == redoButton || e.getSource() == m_redo)
				panel.redo();
			else if(e.getSource() == m_save || e.getSource() == saveButton)
				panel.save();
			else if(e.getSource() == m_saveas || e.getSource() == saveAsButton)
				panel.saveAs();
			else if(e.getSource() == m_open)
				openFile();
			else if(e.getSource() == m_new)
				RubyEditorWindow.OpenEditorTab("", getMainWindow());
			else if(e.getSource() == m_find || e.getSource() == findButton)
				panel.find();
		}
		
		TabComponent comp = this.getSelectedTabComponent();
		if(comp != null) {
			if(e.getSource() == renameButton)
				comp.editLabel();
		}

	}
	
	private void openFile() {
		JFileChooser chooser = new JFileChooser();
		chooser.setDialogTitle("Open text file...");
		chooser.setDialogType(JFileChooser.OPEN_DIALOG);
		chooser.setFileFilter(new RubyFileFilter());
		chooser.setFileFilter(new PlainTextFilter());
	    if (chooser.showOpenDialog(chooser) == JFileChooser.APPROVE_OPTION) {
	    	this.openTab(chooser.getSelectedFile());
	    }
	}
	
	public List<RubyEditorPanel> getEditorPanels() {
		ArrayList<RubyEditorPanel> panels = new ArrayList<RubyEditorPanel>(tabPane.getTabCount());
		for(int i=0;i<tabPane.getTabCount();i++) {
			RubyEditorPanel panel = null;
			if(tabPane.getComponentAt(i) instanceof RubyEditorPanel)
				panel = (RubyEditorPanel)tabPane.getComponentAt(i);
			else
				continue;
			panels.add(panel);
		}
		
		return panels;
	}
}
