/*
 * File    : ProjectInstallerEditPanel.java
 * Created : 02-sep-2004 12:08
 * By      : fbusquets
 *
 * JClic - Authoring and playing system for educational activities
 *
 * Copyright (C) 2000 - 2018 Francesc Busquets & Departament
 * d'Educacio de la Generalitat de Catalunya
 *
 * 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 (see the LICENSE file).
 */

package edu.xtec.jclic.project;

import edu.xtec.jclic.fileSystem.FileSystem;
import edu.xtec.jclic.misc.Utils;
import edu.xtec.util.JDomUtility;
import edu.xtec.util.Messages;
import edu.xtec.util.Options;
import edu.xtec.util.StrUtils;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * @author Francesc Busquets (fbusquets@xtec.cat)
 * @version 13.09.17
 */
public class ProjectInstallerEditPanel extends javax.swing.JPanel implements ListSelectionListener, ActionListener {

  Options options;
  DefaultListModel<Object> availModel, filesModel;

  /** Creates new form ProjectInstallerEditPanel */
  public ProjectInstallerEditPanel(Options options) {
    this.options = options;
    availModel = new DefaultListModel<Object>();
    filesModel = new DefaultListModel<Object>();
    initComponents();
    availFiles.addListSelectionListener(this);
    includedFiles.addListSelectionListener(this);
    addBtn.addActionListener(this);
    removeBtn.addActionListener(this);
    checkButtons();
  }

