/**
 * Opens an editor and connects a listener.
 */
/*
    Copyright 2016-2018 Will Winder

    This file is part of Universal Gcode Sender (UGS).

    UGS 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 3 of the License, or
    (at your option) any later version.

    UGS 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 UGS.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.willwinder.ugs.nbp.editor;

import com.willwinder.ugs.nbp.lib.lookup.CentralLookup;
import com.willwinder.universalgcodesender.listeners.UGSEventListener;
import com.willwinder.universalgcodesender.model.BackendAPI;
import com.willwinder.universalgcodesender.model.UGSEvent;
import com.willwinder.universalgcodesender.model.UGSEvent.FileState;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JEditorPane;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.cookies.EditorCookie;
import org.openide.cookies.OpenCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.nodes.Node;
import org.openide.util.*;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent;

@ActionID(
  category = "File",
  id = "com.willwinder.ugs.nbp.editor.EditGcodeFile"
)
@ActionRegistration(
  displayName = "#CTL_EditGcodeFile",
  lazy = false
)
@ActionReferences({
  @ActionReference(path = "Menu/File", position = 1301),
  @ActionReference(path="Shortcuts", name="M-E")
})
@Messages("CTL_EditGcodeFile=Edit Gcode File...")
public final class EditGcodeFile extends AbstractAction implements ContextAwareAction, UGSEventListener {
    private final EditorListener editorListener;
    private final BackendAPI backend;
    
    public EditGcodeFile() {
      putValue(Action.NAME, org.openide.util.NbBundle.getMessage(EditGcodeFile.class, "EditGcodeFile.action.name")); // NOI18N

      backend = CentralLookup.getDefault().lookup(BackendAPI.class);
      backend.addUGSEventListener(this);

      editorListener = new EditorListener();
    }

    /**
     * If an editor is open and the file has changed, close the editor and open
     * a new one with the new file.
     */
    @Override
    public void UGSEvent(UGSEvent evt) {
      if (evt.isFileChangeEvent() && evt.getFileState() == FileState.FILE_LOADING) {
        if (backend == null || backend.getGcodeFile() == null) return;

        java.awt.EventQueue.invokeLater(() -> {
          if (getCurrentlyOpenedEditors().isEmpty()) return;

          openFile(backend.getGcodeFile());
        });
      }
    }

    @Override
    public Action createContextAwareInstance(Lookup actionContext) {
        return new EditGcodeFile();
    }

    @Override
    public boolean isEnabled() {
      return backend.getGcodeFile() != null;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
      if (backend == null || backend.getGcodeFile() == null) return;

      openFile(backend.getGcodeFile());
    }

    /**
     * Open an Editor Window in the application, ensuring that only one editor
     * is ever opened at the same time.
     */
    private void openFile(File f) {
      // Close any opened file.
      closeOpenFile();

      try {
        FileObject fo = FileUtil.toFileObject(backend.getGcodeFile());
        DataObject dOb = DataObject.find(fo);
        dOb.getLookup().lookup(OpenCookie.class).open();
        java.awt.EventQueue.invokeLater(() -> updateListener(true));
      } catch (DataObjectNotFoundException ex) {
        Exceptions.printStackTrace(ex);
      }
    }

    private void closeOpenFile() {
      updateListener(false);
      Collection<TopComponent> editors = getCurrentlyOpenedEditors();
      for (TopComponent editor : editors) {
        editor.close();
      }
    }
    
    /**
     * Get all the windows in the "Editor" mode, then filter to just editors.
     */
    private Collection<TopComponent> getCurrentlyOpenedEditors() {
      final ArrayList<TopComponent> result = new ArrayList<>();
      Collection<TopComponent> comps = TopComponent.getRegistry().getOpened();
      for (TopComponent tc: comps) {
        Node[] arr = tc.getActivatedNodes();
        for (int j = 0; arr != null && j < arr.length; j++) {
          EditorCookie ec = arr[j].getCookie(EditorCookie.class);
          if (ec != null) {
            result.add(tc);
          }
        }
      }
      return result;
    }

    private void updateListener(Boolean enabled) {
      Collection<TopComponent> comps = TopComponent.getRegistry().getOpened();
      for (TopComponent tc: comps) {
        Node[] arr = tc.getActivatedNodes();
        for (int j = 0; arr != null && j < arr.length; j++) {
          EditorCookie ec = arr[j].getCookie(EditorCookie.class);
          if (ec != null) {
            JEditorPane[] panes = ec.getOpenedPanes();
            for (JEditorPane pane : panes) {
              if (enabled) {
                pane.addCaretListener(editorListener);
              } else {
                pane.removeCaretListener(editorListener);
              }
            }
          }
        }
      }
    }
}
