/*
 * File    : MediaBagEditor.java
 * Created : 17-sep-2002 16:30
 * 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.bags;

import edu.xtec.jclic.Constants;
import edu.xtec.jclic.edit.*;
import edu.xtec.jclic.fileSystem.FileSystem;
import edu.xtec.jclic.fileSystem.ZipFileSystem;
import edu.xtec.jclic.misc.Utils;
import edu.xtec.jclic.project.JClicProjectEditor;
import edu.xtec.util.Messages;
import edu.xtec.util.Options;
import edu.xtec.util.ProgressDialog;
import edu.xtec.util.StrUtils;
import edu.xtec.util.StreamIO;
import java.awt.Color;
import java.awt.Component;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JComponent;

/**
 * @author Francesc Busquets (fbusquets@xtec.cat)
 * @version 13.09.16
 */
public class MediaBagEditor extends Editor {

  public static final int DEFAULT_IMG_MAX_WIDTH = 800;
  public static final int DEFAULT_IMG_MAX_HEIGHT = 600;
  public static final int IMG_MIN_SIZE = 10;
  public static final String RESIZED = "resized";

  public static boolean actionsCreated;
  private boolean initializing;
  public static EditorAction newMediaBagElementAction, updateAllMediaAction, exportAllMediaAction;
  private static Icon icon;
  protected static int imgMaxWidth = DEFAULT_IMG_MAX_WIDTH;
  protected static int imgMaxHeight = DEFAULT_IMG_MAX_HEIGHT;

  /** Creates a new instance of MediaBagEditor */
  public MediaBagEditor(MediaBag mb) {
    super(mb);
  }

  public Options getOptions() {
    return getMediaBag().getProject().getBridge().getOptions();
  }

  protected void createChildren() {
    initializing = true;
    Iterator it = getMediaBag().getElementsByName().iterator();
    while (it.hasNext()) {
      ((MediaBagElement) it.next()).getEditor(this);
    }
    initializing = false;
  }

  public EditorPanel createEditorPanel(Options options) {
    return new MediaBagMultiEditorPanel(options);
  }

  public Class getEditorPanelClass() {
    return MediaBagMultiEditorPanel.class;
  }

  @Override
  public String getTitleKey() {
    return "edit_media";
  }

  public MediaBag getMediaBag() {
    return (MediaBag) getUserObject();
  }

  public JClicProjectEditor getProjectEditor() {
    return (JClicProjectEditor) getFirstParent(JClicProjectEditor.class);
  }

  public static String[] selectMediaFiles(MediaBag mediaBag, Options options, Component dlgOwner, int fileFilters,
      String defaultFile) {
    String[] result;
    FileSystem fs = mediaBag.getProject().getFileSystem();
    int[] filters = new int[] { Utils.ALL_IMAGES_FF, Utils.ALL_SOUNDS_FF, Utils.MIDI_FF, Utils.ALL_VIDEO_FF,
        Utils.ALL_ANIM_FF, Utils.FONTS_FF, Utils.SKINS_FF, Utils.ALL_MEDIA_FF };
    result = fs.chooseFiles(defaultFile, false, filters, options, "edit_find_media", dlgOwner, true, true);
    if (result != null) {
      for (int i = 0; i < result.length; i++) {
        if (result[i] != null) {
          result[i] = result[i].replace(File.separatorChar, '/');
        }
      }
    }
    return result;
  }

  public String getValidMediaName(String suggestedName) {
    String name = suggestedName;
    int i = 1;
    while (getMediaBag().getElement(name) != null) {
      name = name + "_" + i++;
    }
    return name;
  }

  public MediaBagElementEditor addMediaBagElement(MediaBagElement mbe) {
    MediaBagElementEditor result = null;
    if (getMediaBag().addElement(mbe)) {
      result = (MediaBagElementEditor) mbe.getEditor(this);
      fireEditorDataChanged(null);
    }
    return result;
  }

