package com.dbxml.db.admin;

/*
 * dbXML - Native XML Database
 * Copyright (C) 1999-2004  The dbXML Group, L.L.C.
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Id: AdminConfig.java,v 1.10 2004/08/22 04:18:40 bradford Exp $
 */

import java.util.*;

import com.dbxml.util.Configuration;
import com.dbxml.util.ConfigurationCallback;
import com.dbxml.xml.dom.DOMHelper;
import com.dbxml.xml.dom.TextWriter;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

/**
 * AdminConfig
 */

public final class AdminConfig {
   private static final String[] EmptyStrings = new String[0];
   private static final int BUFFER_SIZE = 4096;

   private static final String DRIVERS = "drivers";
   private static final String DRIVER = "driver";
   private static final String FILESYSTEMS = "filesystems";
   private static final String FILESYSTEM = "filesystem";
   private static final String LABEL = "label";
   private static final String CLASS = "class";
   private static final String DIRECTORY = "directory";
   private static final String PROPERTIES = "properties";
   private static final String PROPERTY = "property";
   private static final String NAME = "name";
   private static final String VALUE = "value";
   private static final String UI = "ui";
   private static final String XMLEDITOR = "xmleditor";
   private static final String AUTOBEAUTIFY = "autobeautify";
   private static final String XSLTVIEWER = "xsltviewer";
   private static final String DEFAULTVIEW = "defaultview";
   private static final String TOOLBAR = "toolbar";
   private static final String EXPLORER = "explorer";
   private static final String QUERY = "query";
   private static final String NAMESPACES = "namespaces";
   private static final String NAMESPACE = "namespace";
   private static final String PREFIX = "prefix";
   private static final String URI = "uri";
   private static final String DEFAULTTYPE = "defaulttype";
   private static final String MESSAGES = "messages";
   private static final String STATUSBAR = "statusbar";
   private static final String VISIBLE = "visible";

   private File file;
   private Configuration config;
   private Configuration uiCfg;
   private Configuration driversCfg;
   private Configuration filesystemsCfg;
   private Map nsMap;

   public AdminConfig(File file) {
      this.file = file;
   }

   public Configuration getConfiguration() {
      return config;
   }

   public void load() throws IOException {
      try {
         Document doc = DOMHelper.parse(file);
         config = new Configuration(doc);
         driversCfg = config.getChild(DRIVERS);
         filesystemsCfg = config.getChild(FILESYSTEMS);
         uiCfg = config.getChild(UI);
      }
      catch ( SAXException e ) {
         throw new IOException(e.getMessage());
      }
   }

   public void save() throws IOException {
      FileOutputStream fos = new FileOutputStream(file);
      BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER_SIZE);

      Element elem = config.getElement();
      Document doc = elem.getOwnerDocument();

      TextWriter.writePretty(doc, bos);

