/*******************************************************************************
 * Copyright (c) 2012, 2020 Original authors and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Original authors and others - initial API and implementation
 ******************************************************************************/
package org.eclipse.nebula.widgets.nattable.painter.cell.decorator;

import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.painter.cell.CellPainterWrapper;
import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
import org.eclipse.nebula.widgets.nattable.style.BorderStyle;
import org.eclipse.nebula.widgets.nattable.style.BorderStyle.LineStyleEnum;
import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
import org.eclipse.nebula.widgets.nattable.style.CellStyleUtil;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.nebula.widgets.nattable.style.IStyle;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;

public class LineBorderDecorator extends CellPainterWrapper {

    private final BorderStyle defaultBorderStyle;

    public LineBorderDecorator(ICellPainter interiorPainter) {
        this(interiorPainter, null);
    }

    public LineBorderDecorator(ICellPainter interiorPainter, BorderStyle defaultBorderStyle) {
        super(interiorPainter);
        this.defaultBorderStyle = defaultBorderStyle;
    }

    @Override
    public int getPreferredWidth(ILayerCell cell, GC gc, IConfigRegistry configRegistry) {
        BorderStyle borderStyle = getBorderStyle(cell, configRegistry);
        int borderThickness = borderStyle != null ? borderStyle.getThickness() : 0;

        return super.getPreferredWidth(cell, gc, configRegistry) + (borderThickness * 2);
    }

    @Override
    public int getPreferredHeight(ILayerCell cell, GC gc, IConfigRegistry configRegistry) {
        BorderStyle borderStyle = getBorderStyle(cell, configRegistry);
        int borderThickness = borderStyle != null ? borderStyle.getThickness() : 0;

        return super.getPreferredHeight(cell, gc, configRegistry) + (borderThickness * 2);
    }

    private BorderStyle getBorderStyle(ILayerCell cell, IConfigRegistry configRegistry) {
        IStyle cellStyle = CellStyleUtil.getCellStyle(cell, configRegistry);
        BorderStyle borderStyle = cellStyle.getAttributeValue(CellStyleAttributes.BORDER_STYLE);
        if (borderStyle == null) {
            borderStyle = this.defaultBorderStyle;
        }
        return borderStyle;
    }

    @Override
    public void paintCell(ILayerCell cell, GC gc, Rectangle rectangle, IConfigRegistry configRegistry) {
        BorderStyle borderStyle = getBorderStyle(cell, configRegistry);
        int borderThickness = borderStyle != null ? borderStyle.getThickness() : 0;

        Rectangle interiorBounds = new Rectangle(
                rectangle.x + borderThickness,
                rectangle.y + borderThickness,
                rectangle.width - (borderThickness * 2),
                rectangle.height - (borderThickness * 2));
        super.paintCell(cell, gc, interiorBounds, configRegistry);

        if (borderStyle == null || borderThickness <= 0) {
            return;
        }

        // Save GC settings
        Color originalForeground = gc.getForeground();
        int originalLineWidth = gc.getLineWidth();
        int originalLineStyle = gc.getLineStyle();

        Integer gridLineWidth = configRegistry.getConfigAttribute(
                CellConfigAttributes.GRID_LINE_WIDTH,
                DisplayMode.NORMAL,
                cell.getConfigLabels());
        int adjustment = (gridLineWidth == null || gridLineWidth == 1)
                ? 0
                : Math.round(gridLineWidth.floatValue() / 2);

        gc.setLineWidth(borderThickness);

        Rectangle borderArea = new Rectangle(
                rectangle.x,
                rectangle.y,
                rectangle.width,
                rectangle.height);
        if (borderThickness >= 1) {
            int shift = 0;
            int areaShift = 0;
            if ((borderThickness % 2) == 0) {
                shift = borderThickness / 2;
                areaShift = (shift * 2);
            } else {
                shift = borderThickness / 2;
                areaShift = (shift * 2) + 1;
            }
            borderArea.x += (shift + adjustment);
            borderArea.y += (shift + adjustment);
            borderArea.width -= (areaShift + adjustment);
            borderArea.height -= (areaShift + adjustment);
        }

        gc.setLineStyle(LineStyleEnum.toSWT(borderStyle.getLineStyle()));
        gc.setForeground(borderStyle.getColor());
        gc.drawRectangle(borderArea);

        // Restore GC settings
        gc.setForeground(originalForeground);
        gc.setLineWidth(originalLineWidth);
        gc.setLineStyle(originalLineStyle);
    }

}