  public MediaBagElementEditor[] createNewMediaBagElements(Options options, Component dlgOwner, int fileFilters) {
    MediaBagElementEditor[] result = null;
    Messages msg = options.getMessages();
    MediaBag mb = getMediaBag();
    FileSystem fs = mb.getProject().getFileSystem();
    String[] fNames = selectMediaFiles(getMediaBag(), options, dlgOwner, fileFilters, null);
    if (fNames != null) {
      List<MediaBagElementEditor> v = new ArrayList<MediaBagElementEditor>();
      for (int i = 0; i < fNames.length; i++) {
        if (mb.getElementByFileName(fNames[i]) != null) {
          msg.showAlert(dlgOwner,
              new String[] { msg.get("edit_media_exists_1"), fNames[i], msg.get("edit_media_exists_2") });
        } else {
          MediaBagElement mbe = new MediaBagElement(fNames[i], null, getValidMediaName(fNames[i]));
          boolean cancel = false;

          // Check image size
          if (mbe.isImage()) {
            Image img = null;
            try {
              img = mbe.prepareAndGetImage(fs);
              if (img == null) {
                throw new Exception("invalid image!");
              }
            } catch (Exception ex) {
              msg.showErrorWarning(dlgOwner, "err_reading_data", fNames[i], ex, null);
              cancel = true;
            }

            if (img != null) {
              int imgWidth = img.getWidth(null);
              int imgHeight = img.getHeight(null);

              if ((imgWidth > imgMaxWidth || imgHeight > imgMaxHeight)) {
                int answer = msg.showQuestionDlgObj(
                    dlgOwner, new String[] { StrUtils.replace(msg.get("img_resize_prompt"), "%s", new String[] {
                        fNames[i], "" + imgWidth + "x" + imgHeight, "" + imgMaxWidth + "x" + imgMaxHeight }) },
                    "CONFIRM", "ync");

                if (answer == Messages.CANCEL) {
                  cancel = true;
                } else if (answer == Messages.YES) {

                  // TODO: Process can be long. Display wait cursor
                  String baseName = fNames[i];
                  int lastDot = baseName.lastIndexOf(".");
                  if (lastDot > 0) {
                    baseName = baseName.substring(0, lastDot);
                  }

                  String newName = baseName + "-" + RESIZED + ".jpg";
                  File destFile = new File(fs.getFullFileNamePath(newName));
                  int counter = 0;
                  while (destFile.exists()) {
                    newName = baseName + "-" + RESIZED + Integer.toString(++counter) + ".jpg";
                    destFile = new File(fs.getFullFileNamePath(newName));
                  }

                  try {
                    javax.swing.ImageIcon resizedImg = mbe.getThumbNail(imgMaxWidth, imgMaxHeight, fs);
                    BufferedImage bimg = Utils.toBufferedImage(resizedImg.getImage(), Color.white, null);
                    javax.imageio.ImageIO.write(bimg, "jpg", destFile);
                    fNames[i] = newName;
                    mbe = new MediaBagElement(fNames[i], null, getValidMediaName(fNames[i]));
                  } catch (Exception ex) {
                    cancel = true;
                    msg.showErrorWarning(dlgOwner, "err_file_save", ex);
                  }
                }
              }
            }
          }

          if (!cancel && mb.addElement(mbe)) {
            v.add((MediaBagElementEditor) mbe.getEditor(this));
          }
        }
      }
      if (v.size() > 0) {
        result = v.toArray(new MediaBagElementEditor[v.size()]);
        fireEditorDataChanged(null);
      }
    }
    return result;
  }

  public void updateAllElements(JComponent parent) {
    final ProgressDialog progressDialog = new ProgressDialog(parent, getOptions());
    edu.xtec.util.SwingWorker sw = new edu.xtec.util.SwingWorker() {

      @Override
      public Object construct() {
        FileSystem fs = getMediaBag().getProject().getFileSystem();
        ZipFileSystem zfs = (fs instanceof ZipFileSystem) ? (ZipFileSystem) fs : null;
        int count = 0, updated = 0;
        progressDialog.setProgressMax(getChildCount());
        progressDialog.setProgressValue(count);

        Enumeration en = children();
        while (en.hasMoreElements() && !isCancelled()) {
          MediaBagElementEditor mbeled = (MediaBagElementEditor) en.nextElement();
          String fName = mbeled.getMediaBagElement().getFileName();
          progressDialog.setFileLabel(fName);
          if (fs.realFileExists(fName)) {
            if (zfs != null) {
              ZipFileSystem.ExtendedZipEntry ze = zfs.getEntry(fName);
              if (ze != null && zfs.fileExists(fName)) {
                ze.ignore = true;
              }
            }
            mbeled.updateContent(null);
            updated++;
          }
          progressDialog.setProgressValue(++count);
        }
        progressDialog.setFileLabel(null);
        if (updated > 0) {
          setModified(true);
          fireEditorDataChanged(null);
        }
        return null;
      }

      @Override
      public void finished() {
        progressDialog.setVisible(false);
      }
    };
    progressDialog.start("edit_media_refreshAll", "edit_media_refreshAll_working", sw, true, true, false);
  }

