package edu.sc.seis.gee.configurator;

import java.awt.Component;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import org.apache.log4j.Category;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import edu.sc.seis.fissuresUtil.exceptionHandler.GlobalExceptionHandler;
import edu.sc.seis.gee.FrameManager;
import edu.sc.seis.gee.Start;
import edu.sc.seis.gee.task.AllTasks;


public class Configure {

    public Configure() {
    }

    /** Loads a xml configuration with validation off. */
    public void load(Reader xmlFile) throws ConfigurationException {
        load(xmlFile, false);
    }

    /** Loads a xml configuration file. If validate is true, then the
     *  configuration file is validated first. */
    public void load(Reader xmlFile, boolean validate)
        throws ConfigurationException {
        load(xmlFile, validate, null);
    }

    public void load(Reader xmlFile, boolean validate, final String externalSchemaLoc)
        throws ConfigurationException {
        load(xmlFile, validate, externalSchemaLoc, false);
    }

    protected void load(Reader xmlFile, boolean validate, final String externalSchemaLoc, boolean append)
        throws ConfigurationException {
        try {
            if ( ! append) {
                FrameManager.getManager().reset();
                AllTasks.clear();//If a config has already been loaded, clear it out
                currentActivity = 0;//Reset to the initial activity
            }
            XMLReader parser = (XMLReader)Class.forName(DEFAULT_PARSER_NAME).newInstance();
            errorHandler = new SimpleErrorHandler();
            contentHandler = new GeeConfigHandler(parser);
            parser.setContentHandler(contentHandler);
            parser.setErrorHandler(errorHandler);
            parser.setFeature( "http://xml.org/sax/features/validation",
                              validate);

            parser.setFeature( "http://xml.org/sax/features/namespaces",
                              true );

            parser.setFeature( "http://apache.org/xml/features/validation/schema",
                              validate );
            if (externalSchemaLoc != null) {
                parser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",
                                   externalSchemaLoc.toString());
            } // end of if (externalSchemaLoc != null)
            parser.setFeature( "http://apache.org/xml/features/validation/schema-full-checking",
                              false );

            parser.setEntityResolver(new  EntityResolver() {
                        public InputSource resolveEntity(String publicId,
                                                         String systemId) {
                            try {
                                logger.info("entity resolve "+externalSchemaLoc+" "+publicId+" "+systemId);
                                if (systemId.equals("http://www.seis.sc.edu/xschema/gee/1.0/configuration.xsd") || systemId.endsWith("geeconfigurator.xsd") || systemId.equals(externalSchemaLoc)) {
                                    // return a special input source
                                    //You specify the schema location whereever your xsd/dtd files are located. But the systemId is the dtd that is mentioned
                                    //in the xml file/xml string
                                    Class c = getClass();
                                    java.net.URL entityURL =
                                        c.getClassLoader().getResource(externalSchemaLoc);
                                    InputStream fis = entityURL.openStream();
                                    return new InputSource(fis);
                                } else {
                                    // use the default behaviour return null;
                                }
                            } catch (Exception e) {
                                GlobalExceptionHandler.handle("Caught exception in EntityResolver",
                                            e);
                            }
                            return null;
                        }
                    });

            parser.parse(new InputSource(xmlFile));
            
            AppendConfigHandler[] appends = contentHandler.getAppendConfigs();
            Configure[] appendConfigure = new Configure[appends.length];
            for(int i = 0; i < appends.length; i++) {
                appendConfigure[i] = new Configure();
                // this loads the tasks into AllTask
                appendConfigure[i].load(Start.getReader(appends[i].getConfigURL()), validate, externalSchemaLoc, true);
            }
            // load tasks and activities from current config file
            // tasks with the same id already in AllTask from a append config are overwritten
            TaskHandler[] tasks = contentHandler.getTasks();
            for(int i = 0; i < tasks.length; i++) {
                AllTasks.assoicateTaskActionWithId(tasks[i].getTaskAction());
            }
            
            //append activites from appendConfigs to contentHandler
            for(int i = 0; i < appendConfigure.length; i++) {
                contentHandler.appendActivites(appendConfigure[i].getActivities());
            }
            
        } catch (ClassNotFoundException e) {
            throw new ConfigurationException(e.toString(), e);
        } catch (IllegalAccessException e) {
            throw new ConfigurationException(e.toString(), e);
        } catch (InstantiationException e) {
            throw new ConfigurationException(e.toString(), e);
        } catch (SAXParseException e) {
            throw new ConfigurationException("Problem with the configuration file at "+
                                                 SimpleErrorHandler.getLocationString(e),
                                             e);
        } catch (SAXException e) {
            throw new ConfigurationException(e.toString(), e);
        } catch (java.io.IOException e) {
            throw new ConfigurationException(e.toString(), e);
        } // end of try-catch
    }

    public void validate(Reader xmlFile, String externalSchemaLoc)
        throws ConfigurationException {
        try {
            XMLReader parser = (XMLReader)Class.forName(DEFAULT_PARSER_NAME).newInstance();
            errorHandler = new SimpleErrorHandler();
            SimpleContentHandler simpleHandler = new SimpleContentHandler(parser);
            parser.setContentHandler(simpleHandler);
            parser.setErrorHandler(errorHandler);
            parser.setFeature( "http://xml.org/sax/features/validation",
                              true);

            parser.setFeature( "http://xml.org/sax/features/namespaces",
                              true );

            parser.setFeature( "http://apache.org/xml/features/validation/schema",
                              true );
            if (externalSchemaLoc != null) {
                parser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",
                                   externalSchemaLoc.toString());
            } // end of if (externalSchemaLoc != null)
            parser.setFeature( "http://apache.org/xml/features/validation/schema-full-checking",
                              false );
            parser.parse(new InputSource(xmlFile));
        } catch (ClassNotFoundException e) {
            throw new ConfigurationException(e.toString(), e);
        } catch (IllegalAccessException e) {
            throw new ConfigurationException(e.toString(), e);
        } catch (InstantiationException e) {
            throw new ConfigurationException(e.toString(), e);
        } catch (SAXParseException e) {
            throw new ConfigurationException("Problem with the configuration file at "+
                                                 SimpleErrorHandler.getLocationString(e),
                                             e);
        } catch (SAXException e) {
            throw new ConfigurationException("Problem with the xml parser.", e);
        } catch (java.io.IOException e) {
            throw new ConfigurationException(e.toString(), e);
        } // end of try-catch
    }

    public JMenuBar getMenuBar() throws ConfigurationException {
        if(contentHandler == null){
            return new JMenuBar();
        }
        ActivityHandler[] act =  contentHandler.getActivities();
        return getMenuBar(act[currentActivity]);
    }

    /** Gets the menubar for the given activity. Also prepends the menubar from any
     appendActivity. */
    public JMenuBar getMenuBar(ActivityHandler curAct)  throws ConfigurationException {
        ActivityHandler[] act =  contentHandler.getActivities();
        String appendActivityId = curAct.getAppendActivityId();
        if (appendActivityId.length() != 0) {
            JMenuBar appendBar = getMenuBar(act[getActivityIndex(appendActivityId)]);
            if (appendBar != null) {
                JMenuBar newBar = curAct.getMenuBar();
                for (int i=0; i<newBar.getMenuCount(); i++) {
                    boolean found = false;
                    JMenu newMenu = newBar.getMenu(i);
                    for (int j = 0; j < appendBar.getMenuCount(); j++) {
                        JMenu appendMenu = appendBar.getMenu(j);
                        if (appendMenu.getText().equals(newMenu.getText())) {
                            found = true;
                            Component[] menuItems = newBar.getMenu(i).getMenuComponents();
                            for (int k = 0; k < menuItems.length; k++) {
                                appendBar.getMenu(j).add(menuItems[k]);
                            }
                        }
                    }
                    if ( ! found) {
                        // new menu does not match existing menu
                        appendBar.add(newBar.getMenu(i));
                    }
                }
                return appendBar;
            }
        }
        return curAct.getMenuBar();
    }

    public String[] getInitialTasks() throws ConfigurationException {
        ActivityHandler[] act =  contentHandler.getActivities();
        return getInitialTasks(act[currentActivity]);
    }


    public String[] getInitialTasks(ActivityHandler curAct) throws ConfigurationException{
        ActivityHandler[] act =  contentHandler.getActivities();
        String appendActivityId = curAct.getAppendActivityId();
        String[] appendInitialTasks = new String[0];
        if (appendActivityId.length() != 0) {
            appendInitialTasks =
                getInitialTasks(act[getActivityIndex(appendActivityId)]);
        }
        String[] currentInitialTasks = curAct.getInitialTasks();
        String[] tmp =
            new String[currentInitialTasks.length+appendInitialTasks.length];
        System.arraycopy(appendInitialTasks, 0,
                         tmp, 0,
                         appendInitialTasks.length);
        System.arraycopy(currentInitialTasks, 0,
                         tmp, appendInitialTasks.length,
                         currentInitialTasks.length);
        return tmp;
    }

    public String[] getInactiveTasks() throws ConfigurationException {
        ActivityHandler[] act =  contentHandler.getActivities();
        return getInactiveTasks(act[currentActivity]);
    }

    public String[] getInactiveTasks(ActivityHandler curAct) throws ConfigurationException{
        ActivityHandler[] act =  contentHandler.getActivities();
        String appendActivityId = curAct.getAppendActivityId();
        String[] appendInactiveTasks = new String[0];
        if (appendActivityId.length() != 0) {
            appendInactiveTasks =
                getInactiveTasks(act[getActivityIndex(appendActivityId)]);
        }
        String[] currentInactiveTasks = curAct.getInactiveTasks();
        String[] tmp =
            new String[currentInactiveTasks.length+appendInactiveTasks.length];
        System.arraycopy(appendInactiveTasks, 0,
                         tmp, 0,
                         appendInactiveTasks.length);
        System.arraycopy(currentInactiveTasks, 0,
                         tmp, appendInactiveTasks.length,
                         currentInactiveTasks.length);
        return tmp;
    }

    public java.util.Map getActions() {
        return actions;
    }

    public void setActivity(String id) throws ConfigurationException  {
        currentActivity = getActivityIndex(id);
    }

    public int getActivityIndex(String id) throws ConfigurationException  {
        ActivityHandler[] act =  contentHandler.getActivities();
        int i=0;
        while ( ! act[i].getId().equals(id)) {
            i++;
            if (i == act.length) {
                throw new ConfigurationException("Activity "+id+" not found.");
            } // end of if (i == act.length)
        } // end of while (act[i].getId().equals(id))
        return i;
    }

    public String getActivityId(int index)  {
        ActivityHandler[] act =  contentHandler.getActivities();
        return act[index].getId();
    }

    public int getCurrentActivityIndex() {
        return currentActivity;
    }
    
    public ActivityHandler[] getActivities(){
        return contentHandler.getActivities();
    }

    protected int currentActivity;

    protected java.util.Map actions = new java.util.HashMap();

    /** Default parser name. */
    private static final String
        DEFAULT_PARSER_NAME = "org.apache.xerces.parsers.SAXParser";

    private ErrorHandler errorHandler;
    private GeeConfigHandler contentHandler;

    static Category logger =
        Category.getInstance(Configure.class.getName());

    public static void main (String[] args) {
        org.apache.log4j.BasicConfigurator.configure();
        Configure config = new Configure();

        try {
            String filename;
            InputStream in;
            if (args.length > 0) {
                filename = args[0];
                in = new BufferedInputStream(new FileInputStream(filename));
            } else {
                filename =
                    "edu/sc/seis/gee/configurator/DefaultConfig.xml";
                in =
                    config.getClass().getClassLoader().getResourceAsStream(filename);
            } // end of else


            String schemaFilename =
                "edu/sc/seis/gee/configurator/geeconfigurator.xsd";
            java.net.URL inURL =
                config.getClass().getClassLoader().getResource(schemaFilename);
            logger.debug("inURL="+inURL);

            config.validate(new BufferedReader(new InputStreamReader(in)),
                            inURL.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } // end of try-catch

    } // end of main ()


}
