/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores
 * CA 94065 USA or visit www.oracle.com if you need additional information or
 * have any questions.
 */
package com.codename1.ui;

import com.codename1.ui.animations.Motion;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.events.FocusListener;
import com.codename1.ui.events.SelectionListener;
import com.codename1.ui.geom.Dimension;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.layouts.FlowLayout;
import com.codename1.ui.layouts.GridLayout;
import com.codename1.ui.layouts.Layout;
import com.codename1.ui.plaf.Style;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.EventDispatcher;

/**
 * A component that lets the user switch between a group of components by
 * clicking on a tab with a given title and/or icon.
 *
 * <p>
 * Tabs/components are added to a <code>Tabs</code> object by using the
 * <code>addTab</code> and <code>insertTab</code> methods.
 * A tab is represented by an index corresponding
 * to the position it was added in, where the first tab has an index equal to 0
 * and the last tab has an index equal to the tab count minus 1.
 * <p>
 * The <code>Tabs</code> uses a <code>SingleSelectionModel</code>
 * to represent the set of tab indices and the currently selected index.
 * If the tab count is greater than 0, then there will always be a selected
 * index, which by default will be initialized to the first tab.
 * If the tab count is 0, then the selected index will be -1.
 * <p>
 *
 * @author Chen Fishbein
 *
 */
public class Tabs extends Container {
    private Container contentPane = new Container(new TabsLayout());
    /**
     * Where the tabs are placed.
     */
    private int tabPlacement;
    private Container tabsContainer;
    private ButtonGroup radioGroup = new ButtonGroup();
    private Button selectedTab;
    private boolean swipeActivated = true;
    
    private ActionListener press, drag, release;
    private Motion slideToDestMotion;
    private int initialX = -1;
    private int initialY = -1;
    private int lastX = -1;
    private boolean dragStarted = false;
    private int activeComponent = 0;
    private int active = 0;
    
    private EventDispatcher focusListeners;
    private EventDispatcher selectionListener;
    private TabFocusListener focusListener;
    private boolean tabsFillRows;
    private boolean tabsGridLayout;
    private int textPosition = -1;
    private boolean changeTabOnFocus;
    private boolean changeTabContainerStyleOnFocus;
    private int tabsGap = 0;
    private Style originalTabsContainerUnselected, originalTabsContainerSelected;
    
    
    /**
     * Creates an empty <code>TabbedPane</code> with a default
     * tab placement of <code>Component.TOP</code>.
     */
    public Tabs() {
        this(-1);
    }

