package org.greenstone.server;

import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.*;

import java.lang.reflect.*;

import org.apache.log4j.*;
/**
 * Base class to help start or restart the library server
 * 
 */
public abstract class BaseServerControl extends JFrame {

    static Logger logger = Logger.getLogger(BaseServerControl.class.getName());
 
    /** The dimension of the frame */
    static final private Dimension FRAME_SIZE = new Dimension(430, 310);

    Color bg_color = Color.white;    
    
    /** some components we need to refer to later */
    protected JLabel info_label;
    protected JButton enter_button;
    protected JMenu fMenu;
    protected BaseServer server;
    protected JFrame thisframe;
    
    static boolean DEBUGGING_TEST_MODE = true;
    static boolean TEST_MODE = false;
    
    public static void invokeInDispatchThreadIfNeeded(boolean synchronous, Runnable runnable) {
	if (EventQueue.isDispatchThread()) {
	    runnable.run();
	} else {
	    if(synchronous) {
		try {
		    SwingUtilities.invokeAndWait(runnable);
		} catch(Exception e) {
		    logger.error("### exception trying to invokeAndWait()", e);
		}
	    } else { // asynchronous
		SwingUtilities.invokeLater(runnable);
	    }
	}
    }
    public static void printComponentNames(Component comp, String tabbing) {
	if(!TEST_MODE) return;
	
	System.err.println(tabbing + comp.getName());
	Component[] children = ((Container)comp).getComponents();
	// recursive call
	for(int i = 0; i < children.length; i++) {
	    printComponentNames(children[i], tabbing + "  ");
	}
    }
    public static void setNamesRecursively(Component root, String name) {
	if(!TEST_MODE) return; //if(!BaseServer.isTesting()) return;
	String rootName;
	if(name != null && !name.equals("")) {
	    rootName = name;
	} else {
	    rootName =  root.getName();
	    if(rootName == null || rootName.equals("")) {
		rootName = root.getClass().getSimpleName();	    	    
	    }
	}
	root.setName(rootName);
	//System.err.println("@@@ Root name was: " + root.getName());
	setNamesRecursively(root, true, rootName, rootName);
    }
    public static void setNamesRecursively(Component comp, boolean isRoot,
					   String prefix, String rootName)
    {
	Component[] children = ((Container)comp).getComponents();

	prefix = prefix.replaceAll(".JPanel", "");// avoid nested names like *.JPanel.JPanel.JPanel
	
	for(int i = 0; i < children.length; i++) {
	    String name = children[i].getName();
	    //System.err.println("@@@ Child name before: " + children[i].getName());	    
	    if(name == null || name.equals("")) {
		name = prefix + "." + comp.getClass().getSimpleName();
		if(!name.startsWith(rootName)) {
		    name = rootName + "." + name;
		}
		children[i].setName(name);
	    } else {
		name = name.replace("null", prefix);
		if(!name.startsWith(rootName)) {
		    name = rootName + "." + name;
		}
		children[i].setName(name);
		name = prefix;
	    }
	    //System.err.println("@@@ Child name after: " + children[i].getName());	    
	    // recursive call
	    setNamesRecursively(children[i], false, name, rootName);
	}
    }
    
    // won't launch regular browser if testing: selenium can launch browser with preview URL
    public String getLibraryURL() { return server.getBrowserURL(); }
    
