// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
 *******************************************************************************
 * Copyright (C) 1997-2010, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package com.ibm.icu.dev.demo.holiday;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Panel;

/**
 * Various graphical borders. The border itself is a Panel so that it can
 * contain other Components (i.e. it borders something). You use the
 * HolidayBorderPanel like any other Panel: you set the layout that you prefer and
 * add Components to it. Beware that a null layout does not obey the insets
 * of the panel so if you use null layouts, adjust your measurements to
 * handle the border by calling insets().
 *
 * @author  Andy Clark, Taligent Inc.
 * @version 1.0
 */
public class HolidayBorderPanel extends Panel {
    /**
     * For serialization
     */
    private static final long serialVersionUID = 4669213306492461159L;
    // Constants

    /** Solid border. */
    public final static int SOLID = 0;
    /** A raised border. */
    public final static int RAISED = 1;
    /** A lowered border. */
    public final static int LOWERED = 2;
    /** An etched in border. */
    public final static int IN = 3;
    /** An etched out border. */
    public final static int OUT = 4;

    /** Left alignment. */
    public final static int LEFT = 0;
    /** Center alignment. */
    public final static int CENTER = 1;
    /** Right alignment. */
    public final static int RIGHT = 2;

    /** Default style (IN). */
    public final static int DEFAULT_STYLE = IN;
    /** Default thickness (10). */
    public final static int DEFAULT_THICKNESS = 10;
    /** Default thickness for solid borders (4). */
    public final static int DEFAULT_SOLID_THICKNESS = 4;
    /** Default thickness for raised borders (2). */
    public final static int DEFAULT_RAISED_THICKNESS = 2;
    /** Default thickness for lowered borders (2). */
    public final static int DEFAULT_LOWERED_THICKNESS = 2;
    /** Default thickness for etched-in borders (10). */
    public final static int DEFAULT_IN_THICKNESS = 10;
    /** Default thickness for etched-out borders (10). */
    public final static int DEFAULT_OUT_THICKNESS = 10;
    /** Default gap between border and contained component (5). */
    public final static int DEFAULT_GAP = 5;
    /** Default color (black). Applies to SOLID and etched borders. */
    public final static Color DEFAULT_COLOR = Color.black;

    /** Default font (TimesRoman,PLAIN,14). Only applies to etched borders. */
    public final static Font DEFAULT_FONT = new Font("TimesRoman", Font.PLAIN, 14);
    /** Default alignment (LEFT). Only applies to etched borders. */
    public final static int DEFAULT_ALIGNMENT = LEFT;

    // Data
    private int style;
    private int thickness;
    private int gap;
    private Color color;

    private Font font;
    private String text;
    private int alignment;

    /**
     * Constructor. Makes default border.
     */
    public HolidayBorderPanel() {

        // initialize data
        style       = DEFAULT_STYLE;
        thickness   = DEFAULT_THICKNESS;
        gap         = DEFAULT_GAP;
        color       = DEFAULT_COLOR;

        text        = null;
        font        = DEFAULT_FONT;
        alignment   = DEFAULT_ALIGNMENT;

        }

    /**
     * Constructor. Makes an etched IN border with given text caption.
     *
     * @param text  Text caption
     */
    public HolidayBorderPanel(String text) {
        this();

        style = IN;
        this.text = text;
        }

    /**
     * Constructor. Makes SOLID border with color and thickness given.
     *
     * @param color     The color for the border.
     * @param thickness The thickness of the border.
     */
    public HolidayBorderPanel(Color color, int thickness) {
        this();

        style = SOLID;
        this.color = color;
        this.thickness = thickness;
        }

    /**
     * Constructor. Makes a border of the given style with the default
     * thickness for that style.
     *
     * @param style The style for this border.
     */
    public HolidayBorderPanel(int style) {
        this();

        // set thickness appropriate to this style
        switch (style) {
            case SOLID: thickness = DEFAULT_SOLID_THICKNESS; break;
            case RAISED: thickness = DEFAULT_RAISED_THICKNESS; break;
            case LOWERED: thickness = DEFAULT_LOWERED_THICKNESS; break;
            case IN: thickness = DEFAULT_IN_THICKNESS; break;
            case OUT: thickness = DEFAULT_OUT_THICKNESS; break;
            default:
                thickness = DEFAULT_THICKNESS;
            }

        this.style = style;
        }