  /**
   * This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is always
   * regenerated by the Form Editor.
   */
  // <editor-fold defaultstate="collapsed" desc="Generated
  // Code">//GEN-BEGIN:initComponents
  private void initComponents() {
    java.awt.GridBagConstraints gridBagConstraints;

    javax.swing.JLabel titleLb = new javax.swing.JLabel();
    titleFld = new javax.swing.JTextField();
    javax.swing.JLabel authLb = new javax.swing.JLabel();
    authFld = new javax.swing.JTextField();
    javax.swing.JLabel folderLb = new javax.swing.JLabel();
    folderFld = new javax.swing.JTextField();
    javax.swing.JPanel filesPanel = new javax.swing.JPanel();
    availFilesSP = new javax.swing.JScrollPane();
    availFiles = new javax.swing.JList<>();
    addBtn = new javax.swing.JButton();
    removeBtn = new javax.swing.JButton();
    includedFilesSP = new javax.swing.JScrollPane();
    includedFiles = new javax.swing.JList<>();
    javax.swing.JPanel buttonPanel = new javax.swing.JPanel();
    javax.swing.JLabel projectLb = new javax.swing.JLabel();
    projectFld = new javax.swing.JTextField();
    javax.swing.JLabel txtLb = new javax.swing.JLabel();
    textFld = new javax.swing.JTextField();
    javax.swing.JLabel iconLb = new javax.swing.JLabel();
    iconFld = new javax.swing.JTextField();
    javax.swing.JLabel descLb = new javax.swing.JLabel();
    descFld = new javax.swing.JTextField();

    setLayout(new java.awt.GridBagLayout());

    titleLb.setLabelFor(titleFld);
    titleLb.setText(options.getMsg("installer_title"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    add(titleLb, gridBagConstraints);

    titleFld.setToolTipText(options.getMsg("installer_title_tooltip"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    add(titleFld, gridBagConstraints);

    authLb.setLabelFor(authFld);
    authLb.setText(options.getMsg("installer_authors"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    add(authLb, gridBagConstraints);

    authFld.setToolTipText(options.getMsg("installer_authors_tooltip"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    add(authFld, gridBagConstraints);

    folderLb.setLabelFor(folderFld);
    folderLb.setText(options.getMsg("installer_folder"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    add(folderLb, gridBagConstraints);

    folderFld.setToolTipText(options.getMsg("installer_folder_tooltip"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    add(folderFld, gridBagConstraints);

    filesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(options.getMsg("installer_files")));
    filesPanel.setToolTipText(options.getMsg("installer_files_tooltip"));
    filesPanel.setLayout(new java.awt.GridBagLayout());

    availFilesSP.setPreferredSize(new java.awt.Dimension(120, 100));

    availFiles.setModel(availModel);
    availFiles.setToolTipText(options.getMsg("installer_files_available"));
    availFilesSP.setViewportView(availFiles);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 0.5;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    filesPanel.add(availFilesSP, gridBagConstraints);

    addBtn.setToolTipText(options.getMsg("installer_files_add_tooltip"));
    addBtn.setText(options.getMsg("installer_files_add"));
    addBtn.setEnabled(false);
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    filesPanel.add(addBtn, gridBagConstraints);

    removeBtn.setToolTipText(options.getMsg("installer_files_tooltip"));
    removeBtn.setText(options.getMsg("installer_files_remove"));
    removeBtn.setEnabled(false);
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
    gridBagConstraints.weighty = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    filesPanel.add(removeBtn, gridBagConstraints);

    includedFilesSP.setPreferredSize(new java.awt.Dimension(120, 100));

    includedFiles.setModel(filesModel);
    includedFiles.setToolTipText(options.getMsg("installer_files_selected"));
    includedFilesSP.setViewportView(includedFiles);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 2;
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 0.5;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    filesPanel.add(includedFilesSP, gridBagConstraints);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weighty = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    add(filesPanel, gridBagConstraints);

    buttonPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(options.getMsg("installer_button")));
    buttonPanel.setToolTipText(options.getMsg("installer_button_tooltip"));
    buttonPanel.setLayout(new java.awt.GridBagLayout());

    projectLb.setLabelFor(projectFld);
    projectLb.setText(options.getMsg("installer_button_project"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    buttonPanel.add(projectLb, gridBagConstraints);

    projectFld.setToolTipText(options.getMsg("installer_button_project_tooltip"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    buttonPanel.add(projectFld, gridBagConstraints);

    txtLb.setLabelFor(textFld);
    txtLb.setText(options.getMsg("installer_button_text"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    buttonPanel.add(txtLb, gridBagConstraints);

    textFld.setToolTipText(options.getMsg("installer_button_text_tooltip"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    buttonPanel.add(textFld, gridBagConstraints);

    iconLb.setLabelFor(iconFld);
    iconLb.setText(options.getMsg("installer_button_icon"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    buttonPanel.add(iconLb, gridBagConstraints);

    iconFld.setToolTipText(options.getMsg("installer_button_icon_tooltip"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    buttonPanel.add(iconFld, gridBagConstraints);

    descLb.setLabelFor(descFld);
    descLb.setText(options.getMsg("installer_button_description"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    buttonPanel.add(descLb, gridBagConstraints);

    descFld.setToolTipText(options.getMsg("installer_button_description_tooltip"));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    buttonPanel.add(descFld, gridBagConstraints);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
    add(buttonPanel, gridBagConstraints);
  } // </editor-fold>//GEN-END:initComponents

  private String suggestedFileName;

  public void setValues(JClicProject prj) {
    suggestedFileName = null;
    ProjectInstaller pi = null;
    ProjectInstaller.IconItem ii;
    FileSystem fs = prj.getFileSystem();
    File base = new File(FileSystem.sysFn(fs.getFullFileNamePath(null)));
    try {
      File[] files = base.listFiles(Utils.getFileFilter(Utils.INSTALL_FF, options.getMessages()));
      if (files != null) {
        for (int i = 0; i < files.length; i++) {
          if (files[i].isFile()) {
            suggestedFileName = files[i].getName();
            pi = ProjectInstaller.getProjectInstaller(fs.getFullFileNamePath(suggestedFileName));
            break;
          }
        }
      }
    } catch (Exception ex) {
      System.err.println("Error reading installer: " + ex);
    }
    if (suggestedFileName == null) {
      suggestedFileName = FileSystem.getValidFileName(prj.getName().trim()) + ".jclic.inst";
    }
    if (pi == null) {
      pi = new ProjectInstaller();
      pi.projectTitle = prj.settings.title;
      pi.baseFolder = base.getName();
      StringBuilder sb = new StringBuilder();
      if (prj.settings.authors != null) {
        for (int i = 0; i < prj.settings.authors.length; i++) {
          if (i > 0)
            sb.append(", ");
          sb.append(prj.settings.authors[i].name);
        }
      }
      pi.authors = sb.substring(0);
      String projectFileName = prj.getFullPath();
      if (projectFileName != null)
        projectFileName = FileSystem.getFileNameOf(projectFileName);
      else
        projectFileName = prj.getName() + Utils.EXT_JCLIC_ZIP;
      pi.addFile(projectFileName);
      ii = pi.createIconItem();
      ii.project = projectFileName;
      ii.text = prj.settings.title;
      ii.icon = prj.settings.iconFileName;
    }

    titleFld.setText(StrUtils.secureString(pi.projectTitle));
    authFld.setText(StrUtils.secureString(pi.authors));
    folderFld.setText(StrUtils.secureString(pi.baseFolder));

    availModel.removeAllElements();
    List<String> v = new ArrayList<String>();
    FileSystem.exploreFiles(null, base, v, '/', null, 3, 300);
    for (String v1 : v)
      availModel.addElement(v1);

    filesModel.removeAllElements();
    for (int i = 0; i < pi.files.size(); i++) {
      String s = pi.getFile(i);
      if (availModel.contains(s))
        availModel.removeElement(s);
      filesModel.addElement(s);
    }

    ii = pi.getIconItem(0);
    if (ii != null) {
      projectFld.setText(StrUtils.secureString(ii.project));
      textFld.setText(StrUtils.secureString(ii.text));
      iconFld.setText(StrUtils.secureString(ii.icon));
      descFld.setText(StrUtils.secureString(ii.description));
    }
    checkButtons();
  }

  private void checkButtons() {
    addBtn.setEnabled(!availFiles.isSelectionEmpty());
    removeBtn.setEnabled(!includedFiles.isSelectionEmpty());
  }

  public void valueChanged(ListSelectionEvent e) {
    if (!e.getValueIsAdjusting())
      checkButtons();
  }

  public void actionPerformed(ActionEvent ev) {
    if (ev != null && ev.getSource() != null) {
      DefaultListModel<Object> src = null, dest = null;
      JList list = null;
      if (ev.getSource() == addBtn) {
        src = availModel;
        dest = filesModel;
        list = availFiles;
      } else if (ev.getSource() == removeBtn) {
        dest = availModel;
        src = filesModel;
        list = includedFiles;
      }
      if (dest != null && src != null & list != null) {
        Object[] values = list.getSelectedValuesList().toArray();
        if (values != null) {
          for (int i = 0; i < values.length; i++) {
            src.removeElement(values[i]);
            dest.addElement(values[i]);
          }
        }
        checkButtons();
      }
    }
  }

  public ProjectInstaller getProjectInstaller(Component parent) {

    ProjectInstaller result = new ProjectInstaller();
    String errorKey = null;

    result.projectTitle = StrUtils.nullableString(titleFld.getText());
    result.authors = StrUtils.nullableString(authFld.getText());
    result.baseFolder = StrUtils.nullableString(folderFld.getText());

    int numFiles = filesModel.getSize();
    for (int i = 0; i < numFiles; i++)
      result.addFile((String) filesModel.get(i));

    ProjectInstaller.IconItem ii = result.createIconItem();
    ii.project = StrUtils.nullableString(projectFld.getText());
    ii.text = StrUtils.nullableString(textFld.getText());
    ii.icon = StrUtils.nullableString(iconFld.getText());
    ii.description = StrUtils.nullableString(descFld.getText());

    if (result.projectTitle == null)
      errorKey = "noTitle";
    else if (result.baseFolder == null)
      errorKey = "noBaseFolder";
    else if (numFiles == 0)
      errorKey = "noFilesSelected";
    else if (ii.project == null)
      errorKey = "noProjectSelected";
    else if (ii.text == null)
      errorKey = "noButtonText";
    else if (!result.files.contains(ii.project))
      errorKey = "projectNotInFiles";

    if (errorKey != null)
      options.getMessages().showErrorWarning(parent, "installer_error_" + errorKey, null);

    return errorKey != null ? null : result;
  }

  public static void createInstaller(JClicProject prj, Component parent) {
    Options options = prj.getBridge().getOptions();
    Messages msg = options.getMessages();
    ProjectInstallerEditPanel panel = new ProjectInstallerEditPanel(options);
    panel.setValues(prj);
    boolean exit = false;
    while (!exit) {
      if (msg.showInputDlg(parent, panel, "installer_caption")) {
        ProjectInstaller result = panel.getProjectInstaller(parent);
        if (result != null) {
          int[] filters = { Utils.INSTALL_FF };
          FileSystem fs = prj.getFileSystem();
          String fileName = fs.chooseFile(panel.suggestedFileName, true, filters, options, null, parent, false);
          if (fileName != null) {
            try {
              FileOutputStream fos = fs.createSecureFileOutputStream(fileName, false);
              JDomUtility.saveDocument(fos, new org.jdom.Document(result.getJDomElement(false)));
              fos.close();
              exit = true;
            } catch (Exception ex) {
              options.getMessages().showErrorWarning(parent, null, ex);
            }
          } else
            exit = true;
        }
      } else
        exit = true;
    }
  }

  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton addBtn;
  private javax.swing.JTextField authFld;
  private javax.swing.JList<Object> availFiles;
  private javax.swing.JScrollPane availFilesSP;
  private javax.swing.JTextField descFld;
  private javax.swing.JTextField folderFld;
  private javax.swing.JTextField iconFld;
  private javax.swing.JList<Object> includedFiles;
  private javax.swing.JScrollPane includedFilesSP;
  private javax.swing.JTextField projectFld;
  private javax.swing.JButton removeBtn;
  private javax.swing.JTextField textFld;
  private javax.swing.JTextField titleFld;
  // End of variables declaration//GEN-END:variables

}
