/*
 * 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 ro.nextreports.designer.grid;

import javax.swing.*;
import javax.swing.event.MouseInputListener;

import ro.nextreports.designer.Globals;
import ro.nextreports.designer.LayoutHelper;
import ro.nextreports.designer.grid.plaf.BasicGridHeaderUI;
import ro.nextreports.designer.grid.plaf.GridUI;
import ro.nextreports.designer.util.I18NSupport;
import ro.nextreports.designer.util.Show;

import ro.nextreports.engine.ReportLayout;
import ro.nextreports.engine.ReportGroup;
import ro.nextreports.engine.exporter.ResultExporter;

import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.*;
import java.util.List;
import java.util.ArrayList;

/**
 * @author Decebal Suiu
 */
public class JGridHeader extends JGrid {

    private static final String uiClassID = "GridHeaderUI";
    private boolean resize;
    private boolean allowSelection;

    private Popup popup;
    private JToolTip toolTip;

    public static final int MINIMUM_COLUMN_SIZE = 10;
    public static final int MAXIMUM_COLUMN_SIZE = 1500;

    // Install UI delegate
    static {
        UIManager.getDefaults().put(uiClassID, BasicGridHeaderUI.class.getName());
    }

    public JGridHeader(final JGrid viewport, final int orientation, boolean resize, boolean allowSelection) {
        this.resize = resize;
        this.allowSelection=allowSelection;
        toolTip = createToolTip();
        spanModel = new DefaultSpanModel();
//		setCellRenderer(Object.class, new GridHeaderRenderer()); // moved down
        selectionModel = new DefaultSelectionModel();

        if (orientation == SwingConstants.HORIZONTAL) {
            columnHeaderModel = viewport.getColumnHeaderModel();
            model = new ColumnHeaderGridModel(columnHeaderModel);
            rowHeaderModel = new DefaultHeaderModel(1, DEFAULT_ROW_HEIGHT,
                    SwingConstants.VERTICAL);
        } else {
            rowHeaderModel = viewport.getRowHeaderModel();
            model = new RowHeaderGridModel(rowHeaderModel);
            columnHeaderModel = new DefaultHeaderModel(1, DEFAULT_COLUMN_WIDTH,
                    SwingConstants.HORIZONTAL);

            // tooltip for group band
            addMouseMotionListener(new MouseMotionAdapter() {
                public void mouseMoved(MouseEvent event) {
                    show(event);
                }

                private void show(MouseEvent event) {
                    Point p = event.getPoint();
                    int row = Globals.getReportGrid().rowAtPoint(p);
                    int column = Globals.getReportGrid().columnAtPoint(p);
                    String bandName = Globals.getReportGrid().getBandName(row);
                    int bandRow = Globals.getReportGrid().getBandLocation(bandName).getRow(row);
                    String groupName = null;
                    if (bandName.startsWith(ReportLayout.GROUP_HEADER_BAND_NAME_PREFIX)) {
                        groupName = bandName.substring(ReportLayout.GROUP_HEADER_BAND_NAME_PREFIX.length());
                    } else if (bandName.startsWith(ReportLayout.GROUP_FOOTER_BAND_NAME_PREFIX)) {
                        groupName = bandName.substring(ReportLayout.GROUP_FOOTER_BAND_NAME_PREFIX.length());
                    }
                    if (groupName != null) {
                        ReportGroup group = LayoutHelper.getReportLayout().getGroup(groupName);
                        setToolTipText(group.getColumn());
                    } else {
                        setToolTipText(null);
                    }
                }
            });
        }

        create(model, spanModel, rowHeaderModel, columnHeaderModel, selectionModel);
        setCellRenderer(Object.class, new GridHeaderRenderer()); // !? review createDefaults ?!
        updateUI();
    }

    @Override
    public GridUI getUI() {
        return (GridUI) ui;
    }

    @Override
    public void setUI(GridUI gridUI) {
        if (ui != gridUI) {
            super.setUI(gridUI);
            repaint();
        }
    }