    /**
     * Constructor. Makes border with given style and thickness.
     *
     * @param style     The style for this border.
     * @param thickness The thickness for this border.
     */
    public HolidayBorderPanel(int style, int thickness) {
        this();

        this.style = style;
        this.thickness = thickness;
        }

    /**
     * Returns the insets of this panel..
     */
    public Insets getInsets() {
        int adjustment = 0;

        // adjust for text string
        if (style == IN || style == OUT) {
            if (text != null && text.length() > 0) {
                try {
                    // set font and get info
                    int height = getGraphics().getFontMetrics(font).getHeight();
                    if (height > thickness)
                        adjustment = height - thickness;
                    }
                catch (Exception e) {
                    // nothing: just in case there is no graphics context
                    //   at the beginning.
                    System.out.print("");
                    }
                }
            }

        // return appropriate insets
        int dist = thickness + gap;
        return new Insets(dist + adjustment, dist, dist, dist);
        }

    /**
     * Sets the style of the border
     *
     * @param style The new style.
     */
    public HolidayBorderPanel setStyle(int style) {

        // set the style and re-layout the panel
        this.style = style;
        doLayout();
        repaint();

        return this;
        }

    /**
     * Gets the style of the border
     */
    public int getStyle() {

        return style;
        }

    /**
     * Sets the thickness of the border.
     *
     * @param thickness The new thickness
     */
    public HolidayBorderPanel setThickness(int thickness) {

        if (thickness > 0) {
            this.thickness = thickness;
            doLayout();
            repaint();
            }

        return this;
        }

    /**
     * Gets the thickness of the border.
     */
    public int getThickness() {

        return thickness;
        }

    /**
     * Sets the gap between the border and the contained Component.
     *
     * @param gap The new gap, in pixels.
     */
    public HolidayBorderPanel setGap(int gap) {

        if (gap > -1) {
            this.gap = gap;
            doLayout();
            repaint();
            }

        return this;
        }

    /**
     * Gets the gap between the border and the contained Component.
     */
    public int getGap() {

        return gap;
        }

    /**
     * Sets the current color for SOLID borders and the caption text
     * color for etched borders.
     *
     * @param color The new color.
     */
    public HolidayBorderPanel setColor(Color color) {

        this.color = color;
        if (style == SOLID || style == IN || style == OUT)
            repaint();

        return this;
        }

    /**
     * Gets the current color for SOLID borders and the caption
     * text color for etched borders.
     */
    public Color getColor() {

        return color;
        }

    /**
     * Sets the font. Only applies to etched borders.
     */
    public HolidayBorderPanel setTextFont(Font font) {

        // set font
        if (font != null) {
            this.font = font;
            if (style == IN || style == OUT) {
                doLayout();
                repaint();
                }
            }

        return this;
        }

    /**
     * Gets the font of the text. Only applies to etched borders.
     */
    public Font getTextFont() {

        return font;
        }

    /**
     * Sets the text. Only applies to etched borders.
     *
     * @param text  The new text.
     */
    public HolidayBorderPanel setText(String text) {

        this.text = text;
        if (style == IN || style == OUT) {
            doLayout();
            repaint();
            }

        return this;
        }

    /**
     * Gets the text. Only applies to etched borders.
     */
    public String getText() {

        return text;
        }

    /**
     * Sets the text alignment. Only applies to etched borders.
     *
     * @param alignment The new alignment.
     */
    public HolidayBorderPanel setAlignment(int alignment) {

        this.alignment = alignment;
        if (style == IN || style == OUT) {
            doLayout();
            repaint();
            }

        return this;
        }

    /**
     * Gets the text alignment.
     */
    public int getAlignment() {

        return alignment;
        }