    public BaseServerControl(BaseServer server,String frame_title) 
    {
	super(frame_title);
	
	TEST_MODE = server.isTesting();
	
	this.server = server;
        thisframe = this;

	setSize(FRAME_SIZE);
	if(server.isTesting()) {
	    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
	} else {
	    setDefaultCloseOperation(EXIT_ON_CLOSE);
	}

	// set the icon for the Greenstone Server Interface
	try {
	    ImageIcon image = new ImageIcon(getClass().getResource("/images/servericon.png"));
	    if (image != null) {
		this.setIconImage(image.getImage());
	    }	
	}
	catch (Exception exception) {
	    System.err.println("Error: Could not load servericon.png");
	    logger.error("Error: Could not load servericon.png");
	}	

	Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
	setLocation((screen_size.width - FRAME_SIZE.width) / 2,
		    (screen_size.height - FRAME_SIZE.height) / 2);
	setBackground(Color.white);
        addWindowListener(new MyAdapter()); 
   
        JPanel title_panel = new JPanel();
	title_panel.setLayout(new BorderLayout());

        JLabel title_label = new JLabel();


        String title = BaseServer.dictionary.get(BaseServer.Property.SERVER_CONTROL+".Title");           
        title_label.setText(stringToHTML(title)); 
        title_label.setOpaque(false);	
	title_label.setHorizontalAlignment(SwingConstants.CENTER);
        title_label.setFont(new Font("SansSerif",Font.PLAIN,18));
       

        JLabel version_label = new JLabel();
        String version = BaseServer.dictionary.get(BaseServer.Property.SERVER_CONTROL+".Version").toLowerCase();
         
        version_label.setText(stringToHTML(version)); 
        version_label.setOpaque(false);	
	version_label.setHorizontalAlignment(SwingConstants.CENTER);
        version_label.setFont(new Font("SansSerif",Font.PLAIN,14));
        
        title_panel.add(title_label,BorderLayout.CENTER);
        title_panel.add(version_label,BorderLayout.SOUTH);
	title_panel.setBackground(bg_color);	
	title_panel.setBorder(BorderFactory.createEmptyBorder(5,10,5,10));

   	info_label = new JLabel();
        info_label.setOpaque(false);
        info_label.setHorizontalAlignment(SwingConstants.LEFT);
        info_label.setVerticalAlignment(SwingConstants.CENTER);
        info_label.setFont(new Font("SansSerif",Font.PLAIN,14));
	info_label.setBorder(BorderFactory.createEmptyBorder(5,10,5,10));
			
        JPanel button_panel = new JPanel(new FlowLayout(FlowLayout.CENTER));
	enter_button = new JButton();
	enter_button.setEnabled(false);
	enter_button.addActionListener(new EnterButtonListener());
        enter_button.setText(BaseServer.dictionary.get("ServerControl.EnterLibrary"));
	button_panel.add(enter_button);
	button_panel.setBackground(bg_color);	
	button_panel.setBorder(BorderFactory.createEmptyBorder(5,10,5,10));
	getRootPane().setDefaultButton(enter_button); // button gets the focus for enterpress

	getContentPane().setLayout(new BorderLayout());
	getContentPane().add(title_panel,BorderLayout.NORTH);
	getContentPane().add(info_label,BorderLayout.CENTER);
	getContentPane().add(button_panel,BorderLayout.SOUTH);
	getContentPane().setBackground(bg_color);
    	setJMenuBar(createMenu());
	setVisible(true);
	
	enter_button.setName("ServerControl.enter_button");

	setNamesRecursively(this, "ServerControl"); // naming this JFrame for testing purposes
	printComponentNames(this, "");
    }

    protected abstract JMenuBar createMenu();

