/*
 * $Id: ExtensionsLoader.java 5317 2009-03-15 20:57:55Z uckelman $
 *
 * Copyright (c) 2000-2003 by Rodney Kinney
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License (LGPL) as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, copies are available
 * at http://www.opensource.org.
 */
package VASSAL.build.module;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipException;

import VASSAL.build.GameModule;
import VASSAL.command.Command;
import VASSAL.command.CommandEncoder;
import VASSAL.configure.DirectoryConfigurer;
import VASSAL.i18n.Resources;
import VASSAL.tools.DataArchive;
import VASSAL.tools.SequenceEncoder;
import VASSAL.tools.logging.Logger;

public class ExtensionsLoader implements CommandEncoder {
  public static final String COMMAND_PREFIX = "EXT\t"; //$NON-NLS-1$
  // Preferences key for the list of extensions to load
  public static final String SPECIFY_DIR_IN_PREFS = "specifyExtensionDirInPrefs"; //$NON-NLS-1$
  public static final String EXTENSION_DIR = "extensionDIR"; //$NON-NLS-1$

  protected Set<String> loadedExtensions = new HashSet<String>();
  protected HashMap<String,String> loadedIds = new HashMap<String,String>();
  
  protected ExtensionsManager extMgr;
  protected ExtensionsManager globalExtMgr;

  public void addTo(GameModule mod) {
    extMgr = new ExtensionsManager(mod);
    globalExtMgr = new ExtensionsManager("ext");
    mod.addCommandEncoder(this);
    if ("true".equals(GlobalOptions.getInstance().getAttributeValueString(SPECIFY_DIR_IN_PREFS))) { //$NON-NLS-1$
      final DirectoryConfigurer config = new DirectoryConfigurer(EXTENSION_DIR, Resources.getString("ExtensionsLoader.extensions_directory")); //$NON-NLS-1$
      config.setValue((Object) null);
      GameModule.getGameModule().getPrefs().addOption(Resources.getString("ExtensionsLoader.extensions_tab"), config); //$NON-NLS-1$
      extMgr.setExtensionsDirectory(config.getFileValue());
      if (config.getFileValue() == null) {
        config.setValue(extMgr.getExtensionsDirectory(false).getAbsoluteFile());
      }
      config.addPropertyChangeListener(new PropertyChangeListener() {
        public void propertyChange(PropertyChangeEvent evt) {
          extMgr.setExtensionsDirectory((File) evt.getNewValue());
          addExtensions();
        }
      });
    }
    addExtensions();
  }

  protected void addExtensions() {
    for (File ext : globalExtMgr.getActiveExtensions()) {
      if (!addExtension(ext)) {
        globalExtMgr.setActive(ext, false);
      }
    }
    for (File ext : extMgr.getActiveExtensions()) {
      if (!addExtension(ext)) {
        GameModule.getGameModule().warn(Resources.getString("ExtensionsLoader.deactivating_extension",ext.getName()));
        extMgr.setActive(ext, false);
      }
    }
  }

  protected boolean addExtension(File extension) {
    Logger.log("-- Loading extension "+extension);
    String extname = extension.getPath();
    boolean success = loadedExtensions.contains(extname);
    if (!success) {
      try {
        final ModuleExtension ext = createExtension(extname);
        ext.build();
        
        final String id = ext.getExtensionId();
        String idMsg = "";
        if (id.length() > 0) {
          for (String loadedId : loadedIds.keySet()) {
            if (loadedId.equals(id)) {
              idMsg = Resources.getString("ExtensionsLoader.id_conflict", extension.getName(), id, loadedIds.get(id));              
            }
          }
          loadedIds.put(id, extname);
        }
        
        final String msg = getLoadedMessage(ext.getName(), ext.getVersion());        
        loadedExtensions.add(extname);        
        GameModule.getGameModule().warn(msg);
        System.err.println("-- "+msg);
        
        if (idMsg.length() > 0) {
          GameModule.getGameModule().warn(idMsg);
          System.err.println(idMsg);
        }
        success = true;
      }
      catch (ZipException e) {
        // Not a zip file. Ignore.
      }
      catch (IOException e) {
        reportBuildError(e, extension.getName());
      }
      catch (LoadExtensionException e) {
        reportBuildError(e, extension.getName());
      }
    }
    return success;
  }
  
  protected ModuleExtension createExtension(String extname)
                                            throws ZipException, IOException {
    return new ModuleExtension(new DataArchive(extname));
  }

  protected String getLoadedMessage(String name, String version) {
    return Resources.getString("ExtensionsLoader.extension_loaded", name, version); //$NON-NLS-1$
  }
  
  private void reportBuildError(Exception e, String name) {
    String msg = e.getMessage();
    if (msg == null || msg.length() == 0) {
      msg = e.getClass().getName();
      msg = msg.substring(msg.lastIndexOf('.'));
    }
    GameModule.getGameModule().warn(getErrorMessage(name , msg)); //$NON-NLS-1$
  }

  protected String getErrorMessage(String name, String msg) {
    return Resources.getString("ExtensionsLoader.unable_to_load", name , msg);
  }

  public Command decode(String command) {
    Command c = null;
    if (command.startsWith(COMMAND_PREFIX)) {
      SequenceEncoder.Decoder st = new SequenceEncoder.Decoder(command.substring(COMMAND_PREFIX.length()), '\t');
      c = new ModuleExtension.RegCmd(st.nextToken(), st.nextToken());
    }
    return c;
  }

  public String encode(Command c) {
    String s = null;
    if (c instanceof ModuleExtension.RegCmd) {
      ModuleExtension.RegCmd cmd = (ModuleExtension.RegCmd) c;
      SequenceEncoder se = new SequenceEncoder('\t');
      se.append(cmd.getName()).append(cmd.getVersion());
      s = COMMAND_PREFIX + se.getValue();
    }
    return s;
  }
  
  public static class LoadExtensionException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    public LoadExtensionException() {
      super();
    }

    public LoadExtensionException(String message, Throwable cause) {
      super(message, cause);
    }

    public LoadExtensionException(String message) {
      super(message);
    }

    public LoadExtensionException(Throwable cause) {
      super(cause);
    }
  }
}