    @Override
    public void updateUI() {

        // register(or not) listener for selection
        if  (allowSelection) {
            setUI(new BasicGridHeaderUI());
        } else  {
            setUI(new BasicGridHeaderUI() {
                @Override
                protected MouseInputListener createMouseInputListener() {
                    return null;
                }
            });
        }

        // register listener for cell resize
        if (resize) {
            setUI(new BasicGridHeaderUI() {
                protected MouseInputListener createMouseInputListener() {
                    return new HeaderResizeMouseInputListener();
                }
            });
        }
        
        repaintManager.resizeAndRepaint();
    }

    @Override
    public String getUIClassID() {
        return "GridHeaderUI";
    }


    protected void showToolTip(MouseEvent event) {

        toolTip.setTipText(getToolTipText(event));

        //  Trick is to hide a previous popup before showing a new one
        if (popup != null) popup.hide();

        Point pt = getLocationOnScreen();
        int x = pt.x + event.getPoint().x;
        int y = pt.y - 20;

        PopupFactory factory = PopupFactory.getSharedInstance();
        popup = factory.getPopup(this, toolTip, x, y);
        popup.show();
    }


    private static class RowHeaderGridModel extends AbstractGridModel {

        private HeaderModel headerModel;

        public RowHeaderGridModel(HeaderModel rowModel) {
            headerModel = rowModel;
        }

        public Object getValueAt(int row, int column) {

            String bandName = Globals.getReportGrid().getBandName(row);
            int bandRow = Globals.getReportGrid().getBandLocation(bandName).getRow(row);

            String result;
            if (bandName.startsWith(ReportLayout.GROUP_HEADER_BAND_NAME_PREFIX)) {
                result = I18NSupport.getString("band.group.header.name").substring(0, 1).toUpperCase() + bandName.substring(ReportLayout.GROUP_HEADER_BAND_NAME_PREFIX.length());
            } else if (bandName.startsWith(ReportLayout.GROUP_FOOTER_BAND_NAME_PREFIX)) {
                result = I18NSupport.getString("band.group.footer.name").substring(0, 1).toUpperCase() + bandName.substring(ReportLayout.GROUP_FOOTER_BAND_NAME_PREFIX.length());
            } else if (bandName.equals(ReportLayout.PAGE_HEADER_BAND_NAME) || bandName.equals(ReportLayout.PAGE_FOOTER_BAND_NAME)) {
            	result = String.valueOf("Z" + bandRow);            
            } else {
                result = String.valueOf(I18NSupport.getString("band." + bandName.toLowerCase() + ".name").substring(0, 1).toUpperCase() + bandRow);
            }
            return result;
        }

        public boolean isCellEditable(int row, int column) {
            return false;
        }

        public void setValueAt(Object value, int row, int column) {
        }

        public int getRowCount() {
            return headerModel.getCount();
        }

        public int getColumnCount() {
            return 1;
        }

    }

    private static class ColumnHeaderGridModel extends AbstractGridModel {

        private HeaderModel headerModel;

        public ColumnHeaderGridModel(HeaderModel columnModel) {
            headerModel = columnModel;
        }

        public Object getValueAt(int row, int column) {
            return String.valueOf(column);
        }

        public boolean isCellEditable(int row, int column) {
            return false;
        }

        public void setValueAt(Object value, int row, int column) {
        }

        public int getRowCount() {
            return 1;
        }

        public int getColumnCount() {
            return headerModel.getCount();
        }

    }

    protected class HeaderResizeMouseInputListener implements MouseInputListener {

        private boolean dragging = false;
        private boolean start = false;
        private Rectangle r;
        // Give user some leeway for selections.
        private final int PROX_DIST = 3;

        private int row;
        private int column;
        private int width;
        private String oldTooltip;

        public void mouseClicked(MouseEvent e) {
        }

        public void mousePressed(MouseEvent e) {            
            start = true;
            oldTooltip = getToolTipText();
            if (getCursor() != Cursor.getDefaultCursor()) {
                // If cursor is set for resizing, allow dragging.
                if (LayoutHelper.getReportLayout().isUseSize()) {
                    dragging = true;
                    // take care of double clicks (seen as two mouse pressed followed by mouse released actions)
                    // meaning the width remains the same
                    if (start) {
                        Point p = e.getPoint();
                        p.setLocation(p.x - PROX_DIST - 1, p.y);
                        row = rowAtPoint(p);
                        column = columnAtPoint(p);
                        r = getCellBounds(row, column);
                        width = r.width;
                    }
                }
            }
        }