    /**
     * Repaints the border.
     *
     * @param g The graphics context.
     */
    public void paint(Graphics g) {

        // get current dimensions
        Dimension size = getSize();
        int width = size.width;
        int height = size.height;

        // set colors
        Color light = getBackground().brighter().brighter().brighter();
        Color dark = getBackground().darker().darker().darker();

        // Draw border
        switch (style) {
            case RAISED:    // 3D Border (in or out)
            case LOWERED:
                Color topleft = null;
                Color bottomright = null;

                // set colors
                if (style == RAISED) {
                    topleft = light;
                    bottomright = dark;
                    }
                else {
                    topleft = dark;
                    bottomright = light;
                    }

                // draw border
                g.setColor(topleft);
                for (int i = 0; i < thickness; i++) {
                    g.drawLine(i, i, width - i - 2, i);
                    g.drawLine(i, i + 1, i, height - i - 1);
                    }
                g.setColor(bottomright);
                for (int i = 0; i < thickness; i++) {
                    g.drawLine(i + 1, height - i - 1, width - i - 1, height - i - 1);
                    g.drawLine(width - i - 1, i, width - i - 1, height - i - 2);
                    }
                break;

            case IN:    // Etched Border (in or out)
            case OUT:
                int adjust1 = 0;
                int adjust2 = 0;

                // set font and get info
                Font oldfont = g.getFont();
                g.setFont(font);
                FontMetrics fm = g.getFontMetrics();
                int ascent = fm.getAscent();

                // set adjustment
                if (style == IN)
                    adjust1 = 1;
                else
                    adjust2 = 1;

                // Calculate adjustment for text
                int adjustment = 0;
                if (text != null && text.length() > 0) {
                    if (ascent > thickness)
                        adjustment = (ascent - thickness) / 2;
                    }

                // The adjustment is there so that we always draw the
                // light rectangle first. Otherwise, your eye picks up
                // the discrepancy where the light rect. passes over
                // the darker rect.
                int x = thickness / 2;
                int y = thickness / 2 + adjustment;
                int w = width - thickness - 1;
                int h = height - thickness - 1 - adjustment;

                // draw rectangles
                g.setColor(light);
                g.drawRect(x + adjust1, y + adjust1, w, h);
                g.setColor(dark);
                g.drawRect(x + adjust2, y + adjust2, w, h);

                // draw text, if applicable
                if (text != null && text.length() > 0) {
                    // calculate drawing area
                    int fontheight = fm.getHeight();
                    int strwidth = fm.stringWidth(text);

                    int textwidth = width - 2 * (thickness + 5);
                    if (strwidth > textwidth)
                        strwidth = textwidth;

                    // calculate offset for alignment
                    int offset;
                    switch (alignment) {
                        case CENTER:
                            offset = (width - strwidth) / 2;
                            break;
                        case RIGHT:
                            offset = width - strwidth - thickness - 5;
                            break;
                        case LEFT:
                        default: // assume left alignment if invalid
                            offset = thickness + 5;
                            break;
                        }

                    // clear drawing area and set clipping region
                    g.clearRect(offset - 5, 0, strwidth  + 10, fontheight);
                    g.clipRect(offset, 0, strwidth, fontheight);

                    // draw text
                    g.setColor(color);
                    g.drawString(text, offset, ascent);

                    // restore old clipping area
                    g.clipRect(0, 0, width, height);
                    }

                g.setFont(oldfont);
                break;

            case SOLID:
            default: // assume SOLID
                g.setColor(color);
                for (int i = 0; i < thickness; i++)
                    g.drawRect(i, i, width - 2 * i - 1, height - 2 * i - 1);
            }

        }

    /**
     * Returns the settings of this HolidayBorderPanel instance as a string.
     */
    public String toString() {
        StringBuffer str = new StringBuffer("HolidayBorderPanel[");

        // style
        str.append("style=");
        switch (style) {
            case SOLID: str.append("SOLID"); break;
            case RAISED: str.append("RAISED"); break;
            case LOWERED: str.append("LOWERED"); break;
            case IN: str.append("IN"); break;
            case OUT: str.append("OUT"); break;
            default: str.append("unknown");
            }
        str.append(",");

        // thickness
        str.append("thickness=");
        str.append(thickness);
        str.append(",");

        // gap
        str.append("gap=");
        str.append(gap);
        str.append(",");

        // color
        str.append(color);
        str.append(",");

        // font
        str.append(font);
        str.append(",");

        // text
        str.append("text=");
        str.append(text);
        str.append(",");

        // alignment
        str.append("alignment=");
        switch (alignment) {
            case LEFT: str.append("LEFT"); break;
            case CENTER: str.append("CENTER"); break;
            case RIGHT: str.append("RIGHT"); break;
            default: str.append("unknown");
            }

        str.append("]");

        return str.toString();
        }

    }

