/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */


package org.netbeans.core.windows.view;



import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.*;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.border.Border;
import org.netbeans.core.windows.*;
import org.netbeans.core.windows.view.dnd.*;
import org.openide.util.*;
import org.openide.windows.*;


/**
 * Class which represents model of editor area element for GUI hierarchy.
 *
 * @author  Peter Zavadsky
 */
public class EditorView extends ViewElement {

    private static final boolean IS_GTK = "GTK".equals(UIManager.getLookAndFeel().getID()); //NOI18N
    
    private ViewElement editorArea;
    
    private EditorAreaComponent editorAreaComponent;
    
    // XXX PENDING
    private final WindowDnDManager windowDnDManager;
    
    
    public EditorView(Controller controller, WindowDnDManager windowDnDManager,
    double resizeWeight, ViewElement editorArea) {
        super(controller, resizeWeight);
        
        this.editorArea = editorArea;
        this.windowDnDManager = windowDnDManager;
    }
    
    
    // XXX
    Rectangle getPureBounds() {
        Component comp = getEditorAreaComponent();
        Rectangle bounds = comp.getBounds();
        Point location = new Point(0, 0);
        javax.swing.SwingUtilities.convertPointToScreen(location, comp);
        bounds.setLocation(location);
        return bounds;
    }
    
    private EditorAreaComponent getEditorAreaComponent() {
        if(editorAreaComponent == null) {
            editorAreaComponent = new EditorAreaComponent(this, windowDnDManager);
        }

        // Workaround for #42640
        if (EditorView.IS_GTK && !editorAreaComponent.isValid()) {
            editorAreaComponent.repaint();
        }
        return editorAreaComponent;
    }
    
    /** Handles special border policy - scroll pane like border only 
     * if editor area is null.
     */
    private void manageBorder (JPanel panel) {
        if (editorArea != null) {
            panel.setBorder(null);
        } else {
            if (Utilities.isMac()) {
               //#64701 on macosx the nb.scrollpane.border draws ugly line on top
                panel.setBorder(BorderFactory.createEmptyBorder());
            } else {
                Border border = UIManager.getBorder( "Nb.EmptyEditorArea.border"); //NOI18N
                if( null == border ) {
                    // special border installed into UI manager by netbeans
                    border = UIManager.getBorder("Nb.ScrollPane.border"); //NOI18N
                }
                panel.setBorder(border);
	    }
        }
    }
    
    public ViewElement getEditorArea() {
        return editorArea;
    }
    
    public void setEditorArea(ViewElement editorArea) {
        this.editorArea = editorArea;
    }
    
    @Override
    public Component getComponent() {
//        assureComponentInEditorArea();
        return getEditorAreaComponent();
    }
    
    @Override
    public boolean updateAWTHierarchy(Dimension availableSpace) {
//        System.out.println("EditorView:updateAWTHierarchy=" + availableSpace);
        boolean result = false;
        EditorAreaComponent comp = getEditorAreaComponent();
        Dimension d = (Dimension) comp.getClientProperty ("lastAvailableSpace"); //NOI18N
        Dimension currDim = comp.getPreferredSize();
        if (!availableSpace.equals(d) || !availableSpace.equals(currDim)) {
            //We will only return true if we actually did something
            comp.setPreferredSize(availableSpace);
//            comp.setMinimumSize(availableSpace);
            comp.putClientProperty("lastAvailableSpace", availableSpace); //NOI18N
            result = true;
        }
        assureComponentInEditorArea();      
        if (editorArea != null) {
            result |= editorArea.updateAWTHierarchy(new Dimension(availableSpace.width - 5, availableSpace.height - 5));
        }
        return result;
    }
    