    protected JMenuBar createMenu(JMenuItem iConf, String menuItemNameSuffix) {
	JMenuBar menuBar = new JMenuBar();
	fMenu = new JMenu(BaseServer.dictionary.get("ServerControl.Menu.File"));
	fMenu.setName("ServerControl.fileMenu");
	JMenuItem iExit = new JMenuItem(BaseServer.dictionary.get("ServerControl.Menu.Exit"));
	iExit.setBackground(Color.white);
	iExit.addActionListener(new ActionListener() {
		public void actionPerformed(ActionEvent ae) {
		    /*Thread runInThread = new Thread(new Runnable(){
		      public void run(){
		      server.stop();
		      
		      }
		      },"stop server");
		      try{
		      runInThread.start(); 
		      
		      }
		    catch(Exception e){
		    logger.error(e);
		    }*/    
		    thisframe.dispose();
		    server.stop();
		    if(!server.isTesting()) {
			// If we're doing automated testing, then a System.exit() would kill the
			// testing too, so don't System.exit() when testing.	    
			System.exit(0);
		    }
		}
	    });

	fMenu.add(iConf);
	iConf.setName(fMenu.getName() + "_" + menuItemNameSuffix);
	fMenu.add(iExit);
	iExit.setName(fMenu.getName() + "_exit");
	fMenu.setEnabled(false);
	menuBar.add(fMenu);
	menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));
	
	//setNamesRecursively(menuBar, "");
	printComponentNames(menuBar, "");
	return menuBar;
    }

    public void updateControl(){
      invokeInDispatchThreadIfNeeded(true, new Runnable() {
		public void run() {
      	switch (server.getServerState()){
	case BaseServer.START_SERVER:
	    {
		info_label.setText(stringToHTML(BaseServer.dictionary.get("ServerControl.Help_EnterLibrary")));
		enter_button.setText(stringToHTML(BaseServer.dictionary.get("ServerControl.EnterLibrary")));
		enter_button.setEnabled(true);
		fMenu.setEnabled(true);
		break;
	    }
	case BaseServer.SERVER_STARTED:
	    {
		info_label.setText(stringToHTML(BaseServer.dictionary.get("ServerControl.Help_RestartLibrary")));
		enter_button.setText(stringToHTML(BaseServer.dictionary.get("ServerControl.RestartLibrary")));
		enter_button.setEnabled(true);
		fMenu.setEnabled(true);
		break;
	    }
	case BaseServer.SERVER_START_FAILED:
	    {
		enter_button.setText(stringToHTML(BaseServer.dictionary.get("ServerControl.StartServer")));
		enter_button.setEnabled(true);
		fMenu.setEnabled(true);
		break;	
	    }
	case BaseServer.BROWSER_LAUNCHED:
	    {
		info_label.setText(stringToHTML(BaseServer.dictionary.get("ServerControl.BrowserLaunched",new String[]{server.getBrowserURL()})
						+ BaseServer.dictionary.get("ServerControl.Help_RestartLibrary")));
		enter_button.setText(stringToHTML(BaseServer.dictionary.get("ServerControl.RestartLibrary")));
		thisframe.setState(Frame.ICONIFIED);
		enter_button.setEnabled(true);
                fMenu.setEnabled(true);
		break;
	    }
	case BaseServer.BROWSER_LAUNCH_FAILED:
	    {
		info_label.setText(stringToHTML(BaseServer.dictionary.get("ServerControl.BrowserLaunchFailed",new String[]{server.getBrowserURL()})));
		enter_button.setText(stringToHTML(BaseServer.dictionary.get("ServerControl.EnterLibrary")));
		enter_button.setEnabled(true);
		fMenu.setEnabled(true);
		break;
	    }
        default:
	    {
		enter_button.setText(BaseServer.dictionary.get("ServerControl.EnterLibrary"));
		enter_button.setEnabled(false);
		fMenu.setEnabled(false);
	    }
	}
      } // end run()
     }); // end invoking runnable on EDT
    }
           
   
    public void displayMessage(String message){
	invokeInDispatchThreadIfNeeded(true, new Runnable() {
		public void run() {
		    info_label.setText(stringToHTML("<br>"+message));
		}
	    });		
    }

    private class MyAdapter extends WindowAdapter{   
	public void windowClosing(WindowEvent env){
	    /*          Thread runInThread = new Thread(new Runnable(){
			public void run(){
			server.stop();
			
			}
			},"stop server");
			try{
			runInThread.start(); 
			}
			catch(Exception e){
			logger.error(e);
			}  
			thisframe.dispose();
	    */
	    thisframe.dispose();
	    server.stop();
	    if(!server.isTesting()) {		
		// If we're doing automated testing, then a System.exit() would kill the
		// testing too, so don't System.exit() when testing.	    
		System.exit(0);
	    }
	}
    } 

    private String stringToHTML(String s){
	return "<html><body>"+s+"</body></html>";	
    }
    
    private class EnterButtonListener
	implements ActionListener {
	
	public void actionPerformed(ActionEvent ev) {
	    switch (server.getServerState()){
	    case BaseServer.START_SERVER:
		{
		 Thread runInThread = new Thread(new Runnable(){
			    public void run(){
				server.start();
				server.launchBrowser();
			    }
			},"start server and launch browser");
		    
		    runInThread.start(); 
                   break;     
		}
	    case BaseServer.SERVER_STARTED:
		{
		 Thread runInThread = new Thread(new Runnable(){
			    public void run(){
				server.launchBrowser();
			    }
			},"launch browser");
		    
		    runInThread.start(); 
                   break;     
		}
	    case BaseServer.SERVER_START_FAILED:
		{
		    Thread runInThread = new Thread(new Runnable(){
			    public void run(){
				server.start();
			    }
			},"start server");
		    runInThread.start();
		    break;
		}
	    case BaseServer.BROWSER_LAUNCHED: case BaseServer.BROWSER_LAUNCH_FAILED:
		{
		    Thread runInThread = new Thread(new Runnable(){
			    public void run(){
				server.restart();
				  
			    }
			},"restart server");
		    runInThread.start();
		}  
	    }   
	  
	}
    }
	
	public void errorMessage(String message) {
		JOptionPane.showMessageDialog(null,message,"Error", JOptionPane.ERROR_MESSAGE);
	}
}