  public void exportAllElements(final JComponent parent) {
    final Options options = getOptions();
    final ProgressDialog progressDialog = new ProgressDialog(parent, options);
    edu.xtec.util.SwingWorker sw = new edu.xtec.util.SwingWorker() {

      @Override
      public Object construct() {
        FileSystem fs = getMediaBag().getProject().getFileSystem();
        ZipFileSystem zfs = (fs instanceof ZipFileSystem) ? (ZipFileSystem) fs : null;
        String dlgMsg = options.getMessages().get("filesystem_copyFile");
        int count = 0;
        progressDialog.setProgressMax(getChildCount());
        progressDialog.setProgressValue(count);
        Enumeration en = children();
        boolean overwriteAll = false;
        boolean overwriteNone = false;
        while (en.hasMoreElements() && !isCancelled()) {
          MediaBagElementEditor mbeled = (MediaBagElementEditor) en.nextElement();
          String fName = mbeled.getMediaBagElement().getFileName();
          if (zfs != null) {
            ZipFileSystem.ExtendedZipEntry ze = zfs.getEntry(fName);
            if (ze != null && !ze.ignore) {
              progressDialog.setFileLabel(fName);
              String fNameDest = zfs.getFullFileNamePath(fName);
              File fileDest = new File(fNameDest);
              boolean prompt = true;
              if (fileDest.exists()) {
                if (overwriteNone) {
                  continue;
                } else if (!overwriteAll) {
                  boolean next = false;
                  boolean cancel = false;
                  switch (options.getMessages().confirmOverwriteFile(parent, fileDest, "yYnNc")) {
                  case Messages.NO_TO_ALL:
                    overwriteNone = true;
                  case Messages.NO:
                    next = true;
                    break;
                  case Messages.YES_TO_ALL:
                    overwriteAll = true;
                  case Messages.YES:
                    break;
                  default:
                    cancel = true;
                  }
                  if (next) {
                    continue;
                  } else if (cancel) {
                    break;
                  }
                }
              }
              try {
                OutputStream os = zfs.createSecureFileOutputStream(fNameDest, false);
                InputStream is = zfs.getInputStream(fName);
                StreamIO.writeStreamTo(is, os);
              } catch (Exception ex) {
                options.getMessages().showErrorWarning(progressDialog, "FILE_ERR_SAVING", ex);
                break;
              }
            }
            progressDialog.setProgressValue(++count);
          }
        }
        progressDialog.setFileLabel(null);
        return null;
      }

      @Override
      public void finished() {
        progressDialog.setVisible(false);
      }
    };
    progressDialog.start("edit_media_refreshAll", "edit_media_refreshAll_working", sw, true, true, false);
  }

  @Override
  public void setActionsOwner() {
    allowDelete = allowCut = allowCopy = allowPaste = false;
    super.setActionsOwner();
    if (actionsCreated) {
      newMediaBagElementAction.setActionOwner(this);
      FileSystem fs = getMediaBag().getProject().getFileSystem();
      boolean isZipFileSystem = (fs instanceof ZipFileSystem);
      updateAllMediaAction.setActionOwner(isZipFileSystem ? this : null);
      exportAllMediaAction.setActionOwner(isZipFileSystem ? this : null);
    }
  }

  @Override
  public void clearActionsOwner() {
    super.clearActionsOwner();
    if (actionsCreated) {
      newMediaBagElementAction.setActionOwner(null);
      updateAllMediaAction.setActionOwner(null);
      exportAllMediaAction.setActionOwner(null);
    }
  }