    void assureComponentInEditorArea() {
        EditorAreaComponent eac = getEditorAreaComponent();
        if(editorArea == null) {
            eac.setAreaComponent(null);
        } else {
            eac.setAreaComponent(editorArea.getComponent());
        }
        manageBorder(eac);
        
//        // XXX #36885 When in maximixed and compact mode, we cannot add the components
//        // into the editor area, it would remove it from the screen.
//        if(addingAllowed) {
//            if(this.editorArea != null) {
//                editorAreaComp.add(this.editorArea.getComponent(), BorderLayout.CENTER);
//            }
//
//            editorAreaComp.validate();
//            editorAreaComp.repaint();
//        }
    } 

    private static DataFlavor URI_LIST_DATA_FLAVOR;
    static {
        try {
            URI_LIST_DATA_FLAVOR = new DataFlavor("text/uri-list;class=java.lang.String");
        } catch( ClassNotFoundException cnfE ) {
            cnfE.printStackTrace();
        }
    }
    
    private static class EditorAreaComponent extends JPanel
    implements TopComponentDroppable {
        
        private final EditorView editorView;
        
        // XXX PENDING
        private final WindowDnDManager windowDnDManager;
        
        private Component areaComponent;
        
        
        public EditorAreaComponent(EditorView editorView, WindowDnDManager windowDnDManager) {
            this.editorView = editorView;
            this.windowDnDManager = windowDnDManager;
            
            init();
        }

        
        private void init() {
            setLayout(new BorderLayout());
            // special background for XP style
            String lfID = UIManager.getLookAndFeel().getID();
//            if (lfID.equals("Windows")) {
//                setBackground((Color)UIManager.get("nb_workplace_fill"));
//            }
            
            // PENDING Adding image into empty area.
            String imageSource = Constants.SWITCH_IMAGE_SOURCE; // NOI18N
            if(imageSource != null) {
                Image image = ImageUtilities.loadImage(imageSource);
                if(image != null) {
                    JLabel label = new JLabel(ImageUtilities.image2Icon(image));
                    label.setMinimumSize(new Dimension(0, 0)); // XXX To be able shrink the area.
                    add(label, BorderLayout.CENTER);
                } else {
                    Logger.getLogger(EditorView.class.getName()).log(Level.WARNING, null,
                                      new java.lang.NullPointerException("Image not found at " +
                                                                         imageSource)); // NOI18N
                }
            }
            //listen to files being dragged over the editor area
            DropTarget dropTarget = new DropTarget( this, new DropTargetListener() {
                @Override
                public void dragEnter(DropTargetDragEvent dtde) {
                }
                @Override
                public void dragExit(DropTargetEvent dte) {
                }
                @Override
                public void dragOver(DropTargetDragEvent dtde) {
                    for( ExternalDropHandler handler : Lookup.getDefault().lookupAll( ExternalDropHandler.class ) ) {
                        //check if a file is being dragged over and if anybody can process it
                        if( handler.canDrop( dtde ) ) {
                            dtde.acceptDrag( DnDConstants.ACTION_COPY );
                            return;
                        }
                    }
                    dtde.rejectDrag();
                }
                @Override
                public void drop(DropTargetDropEvent dtde) {
                    boolean dropRes = false;
                    try {
                        for( ExternalDropHandler handler : Lookup.getDefault().lookupAll( ExternalDropHandler.class ) ) {
                            if( handler.canDrop( dtde ) ) {
                                //file is being dragged over
                                dtde.acceptDrop( DnDConstants.ACTION_COPY );
                                //let the handler to take care of it
                                dropRes = handler.handleDrop( dtde );
                                break;
                            }
                        }
                    } finally {
                        dtde.dropComplete( dropRes );
                    }
                }
                @Override
                public void dropActionChanged(DropTargetDragEvent dtde) {
                }
            } );
            setDropTarget( dropTarget );
            if( UIManager.getBoolean( "NbMainWindow.showCustomBackground" )
                    || "Aqua".equals(UIManager.getLookAndFeel().getID()) ) //NOI18N
                setOpaque( false);
        }
        
        public void setAreaComponent(Component areaComponent) {
            if(this.areaComponent == areaComponent) {
                // XXX PENDING revise how to better manipulate with components
                // so there don't happen unneeded removals.
                if(areaComponent != null
                && !Arrays.asList(getComponents()).contains(areaComponent)) {
                    add(areaComponent, BorderLayout.CENTER);
                }
                
                return;
            }
            
            if(this.areaComponent != null) {
                remove(this.areaComponent);
            }
            
            this.areaComponent = areaComponent;
            
            if(this.areaComponent != null) {
                add(this.areaComponent, BorderLayout.CENTER);
            }
        }
        
        @Override
        public Shape getIndicationForLocation(Point location) {
            int kind = windowDnDManager.getStartingTransfer().getKind();
                        
            if(kind == Constants.MODE_KIND_EDITOR) {
                Rectangle rect = getBounds();
                rect.setLocation(0, 0);
                return rect;
            } else {
                Rectangle rect = getBounds();
                rect.setLocation(0, 0);

                String side = getSideForLocation(location);

                double ratio = Constants.DROP_AROUND_EDITOR_RATIO;
                if(Constants.TOP.equals(side)) {
                    return new Rectangle(0, 0, rect.width, (int)(rect.height * ratio));
                } else if(side == Constants.LEFT) {
                    return new Rectangle(0, 0, (int)(rect.width * ratio), rect.height);
                } else if(side == Constants.RIGHT) {
                    return new Rectangle(rect.width - (int)(rect.width * ratio), 0, (int)(rect.width * ratio), rect.height);
                } else if(side == Constants.BOTTOM) {
                    return new Rectangle(0, rect.height - (int)(rect.height * ratio), rect.width, (int)(rect.height * ratio));
                } else if(windowDnDManager.getStartingTransfer().isAllowedToMoveAnywhere()) {
                    return rect;
                } else {
                    return null;
                }
            }
        };
        
        @Override
        public Object getConstraintForLocation(Point location) {
            int kind = windowDnDManager.getStartingTransfer().getKind();
                        
            if(kind == Constants.MODE_KIND_EDITOR) {
                return null;
            } else {
                return getSideForLocation(location);
            }
        }
        
        private String getSideForLocation(Point location) {
            Rectangle bounds = getBounds();
            bounds.setLocation(0, 0);

            // Size of area which indicates creation of new split.
            int delta = Constants.DROP_AREA_SIZE;

            Rectangle top = new Rectangle(0, 0, bounds.width, delta);
            if(top.contains(location)) {
                return Constants.TOP;
            }

            Rectangle left = new Rectangle(0, delta, delta, bounds.height - 2 * delta);
            if(left.contains(location)) {
                return Constants.LEFT;
            }

            Rectangle right = new Rectangle(bounds.width - delta, delta, delta, bounds.height - 2 * delta);
            if(right.contains(location)) {
                return Constants.RIGHT;
            }

            Rectangle bottom = new Rectangle(0, bounds.height - delta, bounds.width, delta);
            if(bottom.contains(location)) {
                return Constants.BOTTOM;
            }

            return null;
        }
        
        @Override
        public Component getDropComponent() {
            return this;
        }
        
        @Override
        public ViewElement getDropViewElement() {
            return editorView;
        }
        
        @Override
        public boolean canDrop(TopComponentDraggable transfer, Point location) {
            if(transfer.isAllowedToMoveAnywhere()) {
                return true;
            }
            
            int kind = transfer.getKind();

            if(kind == Constants.MODE_KIND_EDITOR) {
                return true;
            } else {
                if(WindowManagerImpl.getInstance().getEditorAreaState() == Constants.EDITOR_AREA_JOINED
                && getSideForLocation(location) != null) {
                    return true;
                } else {
                    return false;
                }
            }
        }
        
        @Override
        public boolean supportsKind(TopComponentDraggable transfer) {
            return true;
        }

        @Override
        public int getKind() {
            return Constants.MODE_KIND_EDITOR;
        }
        
    } // End of EditorAreaComponent class.
    
    
}