      bos.flush();
      bos.close();
   }

   public Map getNamespaceMap() {
      if ( nsMap == null ) {
         Configuration nsCfg = config.getChild(NAMESPACES, true);
         nsMap = new TreeMap();
         nsCfg.processChildren(NAMESPACE, new ConfigurationCallback() {
            public void process(Configuration cfg) {
               String prefix = cfg.getAttribute(PREFIX);
               String uri = cfg.getAttribute(URI);
               nsMap.put(prefix, uri);
            }
         });
      }
      return nsMap;
   }

   public void setNamespaceMap(Map nsMap) {
      this.nsMap = nsMap;

      Configuration nsCfg = config.getChild(NAMESPACES, true);

      Configuration[] children = nsCfg.getChildren();
      for ( int i = 0 ; i < children.length; i++ )
         children[i].delete();

      Iterator iter = nsMap.entrySet().iterator();
      while ( iter.hasNext() ) {
         Map.Entry entry = (Map.Entry)iter.next();
         String prefix = (String)entry.getKey();
         String uri = (String)entry.getValue();
         Configuration c = nsCfg.add(NAMESPACE);
         c.setAttribute(PREFIX, prefix);
         c.setAttribute(URI, uri);
      }
   }

   public void setAutoBeautify(boolean autoBeautify) {
      uiCfg.getChild(XMLEDITOR, true).setAttribute(AUTOBEAUTIFY, autoBeautify);
   }

   public boolean isAutoBeautify(boolean defValue) {
      return uiCfg.getChild(XMLEDITOR, true).getBooleanAttribute(AUTOBEAUTIFY, defValue);
   }

   public void setDefaultQueryType(int defaultQueryType) {
      uiCfg.getChild(QUERY, true).setAttribute(DEFAULTTYPE, defaultQueryType);
   }

   public int getDefaultQueryType(int defValue) {
      return uiCfg.getChild(QUERY, true).getIntAttribute(DEFAULTTYPE, defValue);
   }

   public void setDefaultXSLTView(int defaultXSLTView) {
      uiCfg.getChild(XSLTVIEWER, true).setAttribute(DEFAULTVIEW, defaultXSLTView);
   }

   public int getDefaultXSLTView(int defValue) {
      return uiCfg.getChild(XSLTVIEWER, true).getIntAttribute(DEFAULTVIEW, defValue);
   }

   private void setVisible(String name, boolean visible) {
      uiCfg.getChild(name, true).setAttribute(VISIBLE, visible);
   }

   public void setToolbarVisible(boolean visible) {
      setVisible(TOOLBAR, visible);
   }

   public void setExplorerVisible(boolean visible) {
      setVisible(EXPLORER, visible);
   }

   public void setQueryVisible(boolean visible) {
      setVisible(QUERY, visible);
   }

   public void setMessagesVisible(boolean visible) {
      setVisible(MESSAGES, visible);
   }

   public void setStatusbarVisible(boolean visible) {
      setVisible(STATUSBAR, visible);
   }

   private boolean isVisible(String name, boolean defValue) {
      return uiCfg.getChild(name, true).getBooleanAttribute(VISIBLE, defValue);
   }

   public boolean isToolbarVisible(boolean defValue) {
      return isVisible(TOOLBAR, defValue);
   }

   public boolean isExplorerVisible(boolean defValue) {
      return isVisible(EXPLORER, defValue);
   }

   public boolean isQueryVisible(boolean defValue) {
      return isVisible(QUERY, defValue);
   }

   public boolean isMessagesVisible(boolean defValue) {
      return isVisible(MESSAGES, defValue);
   }

   public boolean isStatusbarVisible(boolean defValue) {
      return isVisible(STATUSBAR, defValue);
   }

   public String[] listDrivers() {
      final Set set = new TreeSet();
      driversCfg.processChildren(DRIVER, new ConfigurationCallback() {
         public void process(Configuration cfg) {
            set.add(cfg.getAttribute(LABEL));
         }
      });
      return (String[])set.toArray(EmptyStrings);
   }

   public DriverConfig newDriverConfig() {
      return new DriverConfig();
   }

   public DriverConfig[] getDrivers() {
      String[] drivers = listDrivers();
      DriverConfig[] result = new DriverConfig[drivers.length];
      for ( int i = 0; i < drivers.length; i++ )
         result[i] = getDriver(drivers[i]);
      return result;
   }

   public DriverConfig getDriver(final String label) {
      final DriverConfig driverConfig = new DriverConfig();
      driversCfg.processChildren(DRIVER, new ConfigurationCallback() {
         public void process(Configuration cfg) {
            String lbl = cfg.getAttribute(LABEL);
            if ( !lbl.equals(label) )
               return;
            String className = cfg.getAttribute(CLASS);
            driverConfig.setLabel(lbl);
            driverConfig.setClassName(className);

            final Map propsMap = driverConfig.getProperties();
            Configuration propsCfg = cfg.getChild(PROPERTIES);
            if ( propsCfg != null ) {
               propsCfg.processChildren(PROPERTY, new ConfigurationCallback() {
                  public void process(Configuration pcfg) {
                     propsMap.put(pcfg.getAttribute(NAME), pcfg.getAttribute(VALUE));
                  }
               });
            }
         }
      });

      if ( driverConfig.getLabel() != null )
         return driverConfig;
      else
         return null;
   }

   public void removeDriverConfig(final String label) {
      driversCfg.processChildren(DRIVER, new ConfigurationCallback() {
         public void process(Configuration cfg) {
            String lbl = cfg.getAttribute(LABEL);
            if ( lbl.equals(label) )
               cfg.delete();
         }
      });
   }

   public void addDriverConfig(DriverConfig driverConfig) {
      final String label = driverConfig.getLabel();
      driversCfg.processChildren(DRIVER, new ConfigurationCallback() {
         public void process(Configuration cfg) {
            String lbl = cfg.getAttribute(LABEL);
            if ( lbl.equals(label) )
               cfg.delete();
         }
      });

      Configuration driverCfg = driversCfg.add(DRIVER);
      driverCfg.setAttribute(LABEL, driverConfig.getLabel());
      driverCfg.setAttribute(CLASS, driverConfig.getClassName());
      Configuration propsCfg = driverCfg.getChild(PROPERTIES, true);
      Map propsMap = driverConfig.getProperties();
      Iterator iter = propsMap.entrySet().iterator();
      while ( iter.hasNext() ) {
         Map.Entry entry = (Map.Entry)iter.next();
         String name = (String)entry.getKey();
         String value = (String)entry.getValue();
         Configuration propCfg = propsCfg.add(PROPERTY);
         propCfg.setAttribute(NAME, name);
         propCfg.setAttribute(VALUE, value);
      }
   }

   public String[] listFileSystems() {
      final Set set = new TreeSet();
      filesystemsCfg.processChildren(FILESYSTEM, new ConfigurationCallback() {
         public void process(Configuration cfg) {
            set.add(cfg.getAttribute(LABEL));
         }
      });
      return (String[])set.toArray(EmptyStrings);
   }

   public FileSystemConfig newFileSystemConfig() {
      return new FileSystemConfig();
   }

   public FileSystemConfig[] getFileSystems() {
      String[] filesystems = listFileSystems();
      FileSystemConfig[] result = new FileSystemConfig[filesystems.length];
      for ( int i = 0; i < filesystems.length; i++ )
         result[i] = getFileSystem(filesystems[i]);
      return result;
   }

   public FileSystemConfig getFileSystem(final String label) {
      final FileSystemConfig fsCfg = new FileSystemConfig();
      filesystemsCfg.processChildren(FILESYSTEM, new ConfigurationCallback() {
         public void process(Configuration cfg) {
            String lbl = cfg.getAttribute(LABEL);
            if ( !lbl.equals(label) )
               return;

            String directory = cfg.getAttribute(DIRECTORY);

            fsCfg.setLabel(lbl);
            fsCfg.setDirectory(directory);
         }
      });

      if ( fsCfg.getLabel() != null )
         return fsCfg;
      else
         return null;
   }

   public void removeFileSystem(final String label) {
      filesystemsCfg.processChildren(FILESYSTEM, new ConfigurationCallback() {
         public void process(Configuration cfg) {
            String lbl = cfg.getAttribute(LABEL);
            if ( lbl.equals(label) )
               cfg.delete();
         }
      });
   }

   public void addFileSystem(FileSystemConfig filesystemConfig) {
      final String label = filesystemConfig.getLabel();
      filesystemsCfg.processChildren(FILESYSTEM, new ConfigurationCallback() {
         public void process(Configuration cfg) {
            String lbl = cfg.getAttribute(LABEL);
            if ( lbl.equals(label) )
               cfg.delete();
         }
      });

      Configuration filesystemCfg = filesystemsCfg.add(FILESYSTEM);
      filesystemCfg.setAttribute(LABEL, filesystemConfig.getLabel());
      filesystemCfg.setAttribute(DIRECTORY, filesystemConfig.getDirectory());
   }


   /**
    * FileSystemConfig
    */

   public class FileSystemConfig {
      private String label;
      private String directory;

      public void setLabel(String label) {
         this.label = label;
      }

      public String getLabel() {
         return label;
      }

      public void setDirectory(String directory) {
         this.directory = directory;
      }

      public String getDirectory() {
         return directory;
      }
   }


   /**
    * DriverConfig
    */

   public class DriverConfig {
      private String label;
      private String className;
      private Map properties = Collections.synchronizedMap(new HashMap());

      public void setLabel(String label) {
         this.label = label;
      }

      public String getLabel() {
         return label;
      }

      public void setClassName(String className) {
         this.className = className;
      }

      public String getClassName() {
         return className;
      }

      public Map getProperties() {
         return properties;
      }
   }
}

