/*
* Copyright (c) 2002 and later by MH Software-Entwicklung. All Rights Reserved.
*  
* JTattoo is multiple licensed. If your are an open source developer you can use
* it under the terms and conditions of the GNU General Public License version 2.0
* or later as published by the Free Software Foundation.
*  
* see: gpl-2.0.txt
* 
* If you pay for a license you will become a registered user who could use the
* software under the terms and conditions of the GNU Lesser General Public License
* version 2.0 or later with classpath exception as published by the Free Software
* Foundation.
* 
* see: lgpl-2.0.txt
* see: classpath-exception.txt
* 
* Registered users could also use JTattoo under the terms and conditions of the 
* Apache License, Version 2.0 as published by the Apache Software Foundation.
*  
* see: APACHE-LICENSE-2.0.txt
*/

package com.jtattoo.plaf;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollBarUI;

/**
 * @author Michael Hagen
 */
public class BaseScrollBarUI extends BasicScrollBarUI {

    protected int scrollBarWidth = 17;
    protected int incrGap = 0;
    protected int decrGap = 0;
    protected boolean isRollover = false;

    public static ComponentUI createUI(JComponent c) {
        return new BaseScrollBarUI();
    }

    protected void installDefaults() {
        super.installDefaults();
        
        scrollBarWidth = UIManager.getInt("ScrollBar.width");
        incrGap = UIManager.getInt("ScrollBar.incrementButtonGap");
        decrGap = UIManager.getInt("ScrollBar.decrementButtonGap");

        // TODO this can be removed when incrGap/decrGap become protected
        // handle scaling for sizeVarients for special case components. The
        // key "JComponent.sizeVariant" scales for large/small/mini
        // components are based on Apples LAF
        String scaleKey = (String)scrollbar.getClientProperty("JComponent.sizeVariant");
        if (scaleKey != null){
            if ("large".equals(scaleKey)){
                scrollBarWidth *= 1.15;
                incrGap *= 1.15;
                decrGap *= 1.15;
            } else if ("small".equals(scaleKey)){
                scrollBarWidth *= 0.857;
                incrGap *= 0.857;
                decrGap *= 0.857;
            } else if ("mini".equals(scaleKey)){
                scrollBarWidth *= 0.714;
                incrGap *= 0.714;
                decrGap *= 0.714;
            }
        }
    }

    protected JButton createDecreaseButton(int orientation) {
        if (AbstractLookAndFeel.getTheme().isMacStyleScrollBarOn()) {
            return new InvisibleScrollButton();
        } else {
            return new BaseScrollButton(orientation, scrollBarWidth);
        }
    }

    protected JButton createIncreaseButton(int orientation) {
        if (AbstractLookAndFeel.getTheme().isMacStyleScrollBarOn()) {
            return new InvisibleScrollButton();
        } else {
            return new BaseScrollButton(orientation, scrollBarWidth);
        }
    }

    public TrackListener createTrackListener() {
        return new MyTrackListener();
    }