  public static void createActions(Options opt) {
    createBasicActions(opt);
    if (!actionsCreated) {
      newMediaBagElementAction = new EditorAction("edit_media_new", "icons/media_new.gif", "edit_media_new_tooltip",
          opt) {
        protected void doAction(Editor e) {
          Editor ch = null;
          if (e instanceof MediaBagElementEditor) {
            ch = e;
            e = e.getEditorParent();
          }
          if (e instanceof MediaBagEditor) {
            EditorPanel ep = getEditorPanelSrc();
            MediaBagMultiEditorPanel mbep = null;
            if (ep instanceof MediaBagMultiEditorPanel) {
              mbep = (MediaBagMultiEditorPanel) ep;
            }
            int filters = (mbep == null ? -1 : mbep.getFilters());
            MediaBagElementEditor[] mbed = ((MediaBagEditor) e).createNewMediaBagElements(options, getComponentSrc(),
                filters);
            if (mbed != null && mbed.length > 0 && mbep != null) {
              mbep.setSelected(mbed[0]);
            }
          }
        }
      };
      updateAllMediaAction = new EditorAction("edit_media_refreshAll", "icons/reset_all.gif",
          "edit_media_refreshAll_tooltip", opt) {
        protected void doAction(Editor e) {
          if (e instanceof MediaBagEditor) {
            ((MediaBagEditor) e).updateAllElements(getJComponentSrc());
          }
        }
      };
      exportAllMediaAction = new EditorAction("edit_media_exportAll", "icons/file_save_all.gif",
          "edit_media_exportAll_tooltip", opt) {
        protected void doAction(Editor e) {
          if (e instanceof MediaBagEditor) {
            ((MediaBagEditor) e).exportAllElements(getJComponentSrc());
          }
        }
      };
      actionsCreated = true;
    }
  }

  public MediaBagElementEditor getChildFor(MediaBagElement mbe) {
    MediaBagElementEditor result = null;
    if (mbe != null) {
      Enumeration en = children();
      while (en.hasMoreElements()) {
        Editor e = (Editor) en.nextElement();
        if (e.getUserObject() == mbe) {
          result = (MediaBagElementEditor) e;
          break;
        }
      }
    }
    return result;
  }

  public List<MediaBagElementEditor> getChildrenList(int filters) {
    List<MediaBagElementEditor> v = new ArrayList<MediaBagElementEditor>();
    Iterator it = getMediaBag().getElementsByName().iterator();
    String[] extFilters = Utils.getFileFilterExtensions(filters);
    while (it.hasNext()) {
      MediaBagElement mbe = (MediaBagElement) it.next();
      int i = 0;
      String s = mbe.getFileName().toLowerCase();
      if (extFilters != null) {
        while (i < extFilters.length && !s.endsWith(extFilters[i])) {
          i++;
        }
      }

      if (extFilters == null || i < extFilters.length) {
        MediaBagElementEditor mbed = getChildFor(mbe);
        if (mbed != null) {
          v.add(mbed);
        }
      }
    }
    return v;
  }

  public boolean nameChanged(int type, String oldName, String newName) {
    boolean result = false;
    if ((type & Constants.T_MEDIA) != 0) {
      Enumeration e = children();
      while (e.hasMoreElements()) {
        result |= ((MediaBagElementEditor) e.nextElement()).nameChanged(type, oldName, newName);
      }
    }
    return result;
  }

  public static Icon getIcon() {
    if (icon == null) {
      icon = edu.xtec.util.ResourceManager.getImageIcon("icons/media_bag.gif");
    }
    return icon;
  }

  public int checkOrphanElements(Options options, Component parent, boolean prompt) {
    int result = Messages.YES;
    List<MediaBagElementEditor> v = new ArrayList<MediaBagElementEditor>();
    Enumeration en = children();
    while (en.hasMoreElements()) {
      MediaBagElementEditor mbed = (MediaBagElementEditor) en.nextElement();
      if (mbed.listReferences().isEmpty()) {
        v.add(mbed);
      }
    }
    if (!v.isEmpty()) {
      boolean doIt = !prompt;
      if (!doIt) {
        Object[] object = new Object[] { options.getMsg("edit_project_orphanMedia"),
            v.size() > 10 ? (Object) (new javax.swing.JScrollPane(new javax.swing.JList<Object>(v.toArray())))
                : (Object) v,
            options.getMsg("edit_project_orphanMedia_prompt"), };
        result = options.getMessages().showQuestionDlgObj(parent, object, "edit_project_orphanMedia_title", "ync");
        doIt = (result == Messages.YES);
      }
      if (doIt) {
        Iterator<MediaBagElementEditor> it = v.iterator();
        while (it.hasNext()) {
          it.next().delete(true);
        }
      }
    }
    return result;
  }

  public static int getImgMaxWidth() {
    return imgMaxWidth;
  }

  public static void setImgMaxWidth(int width) {
    imgMaxWidth = Math.max(IMG_MIN_SIZE, width);
  }

  public static int getImgMaxHeight() {
    return imgMaxHeight;
  }

  public static void setImgMaxHeight(int height) {
    imgMaxHeight = Math.max(IMG_MIN_SIZE, height);
  }
}