    /**
     * Creates an empty <code>TabbedPane</code> with the specified tab placement
     * of either: <code>Component.TOP</code>, <code>Component.BOTTOM</code>,
     * <code>Component.LEFT</code>, or <code>Component.RIGHT</code>.
     *
     * @param tabPlacement the placement for the tabs relative to the content
     */
    public Tabs(int tabP) {
        super(new BorderLayout());
        focusListener = new TabFocusListener();
        contentPane.setUIID("TabbedPane");
        super.addComponent(BorderLayout.CENTER, contentPane);
        tabsContainer = new Container();
        tabsContainer.setUIID("TabsContainer");
        tabsContainer.setScrollVisible(false);
        tabsContainer.getStyle().setMargin(0, 0, 0, 0);
        if(tabP == -1){
            setTabPlacement(tabPlacement);
        }else{
            setTabPlacement(tabP);
        }
        press = new SwipeListener(SwipeListener.PRESS);
        drag = new SwipeListener(SwipeListener.DRAG);
        release = new SwipeListener(SwipeListener.RELEASE);
        setUIID("Tabs");
        BorderLayout bd = (BorderLayout)super.getLayout();
        if(bd != null) {
            if(UIManager.getInstance().isThemeConstant("tabsOnTopBool", false)) {
                bd.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_TOTAL_BELOW);
            } else {
                bd.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_SCALE);
            }
        }
    }

    private void checkTabsCanBeSeen() {
        if(UIManager.getInstance().isThemeConstant("tabsOnTopBool", false)) {
            for(int iter = 0 ; iter < getTabCount() ; iter++) {
                Component c = getTabComponentAt(iter);
                if(c.isScrollableY()) {
                    if(c.getStyle().getPadding(BOTTOM) < tabsContainer.getPreferredH()) {
                        c.getStyle().setPadding(BOTTOM, tabsContainer.getPreferredH());
                    }
                }
            }
        }
    }
    
    /**
     * @inheritDoc
     */
    protected void initLaf(UIManager manager) {
        super.initLaf(manager);
        int tabPlace = manager.getThemeConstant("tabPlacementInt", -1);
        tabsFillRows = manager.isThemeConstant("tabsFillRowsBool", false);
        tabsGridLayout = manager.isThemeConstant("tabsGridBool", false);
        changeTabOnFocus = manager.isThemeConstant("changeTabOnFocusBool", false);
        BorderLayout bd = (BorderLayout)super.getLayout();
        if(bd != null) {
            if(manager.isThemeConstant("tabsOnTopBool", false)) {
                if(bd.getCenterBehavior() != BorderLayout.CENTER_BEHAVIOR_TOTAL_BELOW) {
                    bd.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_TOTAL_BELOW);
                    checkTabsCanBeSeen();
                }
            } else {
                bd.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_SCALE);
            }
        }
        changeTabContainerStyleOnFocus =  manager.isThemeConstant("changeTabContainerStyleOnFocusBool", false);
        if(tabPlace != -1){
            tabPlacement = tabPlace;
        }
    }


    
    /**
     * @inheritDoc
     */
    void initComponentImpl() {
        super.initComponentImpl();
        getComponentForm().registerAnimatedInternal(this);
        if(changeTabContainerStyleOnFocus && Display.getInstance().shouldRenderSelection()) {
            Component f = getComponentForm().getFocused();
            if(f != null && f.getParent() == tabsContainer) {
                initTabsContainerStyle();
                tabsContainer.setUnselectedStyle(originalTabsContainerSelected);
                tabsContainer.repaint();
            }
        }
    }

    /**
     * @inheritDoc
     */
    public void refreshTheme(boolean merge) {
        super.refreshTheme(merge);
        originalTabsContainerSelected = null;
        originalTabsContainerUnselected = null;
    }

    /**
     * @inheritDoc
     */
    protected void deinitialize() {
        Form form = this.getComponentForm();
        if (form != null) {
            form.removePointerPressedListener(press);
            form.removePointerReleasedListener(release);
            form.removePointerDraggedListener(drag);
        }
        super.deinitialize();
    }

    /**
     * @inheritDoc
     */
    protected void initComponent() {
        super.initComponent();
        Form form = this.getComponentForm();
        if (form != null && swipeActivated) {
            form.addPointerPressedListener(press);
            form.addPointerReleasedListener(release);
            form.addPointerDraggedListener(drag);
        }
    }

    /**
     * @inheritDoc
     */
    public boolean animate() {
        boolean b = super.animate();
        if (slideToDestMotion != null && swipeActivated) {
            int motionX = slideToDestMotion.getValue();
            final int size = contentPane.getComponentCount();
            int tabWidth = contentPane.getWidth() - tabsGap*2;
            for (int i = 0; i < size; i++) {
                int xOffset;
                if(isRTL()) {
                    xOffset = (size - i) * tabWidth;
                    xOffset -= ((size - active) * tabWidth);
                } else {
                    xOffset = i * tabWidth;
                    xOffset -= (active * tabWidth);
                }
                xOffset += motionX;
                Component component = contentPane.getComponentAt(i);
                component.setX(xOffset);
            }
            if (slideToDestMotion.isFinished()) {
                for (int i = 0; i < contentPane.getComponentCount() ; i++) {
                    Component component = contentPane.getComponentAt(i);
                    component.paintLockRelease();
                }
                slideToDestMotion = null;
                deregisterAnimatedInternal();
                setSelectedIndex(active);
            }
            return true;
        }
        return b;
    }

    void deregisterAnimatedInternal() {
        if (slideToDestMotion == null || (slideToDestMotion.isFinished())) {
            Form f = getComponentForm();
            if (f != null) {
                f.deregisterAnimatedInternal(this);
            }
        }
    }

    /**
     * Sets the position of the text relative to the icon if exists
     *
     * @param textPosition alignment value (LEFT, RIGHT, BOTTOM or TOP)
     * @see #LEFT
     * @see #RIGHT
     * @see #BOTTOM
     * @see #TOP
     */
    public void setTabTextPosition(int textPosition) {
        if (textPosition != LEFT && textPosition != RIGHT && textPosition != BOTTOM && textPosition != TOP) {
            throw new IllegalArgumentException("Text position can't be set to " + textPosition);
        }
        this.textPosition = textPosition;
        for(int iter = 0 ; iter < getTabCount() ; iter++) {
            Button b = (Button)tabsContainer.getComponentAt(iter);
            b.setTextPosition(textPosition);
        }
    }


    /**
     * Returns The position of the text relative to the icon
     *
     * @return The position of the text relative to the icon, one of: LEFT, RIGHT, BOTTOM, TOP
     * @see #LEFT
     * @see #RIGHT
     * @see #BOTTOM
     * @see #TOP
     */
    public int getTabTextPosition(){
        return textPosition;
    }

    /**
     * Sets the tab placement for this tabbedpane.
     * Possible values are:<ul>
     * <li><code>Component.TOP</code>
     * <li><code>Component.BOTTOM</code>
     * <li><code>Component.LEFT</code>
     * <li><code>Component.RIGHT</code>
     * </ul>
     * The default value, if not set, is <code>Component.TOP</code>.
     *
     * @param tabPlacement the placement for the tabs relative to the content
     */
    public void setTabPlacement(int tabPlacement) {
        if (tabPlacement != TOP && tabPlacement != LEFT &&
                tabPlacement != BOTTOM && tabPlacement != RIGHT) {
            throw new IllegalArgumentException("illegal tab placement: must be TOP, BOTTOM, LEFT, or RIGHT");
        }
        if (this.tabPlacement == tabPlacement && tabsContainer.getParent() == null && isInitialized()) {
            return;
        }
        this.tabPlacement = tabPlacement;
        removeComponent(tabsContainer);

        setTabsLayout(tabPlacement);

        if (tabPlacement == TOP) {
            super.addComponent(BorderLayout.NORTH, tabsContainer);
        } else if (tabPlacement == BOTTOM) {
            super.addComponent(BorderLayout.SOUTH, tabsContainer);
        } else if (tabPlacement == LEFT) {
            super.addComponent(BorderLayout.WEST, tabsContainer);
        } else {// RIGHT
            super.addComponent(BorderLayout.EAST, tabsContainer);
        }

        initTabsFocus();

        tabsContainer.setShouldCalcPreferredSize(true);
        contentPane.setShouldCalcPreferredSize(true);

        revalidate();
    }

    /**
     * Adds a <code>component</code>
     * represented by a <code>title</code> and/or <code>icon</code>,
     * either of which can be <code>null</code>.
     * Cover method for <code>insertTab</code>.
     *
     * @param title the title to be displayed in this tab
     * @param icon the icon to be displayed in this tab
     * @param component the component to be displayed when this tab is clicked
     *
     * @see #insertTab
     * @see #removeTabAt
     */
    public void addTab(String title, Image icon, Component component) {
        insertTab(title, icon, component, tabsContainer.getComponentCount());
    }

    /**
     * Adds a <code>component</code>
     * represented by a <code>title</code> and no <code>icon</code>.
     * Cover method for <code>insertTab</code>.
     *
     * @param title the title to be displayed in this tab
     * @param component the component to be displayed when this tab is clicked
     *
     * @see #insertTab
     * @see #removeTabAt
     */
    public void addTab(String title, Component component) {
        insertTab(title, null, component, tabsContainer.getComponentCount());
    }
    
    /**
     * Adds a <code>component</code>
     * represented by a <code>button</code>.
     * Cover method for <code>insertTab</code>.
     * The Button styling will be associated with "Tab" UIID.
     *
     * @param tab represents the tab on top
     * @param component the component to be displayed when this tab is clicked
     *
     * @see #insertTab
     * @see #removeTabAt
     * @deprecated should use radio button as an argument
     */
    public void addTab(Button tab, Component component) {
        insertTab(tab, component, tabsContainer.getComponentCount());
    }

    /**
     * Inserts a <code>component</code>, at <code>index</code>,
     * represented by a <code>title</code> and/or <code>icon</code>,
     * either of which may be <code>null</code>.
     * Uses java.util.Vector internally, see <code>insertElementAt</code>
     * for details of insertion conventions.
     *
     * @param title the title to be displayed in this tab
     * @param icon the icon to be displayed in this tab
     * @param component The component to be displayed when this tab is clicked.
     * @param index the position to insert this new tab
     *
     * @see #addTab
     * @see #removeTabAt
     */
    public void insertTab(String title, Image icon, Component component,
            int index) {
        RadioButton b = new RadioButton(title != null ? title : "", icon);
        b.setToggle(true);
        radioGroup.add(b);
        b.setTextPosition(BOTTOM);
        if(radioGroup.getButtonCount() == 1) {
            b.setSelected(true);
        }
        if(textPosition != -1) {
            b.setTextPosition(textPosition);
        }
        insertTab(b, component, index);
    }

    /**
     * Inserts a <code>component</code>, at <code>index</code>,
     * represented by a <code>button</code>
     * Uses java.util.Vector internally, see <code>insertElementAt</code>
     * for details of insertion conventions.
     * The Button styling will be associated with "Tab" UIID.
     *
     * @param tab represents the tab on top
     * @param component The component to be displayed when this tab is clicked.
     * @param index the position to insert this new tab
     *
     * @see #addTab
     * @see #removeTabAt
     * @deprecated should use radio button as an argument
     */
    public void insertTab(Button tab, Component component,
            int index) {
        checkIndex(index);
        if (component == null) {
            return;
        }
        final Button b = tab;
        b.setUIID("Tab");

        if(b.getIcon() == null) {
            Image d = getUIManager().getThemeImageConstant("TabUnselectedImage");
            if(d != null) {
                b.setIcon(d);
                d = getUIManager().getThemeImageConstant("TabSelectedImage");
                if(d != null) {
                    b.setRolloverIcon(d);
                    b.setPressedIcon(d);
                }
            }
        }

        b.addFocusListener(focusListener);
        
        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {

                if(selectedTab != null){
                    selectedTab.setUIID("Tab");
                    selectedTab.setShouldCalcPreferredSize(true);
                    selectedTab.repaint();
                    int previousSelectedIndex = tabsContainer.getComponentIndex(selectedTab);
                    
                    // this might happen if a tab was removed
                    if(previousSelectedIndex != -1) {
                        Component previousContent = contentPane.getComponentAt(previousSelectedIndex);
                        if (previousContent instanceof Container) {
                            ((Container) previousContent).setBlockFocus(true);
                        }
                    }
                }
                active = tabsContainer.getComponentIndex(b);                
                Component content = contentPane.getComponentAt(active);
                if (content instanceof Container) {
                    ((Container) content).setBlockFocus(false);
                }
                setSelectedIndex(active);
                initTabsFocus();
                selectedTab = b;
                selectedTab.setShouldCalcPreferredSize(true);
                tabsContainer.revalidate();
                tabsContainer.scrollComponentToVisible(selectedTab);
                contentPane.revalidate();
            }
        });

        if (component instanceof Container) {
            ((Container) component).setBlockFocus(true);
        }

        tabsContainer.addComponent(index, b);
        contentPane.addComponent(index, component);
        setTabsLayout(tabPlacement);
        if (tabsContainer.getComponentCount() == 1) {
            selectedTab = (Button) tabsContainer.getComponentAt(0);
            if (component instanceof Container) {
                ((Container) component).setBlockFocus(false);
            }
            initTabsFocus();
        }
        checkTabsCanBeSeen();
    }

    /**
     * Updates the information about the tab details
     *
     * @param title the title to be displayed in this tab
     * @param icon the icon to be displayed in this tab
     * @param index the position to insert this new tab
     */
    public void setTabTitle(String title, Image icon, int index) {
        checkIndex(index);
        Button b = (Button)tabsContainer.getComponentAt(index);
        b.setText(title);
        b.setIcon(icon);
    }

    /**
     * Returns the title of the tab at the given index
     * 
     * @param index index for the tab
     * @return label of the tab at the given index
     */
    public String getTabTitle(int index) {
        checkIndex(index);
        return ((Button)tabsContainer.getComponentAt(index)).getText();
    }

    /**
     * Returns the icon of the tab at the given index
     *
     * @param index index for the tab
     * @return icon of the tab at the given index
     */
    public Image getTabIcon(int index) {
        checkIndex(index);
        return ((Button)tabsContainer.getComponentAt(index)).getIcon();
    }


    /**
     * Returns the icon of the tab at the given index
     *
     * @param index index for the tab
     * @return icon of the tab at the given index
     */
    public Image getTabSelectedIcon(int index) {
        checkIndex(index);
        return ((Button)tabsContainer.getComponentAt(index)).getPressedIcon();
    }

    /**
     * Sets the selected icon of the tab at the given index
     *
     * @param index index for the tab
     * @param icon of the tab at the given index
     */
    public void setTabSelectedIcon(int index, Image icon) {
        checkIndex(index);
        ((Button)tabsContainer.getComponentAt(index)).setPressedIcon(icon);
    }

    /**
     * Removes the tab at <code>index</code>.
     * After the component associated with <code>index</code> is removed,
     * its visibility is reset to true to ensure it will be visible
     * if added to other containers.
     * @param index the index of the tab to be removed
     * @exception IndexOutOfBoundsException if index is out of range
     *            (index < 0 || index >= tab count)
     *
     * @see #addTab
     * @see #insertTab
     */
    public void removeTabAt(int index) {
        checkIndex(index);
        int act = activeComponent - 1;
        act = Math.max(act, 0);
        setSelectedIndex(act);
        Component key = tabsContainer.getComponentAt(index);
        tabsContainer.removeComponent(key);
        Component content = contentPane.getComponentAt(index);
        contentPane.removeComponent(content);
        setTabsLayout(tabPlacement);
    }

    /**
     * Returns the tab at <code>index</code>.
     *
     * @param index the index of the tab to be removed
     * @exception IndexOutOfBoundsException if index is out of range
     *            (index < 0 || index >= tab count)
     * @return the component at the given tab location
     * @see #addTab
     * @see #insertTab
     */
    public Component getTabComponentAt(int index) {
        checkIndex(index);
        return contentPane.getComponentAt(index);
    }

    private void checkIndex(int index) {
        if (index < 0 || index > tabsContainer.getComponentCount()) {
            throw new IndexOutOfBoundsException("Index: " + index);
        }
    }

    /**
     * Returns the index of the tab for the specified component.
     * Returns -1 if there is no tab for this component.
     *
     * @param component the component for the tab
     * @return the first tab which matches this component, or -1
     *		if there is no tab for this component
     */
    public int indexOfComponent(Component component) {
        return contentPane.getComponentIndex(component);
    }

    /**
     * Returns the number of tabs in this <code>tabbedpane</code>.
     *
     * @return an integer specifying the number of tabbed pages
     */
    public int getTabCount() {
        return tabsContainer.getComponentCount();
    }

    /**
     * Returns the currently selected index for this tabbedpane.
     * Returns -1 if there is no currently selected tab.
     *
     * @return the index of the selected tab
     */
    public int getSelectedIndex() {
        if(tabsContainer != null){
            return activeComponent;
        }
        return -1;
    }

    /**
     * Returns the component associated with the tab at the given index
     *
     * @return the component is now showing in the tabbed pane
     */
    public Component getSelectedComponent() {
        int i = getSelectedIndex();
        if(i == -1) {
            return null;
        }
        return getTabComponentAt(i);
    }

    /**
     * Adds a focus listener to the tabs buttons
     * 
     * @deprecated use addSelectionListener instead
     * @param listener FocusListener
     */
    public void addTabsFocusListener(FocusListener listener){
        if(focusListeners == null){
            focusListeners = new EventDispatcher();
        }
        focusListeners.addListener(listener);
    }
    
    
     
    /**
     * Removes a foucs Listener from the tabs buttons
     * 
     * @deprecated use addSelectionListener instead
     * @param listener FocusListener
     */
    public void removeTabsFocusListener(FocusListener listener){
        if(focusListeners != null){
            focusListeners.removeListener(listener);
        }
    }
    
    /**
     * Adds a selection listener to the tabs.
     * 
     * @param listener SelectionListener
     */
    public void addSelectionListener(SelectionListener listener){
        if(selectionListener == null){
            selectionListener = new EventDispatcher();
        }
        selectionListener.addListener(listener);
    }
    
    
     
    /**
     * Removes a selection Listener from the tabs
     * 
     * @param listener SelectionListener
     */
    public void removeSelectionListener(SelectionListener listener){
        if(selectionListener != null){
            selectionListener.removeListener(listener);
        }
    }
    

    /**
     * @inheritDoc
     */
    public String toString() {
        String className = getClass().getName();
        className = className.substring(className.lastIndexOf('.') + 1);
        return className + "[x=" + getX() + " y=" + getY() + " width=" +
                getWidth() + " height=" + getHeight() + ", tab placement = " +
                tabPlacement + ", tab count = " + getTabCount() +
                ", selected index = " + getSelectedIndex() + "]";
    }

    /**
     * Returns the placement of the tabs for this tabbedpane.
     *
     * @return the tab placement value
     * @see #setTabPlacement
     */
    public int getTabPlacement() {
        return tabPlacement;
    }

    /**
     * This method retrieves the Tabs content pane
     *
     * @return the content pane Container
     */
    public Container getContentPane(){
        return contentPane;
    }

    /**
     * This method retrieves the Tabs buttons Container
     *
     * @return the Tabs Container
     */
    public Container getTabsContainer(){
        return tabsContainer;
    }

    /**
     * Sets the selected index for this tabbedpane. The index must be a valid
     * tab index.
     * @param index the index to be selected
     * @throws IndexOutOfBoundsException if index is out of range
     * (index < 0 || index >= tab count)
     */
    public void setSelectedIndex(int index) {        
        if (index < 0 || index >= tabsContainer.getComponentCount()) {
            throw new IndexOutOfBoundsException("Index: "+index+", Tab count: "+tabsContainer.getComponentCount());
        }
        if(index == activeComponent){
            return;
        }
        if(selectionListener != null){
            selectionListener.fireSelectionEvent(activeComponent, index);
        }
        activeComponent = index;
        Button b = (Button)tabsContainer.getComponentAt(index);
        b.fireClicked();
        b.requestFocus();
    }

    /**
     * Hide the tabs bar
     */
    public void hideTabs(){
        removeComponent(tabsContainer);
        revalidate();
    }
    
    /**
     * Show the tabs bar if it was hidden
     */
    public void showTabs(){
        int tp = tabPlacement;
        tabPlacement = -1;
        setTabPlacement(tp);
        revalidate();
    }

    /**
     * Returns true if the swipe between tabs is activated, this is relevant for
     * touch devices only
     *
     * @return swipe activated flag
     */
    public boolean isSwipeActivated() {
        return swipeActivated;
    }

    /**
     * Setter method for swipe mode
     *
     * @param swipeActivated
     */
    public void setSwipeActivated(boolean swipeActivated) {
        this.swipeActivated = swipeActivated;
    }

    private void initTabsFocus(){
        for (int i = 0; i < tabsContainer.getComponentCount(); i++) {
            initTabFocus(tabsContainer.getComponentAt(i), contentPane.getComponentAt(activeComponent));
        }

    }

    private void initTabFocus(Component tab, Component content) {
        Component focus = null;
        if (content.isFocusable()) {
            focus = content;
        }

        if (content instanceof Container) {
            focus = ((Container) content).findFirstFocusable();
        }

    }

    /**
     * Indicates that a tab should change when the focus changes without the user physically pressing a button
     * @return the changeTabOnFocus
     */
    public boolean isChangeTabOnFocus() {
        return changeTabOnFocus;
    }

    /**
     * Indicates that a tab should change when the focus changes without the user physically pressing a button
     * @param changeTabOnFocus the changeTabOnFocus to set
     */
    public void setChangeTabOnFocus(boolean changeTabOnFocus) {
        this.changeTabOnFocus = changeTabOnFocus;
    }

    /**
     * Indicates that the tabs container should have its style changed to the selected style when one of the tabs has focus
     * this allows incorporating it into the theme of the application
     * @return the changeTabContainerStyleOnFocus
     */
    public boolean isChangeTabContainerStyleOnFocus() {
        return changeTabContainerStyleOnFocus;
    }

    /**
     * Indicates that the tabs container should have its style changed to the selected style when one of the tabs has focus
     * this allows incorporating it into the theme of the application
     * @param changeTabContainerStyleOnFocus the changeTabContainerStyleOnFocus to set
     */
    public void setChangeTabContainerStyleOnFocus(boolean changeTabContainerStyleOnFocus) {
        this.changeTabContainerStyleOnFocus = changeTabContainerStyleOnFocus;
    }
    
    /**
     * This method allows setting the Tabs content pane spacing (right and left), 
     * This can be used to create an effect where the selected tab is smaller 
     * and the right and left tabs are visible on the sides
     * @param tabsGap the gap on the sides of the content in pixels, the value must
     * be positive.
     */
    public void setTabsContentGap(int tabsGap){
        if(tabsGap < 0){
            throw new IllegalArgumentException("gap must be positive");
        }
        this.tabsGap = tabsGap;
    }

    private void setTabsLayout(int tabPlacement) {
        if (tabPlacement == TOP || tabPlacement == BOTTOM) {
            if(tabsFillRows) {
                FlowLayout f = new FlowLayout();
                f.setFillRows(true);
                tabsContainer.setLayout(f);
            } else {
                if(tabsGridLayout) {
                    tabsContainer.setLayout(new GridLayout(1, Math.max(1 ,getTabCount())));
                } else {
                    tabsContainer.setLayout(new BoxLayout(BoxLayout.X_AXIS));
                }
            }
            tabsContainer.setScrollableX(true);
            tabsContainer.setScrollableY(false);
        } else {// LEFT Or RIGHT
                if(tabsGridLayout) {
                    tabsContainer.setLayout(new GridLayout(Math.max(1 ,getTabCount()), 1));
                } else {
                    tabsContainer.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
                }
            tabsContainer.setScrollableX(false);
            tabsContainer.setScrollableY(true);
        }
    }


    class TabsLayout extends Layout{

        public void layoutContainer(Container parent) {
            final int size = parent.getComponentCount();
           
            int tabWidth = parent.getWidth() - tabsGap*2;
            for (int i = 0; i < size; i++) {
                int xOffset;
                if(parent.isRTL()) {
                    xOffset = (size - i) * tabWidth + tabsGap;
                    xOffset -= ((size - activeComponent) * tabWidth);
                } else {
                    xOffset = i * tabWidth + tabsGap;
                    xOffset -= (activeComponent * tabWidth);
                }
                Component component = parent.getComponentAt(i);
                component.setX(component.getStyle().getMargin(Component.LEFT) + xOffset);
                component.setY(component.getStyle().getMargin(Component.TOP));
                component.setWidth(tabWidth - component.getStyle().getMargin(Component.LEFT)
                        - component.getStyle().getMargin(Component.RIGHT));
                component.setHeight(parent.getHeight() - component.getStyle().getMargin(Component.TOP)
                        - component.getStyle().getMargin(Component.BOTTOM));
            }

        }

        public Dimension getPreferredSize(Container parent) {
            // fill
            Dimension dim = new Dimension(0, 0);
            dim.setWidth(parent.getWidth() + parent.getStyle().getPadding(false, Component.LEFT)
                    + parent.getStyle().getPadding(false, Component.RIGHT));
            dim.setHeight(parent.getHeight() + parent.getStyle().getPadding(false, Component.TOP)
                    + parent.getStyle().getPadding(false, Component.BOTTOM));
            int compCount = contentPane.getComponentCount();
            for(int iter = 0 ; iter < compCount ; iter++) {
                Dimension d = contentPane.getComponentAt(iter).getPreferredSizeWithMargin();
                dim.setWidth(Math.max(d.getWidth(), dim.getWidth()));
                dim.setHeight(Math.max(d.getHeight(), dim.getHeight()));
            }
            return dim;
        }
    }

    void initTabsContainerStyle() {
        if(originalTabsContainerSelected == null) {
            originalTabsContainerSelected = tabsContainer.getSelectedStyle();
            originalTabsContainerUnselected = tabsContainer.getUnselectedStyle();
        }
    }
    
    class TabFocusListener implements FocusListener{

        public void focusGained(Component cmp) {
            if(focusListeners != null){
                focusListeners.fireFocus(cmp);
            }
            if(Display.getInstance().shouldRenderSelection()) {
                if(isChangeTabOnFocus()) {
                    if(!((Button)cmp).isSelected()) {
                        ((Button)cmp).fireClicked();
                    }
                }
                if(changeTabContainerStyleOnFocus) {
                    initTabsContainerStyle();
                    tabsContainer.setUnselectedStyle(originalTabsContainerSelected);
                    tabsContainer.repaint();
                }
            }
        }


        public void focusLost(Component cmp) {
            if(focusListeners != null){
                focusListeners.fireFocus(cmp);
            }
            if(changeTabContainerStyleOnFocus) {
                initTabsContainerStyle();
                tabsContainer.setUnselectedStyle(originalTabsContainerUnselected);
                tabsContainer.repaint();
            }
        }
    
    }
    
    
    class SwipeListener implements ActionListener{

        private final static int PRESS = 0;
        private final static int DRAG = 1;
        private final static int RELEASE = 2;
        private final int type;
        private boolean blockSwipe;
        private boolean riskySwipe;

        public SwipeListener(int type) {
            this.type = type;
        }

        public void actionPerformed(ActionEvent evt) {
            
            if (getComponentCount() == 0 || !swipeActivated ||animate()) {
                return;
            }
            final int x = evt.getX();
            final int y = evt.getY();
            switch (type) {
                case PRESS: {
                    blockSwipe = false;
                    riskySwipe = false;
                    if (contentPane.contains(x, y)) {
                        Component testCmp = contentPane.getComponentAt(x, y);
                        if(testCmp != null && testCmp != contentPane) {
                            while(testCmp != null && testCmp != contentPane) {
                                if(testCmp.shouldBlockSideSwipe()) {
                                    lastX = -1;
                                    initialX = -1;
                                    blockSwipe = true;
                                    return;
                                }
                                if(testCmp.isScrollable()) {
                                    if(testCmp.isScrollableX()) {
                                        // we need to block swipe since the user is trying to scroll a component
                                        lastX = -1;
                                        initialX = -1;
                                        blockSwipe = true;
                                        return;
                                    }

                                    // scrollable Y component, we want to make side scrolling
                                    // slightly harder so it doesn't bother the vertical swipe
                                    riskySwipe = true;
                                    break;
                                }
                                testCmp = testCmp.getParent();
                            }
                        }
                        lastX = x;
                        initialX = x;
                        initialY = y;
                    } else {
                        lastX = -1;
                        initialX = -1;
                        initialY = -1;
                    }
                    dragStarted = false;
                    break;
                }
                case DRAG: {
                    if(blockSwipe) {
                        return;
                    }
                    if (!dragStarted) {
                        if(riskySwipe) {
                            if(Math.abs(x - initialX) < Math.abs(y - initialY)) {
                                return;
                            }
                            // give heavier weight when we have two axis swipe
                            dragStarted = Math.abs(x - initialX) > (contentPane.getWidth() / 5);
                        } else {
                            // start drag not imediately, giving components some sort
                            // of weight.
                            dragStarted = Math.abs(x - initialX) > (contentPane.getWidth() / 8);
                        }
                    }
                    if (initialX != -1 && contentPane.contains(x, y)) {
                        int diffX = x - lastX;
                        if (diffX != 0 && dragStarted) {
                            lastX += diffX;
                            final int size = contentPane.getComponentCount();
                            for (int i = 0; i < size; i++) {
                                Component component = contentPane.getComponentAt(i);
                                component.setX(component.getX() + diffX);
                                component.paintLock(false);
                            }
                            repaint();
                        }
                    }
                    break;
                }
                case RELEASE: {
                    if(changeTabContainerStyleOnFocus) {
                        initTabsContainerStyle();
                        tabsContainer.setUnselectedStyle(originalTabsContainerUnselected);
                        tabsContainer.repaint();
                    }
                    if(blockSwipe) {
                        return;
                    }
                    if (initialX != -1) {
                        int diff = x - initialX;
                        if (diff != 0 && dragStarted) {
                            if (Math.abs(diff) > contentPane.getWidth() / 6) {
                                if(isRTL()) {
                                    diff *= -1;
                                }
                                if (diff > 0) {
                                    active = activeComponent - 1;
                                    if (active < 0) {
                                        active = 0;
                                    }
                                } else {
                                    active = activeComponent + 1;
                                    if (active >= contentPane.getComponentCount()) {
                                        active = contentPane.getComponentCount() - 1;
                                    }
                                }
                            }
                            int start = contentPane.getComponentAt(active).getX();
                            int end = tabsGap;
                            slideToDestMotion = Motion.createSplineMotion(start, end, 250);
                            slideToDestMotion.start();
                            Form form = getComponentForm();
                            if (form != null) {
                                form.registerAnimatedInternal(Tabs.this);
                            }
                        }
                    }
                    lastX = -1;
                    initialX = -1;
                    dragStarted = false;
                    break;
                }
            }
        }
    }

    /**
     * @inheritDoc
     */
    public String[] getPropertyNames() {
        return new String[] {"titles", "icons", "selectedIcons"};
    }

    /**
     * @inheritDoc
     */
    public Class[] getPropertyTypes() {
       return new Class[] {com.codename1.impl.CodenameOneImplementation.getStringArrayClass(), 
           com.codename1.impl.CodenameOneImplementation.getImageArrayClass(), 
           com.codename1.impl.CodenameOneImplementation.getImageArrayClass()};
    }
    
    /**
     * @inheritDoc
     */
    public String[] getPropertyTypeNames() {
        return new String[] {"String[]", "Image[]", "Image[]"};
    }

    /**
     * @inheritDoc
     */
    public Object getPropertyValue(String name) {
        if(name.equals("titles")) {
            String[] t = new String[getTabCount()];
            for(int iter = 0 ; iter < t.length ; iter++) {
                t[iter] = getTabTitle(iter);
            }
            return t;
        }
        if(name.equals("icons")) {
            Image[] t = new Image[getTabCount()];
            for(int iter = 0 ; iter < t.length ; iter++) {
                t[iter] = getTabIcon(iter);
            }
            return t;
        }
        if(name.equals("selectedIcons")) {
            Image[] t = new Image[getTabCount()];
            for(int iter = 0 ; iter < t.length ; iter++) {
                t[iter] = getTabSelectedIcon(iter);
            }
            return t;
        }
        return null;
    }


    /**
     * @inheritDoc
     */
    public String setPropertyValue(String name, Object value) {
        if(name.equals("titles")) {
            String[] t = (String[])value;
            for(int iter = 0 ; iter < Math.min(getTabCount(), t.length) ; iter++) {
                setTabTitle(t[iter], getTabIcon(iter), iter);
            }
            return null;
        }
        if(name.equals("icons")) {
            Image[] t = (Image[])value;
            for(int iter = 0 ; iter < Math.min(getTabCount(), t.length) ; iter++) {
                setTabTitle(getTabTitle(iter), t[iter], iter);
            }
            return null;
        }
        if(name.equals("selectedIcons")) {
            Image[] t = (Image[])value;
            for(int iter = 0 ; iter < Math.min(getTabCount(), t.length) ; iter++) {
                setTabSelectedIcon(iter, t[iter]);
            }
            return null;
        }
        return super.setPropertyValue(name, value);
    }
}
