/*
 * $Id: SavedGameUpdaterDialog.java 4613 2008-12-04 18:56:01Z uckelman $
 *
 * Copyright (c) 2005 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.configure;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.util.Properties;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import VASSAL.build.GameModule;
import VASSAL.build.module.Documentation;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.tools.ErrorDialog;
import VASSAL.tools.SavedGameUpdater;
import VASSAL.tools.ScrollPane;
import VASSAL.tools.io.IOUtils;

public class SavedGameUpdaterDialog extends JDialog {
  private static final long serialVersionUID = 1L;

  private DefaultListModel savedGamesModel;
  private SavedGameUpdater updater = new SavedGameUpdater();
  private Properties oldPieceInfo;
  private JFileChooser fc;
  private static final String VERSION_KEY = "moduleVerion";
  private static final String MODULE_NAME_KEY = "moduleName";
  private JButton updateButton;
  private JTextField versionField;

  public SavedGameUpdaterDialog(Frame owner) throws HeadlessException {
    super(owner, false);
    setTitle("Update Saved Games");
    initComponents();
    fc = new JFileChooser();
    fc.setCurrentDirectory(GameModule.getGameModule().getFileChooser().getCurrentDirectory());
  }

  private void initComponents() {
    setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
    Box versionBox = Box.createHorizontalBox();
    versionBox.add(new JLabel("Module version of saved games:  "));
    versionField = new JTextField(8);
    versionField.setEditable(false);
    versionField.setMaximumSize(new Dimension(versionField.getMaximumSize().width, versionField.getPreferredSize().height));
    versionBox.add(versionField);
    JButton importButton = new JButton("Import GamePiece info");
    importButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        importPieceInfo();
      }
    });
    versionBox.add(importButton);
    add(versionBox);
    JButton exportButton = new JButton("Export GamePiece info");
    exportButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        exportPieceInfo();
      }
    });
    Box importExportBox = Box.createHorizontalBox();
    importExportBox.add(importButton);
    importExportBox.add(exportButton);
    add(importExportBox);

    Box savedGamesBox = Box.createHorizontalBox();
    Box left = Box.createVerticalBox();
    left.add(new JLabel("Saved Games:"));
    JButton chooseGamesButton = new JButton("Choose");
    chooseGamesButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        chooseSavedGames();
      }
    });
    left.add(chooseGamesButton);
    savedGamesBox.add(left);
    savedGamesModel = new DefaultListModel();
    JList savedGamesList = new JList(savedGamesModel);
    savedGamesList.setVisibleRowCount(5);
    savedGamesList.setCellRenderer(new DefaultListCellRenderer() {
      private static final long serialVersionUID = 1L;

      public Component getListCellRendererComponent(
          JList list,
          Object value,
          int index,
          boolean isSelected,
          boolean cellHasFocus) {
        super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        setText(((File) value).getName());
        return this;
      }
    });
    savedGamesBox.add(new ScrollPane(savedGamesList));
    add(savedGamesBox);

    Box buttonsBox = Box.createHorizontalBox();
    updateButton = new JButton("Update games");
    updateButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        updateGames();
      }
    });
    updateButton.setEnabled(false);
    buttonsBox.add(updateButton);
    JButton helpButton = new JButton("Help");

    HelpFile hf = null;
    try {
      hf = new HelpFile(null, new File(
        new File(Documentation.getDocumentationBaseDir(), "ReferenceManual"),
        "SavedGameUpdater.htm"));
    }
    catch (MalformedURLException ex) {
      ErrorDialog.bug(ex);
    }

    helpButton.addActionListener(new ShowHelpAction(hf.getContents(), null));
    buttonsBox.add(helpButton);
    JButton closeButton = new JButton("Close");
    closeButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        dispose();
      }
    });
    buttonsBox.add(closeButton);
    add(buttonsBox);
    pack();
    setLocationRelativeTo(getOwner());
  }

  private void updateGames() {
    updateButton.setEnabled(false);
    Runnable runnable = new Runnable() {
      public void run() {
        for (int i=0,n=savedGamesModel.size();i<n;++i) {
          try {
            File savedGame = (File)savedGamesModel.getElementAt(i);
            updater.updateSavedGame(oldPieceInfo,savedGame);
            GameModule.getGameModule().warn("Updated "+savedGame.getName()+" from version "+versionField.getText()+" to "+GameModule.getGameModule().getGameVersion());
          }
          // FIXME: review error message
          catch (final IOException e) {
            Runnable showError = new Runnable() {
              public void run() {
                showErrorMessage(e,"Update failed","Unable to save file");
              }
            };
            try {
              SwingUtilities.invokeAndWait(showError);
            }
            // FIXME: review error message
            catch (InterruptedException e1) {
            }
            // FIXME: review error message
            catch (InvocationTargetException e1) {
            }
          }
        }
        updateButton.setEnabled(true);
      }
    };
    new Thread(runnable).start();
  }

  private void chooseSavedGames() {
    fc.setMultiSelectionEnabled(true);
    if (JFileChooser.CANCEL_OPTION != fc.showOpenDialog(this)) {
      File[] selectedFiles = fc.getSelectedFiles();
      if (selectedFiles != null) {
        savedGamesModel.clear();
        for (int i = 0; i < selectedFiles.length; i++) {
          savedGamesModel.addElement(selectedFiles[i]);
        }
      }
    }
  }

  private void exportPieceInfo() {
    fc.setMultiSelectionEnabled(false);
    if (JFileChooser.CANCEL_OPTION != fc.showSaveDialog(this)) {
      final Properties p = updater.getPieceSlotsMap();
      p.put(MODULE_NAME_KEY, GameModule.getGameModule().getGameName());
      p.put(VERSION_KEY, GameModule.getGameModule().getGameVersion());

      BufferedOutputStream out = null;
      try {
        out = new BufferedOutputStream(
                new FileOutputStream(fc.getSelectedFile()));
        p.store(out, null);
        out.close();
      }
      // FIXME: review error message
      catch (IOException e) {
        showErrorMessage(e, "Export failed","Unable to write info");
      }
      finally {
        IOUtils.closeQuietly(out);
      }
    }
  }

  private void importPieceInfo() {
    fc.setMultiSelectionEnabled(false);
    if (JFileChooser.CANCEL_OPTION != fc.showOpenDialog(this)) {
      oldPieceInfo = new Properties();

      BufferedInputStream in = null;
      try { 
        in = new BufferedInputStream(new FileInputStream(fc.getSelectedFile()));
        oldPieceInfo.load(in);
        in.close();
      
        String moduleVersion = oldPieceInfo.getProperty(VERSION_KEY);
        String moduleName = oldPieceInfo.getProperty(MODULE_NAME_KEY);
        if (!GameModule.getGameModule().getGameName().equals(moduleName)) {
          showErrorMessage(null,"Import failed","Imported info is from the wrong module:  "+moduleName);
          oldPieceInfo = null;
          versionField.setText(null);
        }
        else if (GameModule.getGameModule().getGameVersion().equals(moduleVersion)) {
          showErrorMessage(null,"Import failed","Imported info is from the current version, "+moduleVersion+".\nLoad the older version in the editor and export the GamePiece info,\nThen load this module again and import the older version's info");
          oldPieceInfo = null;
          versionField.setText(null);
        }
        else {
          versionField.setText(moduleVersion);
        }
      }
      // FIXME: review error message
      catch (IOException e) {
        showErrorMessage(e,"Import failed","Unable to import info");
        oldPieceInfo = null;
      }
      catch (IllegalArgumentException e) { // catches malformed input files
        showErrorMessage(e, "Import failed", "Malformed input file");
        oldPieceInfo = null;
      }
      finally {
        IOUtils.closeQuietly(in);
      }
    }
    updateButton.setEnabled(oldPieceInfo != null);
  }

  private void showErrorMessage(Exception e, String title, String defaultMessage) {
    String msg = e == null ? null : e.getMessage();
    if (msg == null) {
      msg = defaultMessage;
    }
    JOptionPane.showMessageDialog(this, msg, title, JOptionPane.ERROR_MESSAGE);
  }
}