        public void mouseReleased(MouseEvent e) {
            if (dragging & LayoutHelper.getReportLayout().isUseSize()) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        int total = 0;
                        List<Integer> columnsWidth = new ArrayList<Integer>();
                        for (int i = 0, n = getColumnCount(); i < n; i++) {
                            if (i == column) {
                                total += width;
                                columnsWidth.add(width);
                            } else {
                                total += getColumnWidth(i);
                                columnsWidth.add(getColumnWidth(i));
                            }
                        }
                        LayoutHelper.getReportLayout().setColumnsWidth(columnsWidth);
                        // repaint headers
                        Globals.getReportLayoutPanel().getReportGridPanel().repaintHeaders();
                        Globals.getReportDesignerPanel().refresh();

                        if (Globals.getA4Warning()) {
                            if (ResultExporter.A4_LANDSCAPE_PIXELS < total) {
                                Show.info(I18NSupport.getString("width.action.exceed.landscape"));
                            } else if (ResultExporter.A4_PORTRAIT_PIXELS < total) {
                                Show.info(I18NSupport.getString("width.action.exceed.portrait"));
                            }
                        }
                    }
                });
                dragging = false;

                // popup may be null -> on double click
                if (popup != null) {
                    popup.hide();
                }
                setToolTipText(oldTooltip);
                oldTooltip = null;
            }
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mouseDragged(MouseEvent e) {
            if (dragging) {                
                Point p = e.getPoint();
                // adjust the point to left with the PROX_DIST : so we know that the left cell from the
                // mouse will be resized (otherwise can be the left or the right one : undeterministic)
                p.setLocation(p.x - PROX_DIST - 1, p.y);
                if (start) {
                    start = false;
                    row = rowAtPoint(p);
                    column = columnAtPoint(p);
                    r = getCellBounds(row, column);
                }
                int type = getCursor().getType();
                switch (type) {
                    case Cursor.E_RESIZE_CURSOR:
                        if (p.x >= r.x + r.width) {
                            width = p.x - r.x;
                        } else if (p.x <= r.x) {
                            width = r.width;
                        } else {
                            width = p.x - r.x;
                        }
                        break;
                    default:
                }
                if (width < MINIMUM_COLUMN_SIZE) {
                    width = MINIMUM_COLUMN_SIZE;
                } else if (width > MAXIMUM_COLUMN_SIZE) {
                    width = MAXIMUM_COLUMN_SIZE;
                }
                setToolTipText(I18NSupport.getString("width.text") + " : " + String.valueOf(width));
                showToolTip(e);
            }
        }

        public void mouseMoved(MouseEvent e) {
            Point p = e.getPoint();
            if (!isOverRect(p)) {
                if (getCursor() != Cursor.getDefaultCursor()) {
                    // If cursor is not over rect reset it to the default.
                    setCursor(Cursor.getDefaultCursor());
                }
                return;
            }
            // Locate cursor relative to center of rect.
            int outcode = getOutcode(p);
            int row = rowAtPoint(p);
            int column = columnAtPoint(p);
            Rectangle r = getCellBounds(row, column);
            switch (outcode) {
                case Rectangle.OUT_RIGHT:
                    if (Math.abs(p.x - (r.x + r.width)) < PROX_DIST) {
                        if (LayoutHelper.getReportLayout().isUseSize()) {
                            setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
                        }
                    }
                    break;
                default:    // center
                    setCursor(Cursor.getDefaultCursor());
            }
        }

        //
        // Make a smaller Rectangle and use it to locate the
        // cursor relative to the Rectangle center.
        private int getOutcode(Point p) {
            int row = rowAtPoint(p);
            int column = columnAtPoint(p);
            Rectangle r = (Rectangle) getCellBounds(row, column).clone();
            r.grow(-PROX_DIST, -PROX_DIST);
            return r.outcode(p.x, p.y);
        }

        //
        // Make a larger Rectangle and check to see if the
        // cursor is over it.         
        private boolean isOverRect(Point p) {
            int row = rowAtPoint(p);
            int column = columnAtPoint(p);
            Rectangle r = (Rectangle) getCellBounds(row, column).clone();
            r.grow(PROX_DIST, PROX_DIST);
            return r.contains(p);
        }

    }
   
}