    public Dimension getPreferredSize(JComponent c) {
        if (AbstractLookAndFeel.getTheme().isMacStyleScrollBarOn()) {
            if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
                return new Dimension(scrollBarWidth + 1, scrollBarWidth * 3);
            } else {
                return new Dimension(scrollBarWidth * 3, scrollBarWidth);
            }
        } else {
            if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
                return new Dimension(scrollBarWidth, scrollBarWidth * 3 + 16);
            } else {
                return new Dimension(scrollBarWidth * 3 + 16, scrollBarWidth);
            }
        }
    }

    protected Dimension getMinimumThumbSize() {
        return new Dimension(scrollBarWidth, scrollBarWidth);
    }

    protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
        int w = c.getWidth();
        int h = c.getHeight();
        if (AbstractLookAndFeel.getTheme().isMacStyleScrollBarOn()) {
            Color bc = ColorHelper.darker(AbstractLookAndFeel.getTheme().getBackgroundColor(), 4);
            g.setColor(bc);
            g.fillRect(0, 0, w, h);
        } else {
            if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
                JTattooUtilities.fillVerGradient(g, AbstractLookAndFeel.getTheme().getTrackColors(), 0, 0, w, h);
            } else {
                JTattooUtilities.fillHorGradient(g, AbstractLookAndFeel.getTheme().getTrackColors(), 0, 0, w, h);
            }
        }
    }

    protected Color[] getThumbColors() {
        if (isRollover || isDragging) {
            return AbstractLookAndFeel.getTheme().getRolloverColors();
        } else if (!JTattooUtilities.isActive(scrollbar)) {
            return AbstractLookAndFeel.getTheme().getInActiveColors();
        } else {
            return AbstractLookAndFeel.getTheme().getThumbColors();
        }
    }

    protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
        if (!c.isEnabled()) {
            return;
        }

        g.translate(thumbBounds.x, thumbBounds.y);

        Color colors[] = getThumbColors();

        Color frameColorHi = ColorHelper.brighter(colors[1], 20);
        Color frameColorLo = ColorHelper.darker(colors[colors.length - 1], 10);

        Graphics2D g2D = (Graphics2D) g;
        Composite savedComposite = g2D.getComposite();
        if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
            JTattooUtilities.fillVerGradient(g, colors, 1, 1, thumbBounds.width - 1, thumbBounds.height - 1);
            JTattooUtilities.draw3DBorder(g, frameColorLo, ColorHelper.darker(frameColorLo, 15), 0, 0, thumbBounds.width, thumbBounds.height);

            g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
            g.setColor(frameColorHi);
            g.drawLine(1, 1, thumbBounds.width - 2, 1);
            g.drawLine(1, 1, 1, thumbBounds.height - 2);

            if (!AbstractLookAndFeel.getTheme().isMacStyleScrollBarOn()) {
                int dx = 5;
                int dy = thumbBounds.height / 2 - 3;
                int dw = thumbBounds.width - 11;

                Color c1 = Color.white;
                Color c2 = Color.darkGray;

                for (int i = 0; i < 4; i++) {
                    g.setColor(c1);
                    g.drawLine(dx, dy, dx + dw, dy);
                    dy++;
                    g.setColor(c2);
                    g.drawLine(dx, dy, dx + dw, dy);
                    dy++;
                }
            }
            g2D.setComposite(savedComposite);
        } else { // HORIZONTAL
            JTattooUtilities.fillHorGradient(g, colors, 1, 1, thumbBounds.width - 1, thumbBounds.height - 1);
            JTattooUtilities.draw3DBorder(g, frameColorLo, ColorHelper.darker(frameColorLo, 10), 0, 0, thumbBounds.width, thumbBounds.height);

            g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
            g.setColor(frameColorHi);
            g.drawLine(1, 1, thumbBounds.width - 2, 1);
            g.drawLine(1, 1, 1, thumbBounds.height - 2);

            if (!AbstractLookAndFeel.getTheme().isMacStyleScrollBarOn()) {
                int dx = thumbBounds.width / 2 - 3;
                int dy = 5;
                int dh = thumbBounds.height - 11;

                Color c1 = Color.white;
                Color c2 = Color.darkGray;

                for (int i = 0; i < 4; i++) {
                    g.setColor(c1);
                    g.drawLine(dx, dy, dx, dy + dh);
                    dx++;
                    g.setColor(c2);
                    g.drawLine(dx, dy, dx, dy + dh);
                    dx++;
                }
            }
        }
        g2D.setComposite(savedComposite);

        g.translate(-thumbBounds.x, -thumbBounds.y);
    }

    protected void layoutVScrollbar(JScrollBar sb) {
        if (AbstractLookAndFeel.getTheme().isLinuxStyleScrollBarOn() && incrButton.isVisible() && decrButton.isVisible()) {
            Dimension sbSize = sb.getSize();
            Insets sbInsets = sb.getInsets();
            int sizeH = sbSize.height - sbInsets.top - sbInsets.bottom;

            /*
             * Width and left edge of the buttons and thumb.
             */
            int itemX = sbInsets.left;
            int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
            int itemH = Math.min(itemW, sizeH / 2);
            
            /* Nominal locations of the buttons, assuming their preferred
             * size will fit.
             */
            int decrButtonY = sbSize.height - sbInsets.bottom - itemH - itemH + 1;
            int incrButtonY = sbSize.height - sbInsets.bottom - itemH;

            /* Compute the height and origin of the thumb. The case
             * where the thumb is at the bottom edge is handled specially 
             * to avoid numerical problems in computing thumbY.  Enforce
             * the thumbs min/max dimensions. If the thumb doesn't
             * fit in the track (trackH) we'll hide it later.
             */
            float trackH = sbSize.height - sbInsets.top - sbInsets.bottom - itemW - itemW + 1;
            float min = sb.getMinimum();
            float max = sb.getMaximum();
            float extent = sb.getVisibleAmount();
            float range = max - min;
            float value = sb.getValue();
            
            int maxThumbH = getMaximumThumbSize().height;
            int minThumbH = getMinimumThumbSize().height;
            int thumbH = (range <= 0) ? maxThumbH : (int) (trackH * (extent / range));
            thumbH = Math.max(thumbH, minThumbH);
            thumbH = Math.min(thumbH, maxThumbH);

            int thumbY = decrButtonY - thumbH;
            if (value < (max - extent)) {
                float thumbRange = trackH - thumbH;
                thumbY = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
            }
            
            /* If the thumb isn't going to fit, zero it's bounds.  Otherwise
             * make sure it fits between the buttons.  Note that setting the
             * thumbs bounds will cause a repaint.
             */
            if (thumbH > trackH) {
                setThumbBounds(0, 0, 0, 0);
            } else {
                setThumbBounds(itemX, thumbY, itemW, thumbH);
            }
            decrButton.setBounds(itemX, decrButtonY, itemW, itemH);
            incrButton.setBounds(itemX, incrButtonY, itemW, itemH);
            
            /* Update the trackRect field.
             */
            trackRect.setBounds(itemX, 0, itemW, (int)trackH);
            
        } else {
            super.layoutVScrollbar(sb);
        }
    }

    protected void layoutHScrollbar(JScrollBar sb) {
        if (AbstractLookAndFeel.getTheme().isLinuxStyleScrollBarOn() && incrButton.isVisible() && decrButton.isVisible()) {
            Dimension sbSize = sb.getSize();
            Insets sbInsets = sb.getInsets();
            int sizeW = sbSize.width - sbInsets.left - sbInsets.right;

            /*
             * Height and top edge of the buttons and thumb.
             */
            int itemY = sbInsets.top;
            int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);//Math.min(itemW, sizeH / 2);
            int itemW = Math.min(itemH, sizeW / 2);//sbSize.width - (sbInsets.left + sbInsets.right);
            
            /* Nominal locations of the buttons, assuming their preferred
             * size will fit.
             */
            int decrButtonX = sbSize.width - sbInsets.right - itemW - itemW + 1;
            int incrButtonX = sbSize.width - sbInsets.right - itemW;

            /* Compute the width and origin of the thumb. The case
             * where the thumb is at the right edge is handled specially 
             * to avoid numerical problems in computing thumbX.  Enforce
             * the thumbs min/max dimensions. If the thumb doesn't
             * fit in the track (trackW) we'll hide it later.
             */
            float trackW = sbSize.width - sbInsets.left - sbInsets.right - itemH - itemH + 1;
            float min = sb.getMinimum();
            float max = sb.getMaximum();
            float extent = sb.getVisibleAmount();
            float range = max - min;
            float value = sb.getValue();
            
            int maxThumbW = getMaximumThumbSize().width;
            int minThumbW = getMinimumThumbSize().width;
            int thumbW = (range <= 0) ? maxThumbW : (int) (trackW * (extent / range));
            thumbW = Math.max(thumbW, minThumbW);
            thumbW = Math.min(thumbW, maxThumbW);

            int thumbX = decrButtonX - thumbW;
            if (value < (max - extent)) {
                float thumbRange = trackW - thumbW;
                thumbX = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
            }
            
            /* If the thumb isn't going to fit, zero it's bounds.  Otherwise
             * make sure it fits between the buttons.  Note that setting the
             * thumbs bounds will cause a repaint.
             */
            if (thumbW > trackW) {
                setThumbBounds(0, 0, 0, 0);
            } else {
                setThumbBounds(thumbX, itemY, thumbW, itemH);
            }
            decrButton.setBounds(decrButtonX, itemY, itemW, itemH);
            incrButton.setBounds(incrButtonX, itemY, itemW, itemH);
            
            /* Update the trackRect field.
             */
            trackRect.setBounds(0, itemY, (int)trackW, itemH);
            
        } else {
            super.layoutHScrollbar(sb);
        }
    }
    
//-----------------------------------------------------------------------------
// inner classes    
//-----------------------------------------------------------------------------
    protected class MyTrackListener extends TrackListener {

        public void mouseEntered(MouseEvent e) {
            super.mouseEntered(e);
            isRollover = true;
            Rectangle r = getTrackBounds();
            scrollbar.repaint(r.x, r.y, r.width, r.height);
        }

        public void mouseExited(MouseEvent e) {
            super.mouseExited(e);
            isRollover = false;
            Rectangle r = getTrackBounds();
            scrollbar.repaint(r.x, r.y, r.width, r.height);
        }

        public void mousePressed(MouseEvent e) {
            super.mousePressed(e);
            Rectangle r = getTrackBounds();
            scrollbar.repaint(r.x, r.y, r.width, r.height);
        }

        public void mouseReleased(MouseEvent e) {
            super.mouseReleased(e);
            Rectangle r = getTrackBounds();
            scrollbar.repaint(r.x, r.y, r.width, r.height);
        }
    }

//-----------------------------------------------------------------------------    
    private static class InvisibleScrollButton extends JButton {
        
        public InvisibleScrollButton() {
            super();
            setVisible(false);
        }

        public Dimension getPreferredSize() {
            return new Dimension(0, 0);
        }
        
    }
}
