package org.drools.guvnor.client.util;

import org.drools.guvnor.client.resources.Images;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.layout.client.Layout.Alignment;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.Widget;

public class ScrollTabLayoutPanel extends TabLayoutPanel {
    private static Images       images             = GWT.create( Images.class );

    private Image               scrollLeft;
    private Image               scrollRight;
    private FlowPanel           tabBar             = null;
    private LayoutPanel         panel;
    private static final int    ScrollLengh        = 100;

    private double              barHeight;
    private Unit                barUnit;
    private boolean             isScrollingEnabled = false;

    private HandlerRegistration windowResizeHandler;

    public ScrollTabLayoutPanel(double barHeight,
                                Unit barUnit) {
        super( barHeight,
               barUnit );
        this.barHeight = barHeight;
        this.barUnit = barUnit;
        this.panel = (LayoutPanel) getWidget();

        for ( int i = 0; i < panel.getWidgetCount(); ++i ) {
            Widget widget = panel.getWidget( i );
            if ( widget instanceof FlowPanel ) {
                tabBar = (FlowPanel) widget;
                break;
            }
        }
    }

    @Override
    public void add(Widget child,
                    Widget tab) {
        super.add( child,
                   tab );
        checkIfScrollButtonsNecessary();
    }

    @Override
    public boolean remove(Widget w) {
        boolean b = super.remove( w );
        checkIfScrollButtonsNecessary();
        return b;
    }

    @Override
    public boolean remove(int index) {
        boolean b = super.remove( index );
        checkIfScrollButtonsNecessary();
        return b;
    }

    @Override
    protected void onLoad() {
        super.onLoad();

        if ( windowResizeHandler == null ) {
            windowResizeHandler = Window.addResizeHandler( new ResizeHandler() {
                public void onResize(ResizeEvent event) {
                    checkIfScrollButtonsNecessary();
                }
            } );
        }
    }

    @Override
    protected void onUnload() {
        super.onUnload();

        if ( windowResizeHandler != null ) {
            windowResizeHandler.removeHandler();
            windowResizeHandler = null;
        }
    }

    private void enableScroll() {
        if ( !isScrollingEnabled ) {
            scrollRight = new Image( images.scrollRight() );
            scrollLeft = new Image( images.scrollLeft() );
            scrollLeft.addClickHandler( createScrollClickHandler( ScrollLengh ) );
            scrollRight.addClickHandler( createScrollClickHandler( -ScrollLengh ) );

            panel.setWidgetLeftRight( tabBar,
                                      30,
                                      Unit.PX,
                                      30,
                                      Unit.PX );
            panel.setWidgetTopHeight( tabBar,
                                      0,
                                      Unit.PX,
                                      barHeight,
                                      barUnit );
            panel.setWidgetVerticalPosition( tabBar,
                                             Alignment.END );

            panel.add( scrollRight );
            panel.setWidgetRightWidth( scrollRight,
                                       0,
                                       Unit.PX,
                                       27,
                                       Unit.PX );
            panel.setWidgetTopHeight( scrollRight,
                                      0,
                                      Unit.PX,
                                      barHeight,
                                      barUnit );
            panel.setWidgetVerticalPosition( scrollRight,
                                             Alignment.END );

            panel.add( scrollLeft );
            panel.setWidgetLeftWidth( scrollLeft,
                                      0,
                                      Unit.PX,
                                      27,
                                      Unit.PX );
            panel.setWidgetTopHeight( scrollLeft,
                                      0,
                                      Unit.PX,
                                      barHeight,
                                      barUnit );
            panel.setWidgetVerticalPosition( scrollLeft,
                                             Alignment.END );

            isScrollingEnabled = true;
        }
    }

    private void disableScroll() {
        if ( isScrollingEnabled ) {
            panel.remove( scrollRight );
            panel.remove( scrollLeft );

            panel.setWidgetLeftRight( tabBar,
                                      0,
                                      Unit.PX,
                                      0,
                                      Unit.PX );
            panel.setWidgetTopHeight( tabBar,
                                      0,
                                      Unit.PX,
                                      barHeight,
                                      barUnit );
            panel.setWidgetVerticalPosition( tabBar,
                                             Alignment.END );

            isScrollingEnabled = false;
        }
    }

    private void checkIfScrollButtonsNecessary() {
        // Defer size calculations until sizes are available, when calculating
        // immediately after add(), all size methods return zero
        Scheduler.get().scheduleDeferred( new Scheduler.ScheduledCommand() {
            public void execute() {
                boolean isScrolling = isScrollingNecessary();
                if ( isScrolling ) {
                    enableScroll();
                } else {
                    disableScroll();
                }
            }
        } );
    }

    private boolean isScrollingNecessary() {
        Widget lastTab = getLastTab();
        if ( lastTab == null ) return false;

        return getRightOfWidget( lastTab ) > getTabBarWidth();
    }

    private ClickHandler createScrollClickHandler(final int diff) {
        return new ClickHandler() {
            public void onClick(ClickEvent event) {
                Widget lastTab = getLastTab();
                if ( lastTab == null ) return;

                int newLeft = parsePosition( tabBar.getElement().getStyle().getLeft() ) + diff;
                int rightOfLastTab = getRightOfWidget( lastTab );

                // Prevent scrolling the last tab too far away form the right
                // border, or the first tab further than the left border position
                if ( newLeft <= 0 && (getTabBarWidth() - newLeft < (rightOfLastTab + ScrollLengh)) ) {
                    scrollTo( newLeft );
                }
            }
        };
    }

    private void scrollTo(int pos) {
        tabBar.getElement().getStyle().setLeft( pos,
                                                Unit.PX );
    }

    private int getRightOfWidget(Widget widget) {
        return widget.getElement().getOffsetLeft() + widget.getElement().getOffsetWidth();
    }

    private int getTabBarWidth() {
        return tabBar.getElement().getParentElement().getClientWidth();
    }

    private Widget getLastTab() {
        if ( tabBar.getWidgetCount() == 0 ) return null;

        return tabBar.getWidget( tabBar.getWidgetCount() - 1 );
    }

    private static int parsePosition(String positionString) {
        int position;
        try {
            for ( int i = 0; i < positionString.length(); i++ ) {
                char c = positionString.charAt( i );
                if ( c != '-' && !(c >= '0' && c <= '9') ) {
                    positionString = positionString.substring( 0,
                                                               i );
                }
            }

            position = Integer.parseInt( positionString );
        } catch ( NumberFormatException ex ) {
            position = 0;
        }
        return position;
    }
}
