/*******************************************************************************
* Copyright (c)                                                                *
*           2005   Tristan Allwood,                                            *
*           2005   Daniel Burke,                                               *
*           2005   Marc Hull,                                                  *
*           2005   Ekaterina Itskova,                                          *
*           2005   Steve Zymler                                                *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a      *
* copy of this software and associated documentation files (the                *
* "Software"), to deal in the Software without restriction, including          *
* without limitation the rights to use, copy, modify, merge, publish,          *
* distribute, sublicense, and/or sell copies of the Software, and to           *
* permit persons to whom the Software is furnished to do so, subject to        *
* the following conditions:                                                    *
*                                                                              *
* The above copyright notice and this permission notice shall be included      *
* in all copies or substantial portions of the Software.                       *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS      *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF                   *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.       *
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY         *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,         *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE            *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                       *
*******************************************************************************/
/*
 * Created on 19-Nov-2004
 */
package uk.ac.ic.doc.kevlar.gui.internal.widgets.tab;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;

import uk.ac.ic.doc.kevlar.gui.internal.ADrawableObject;
import uk.ac.ic.doc.kevlar.gui.internal.AMouseListenerObject;
import uk.ac.ic.doc.kevlar.gui.internal.caches.ColorCache;
import uk.ac.ic.doc.kevlar.gui.internal.caches.FontCache;
import uk.ac.ic.doc.kevlar.gui.internal.caches.Utils;

/**
 * @author Daniel Burke
 *
 */
public class TabContainer<T extends ADrawableObject> extends AMouseListenerObject implements Iterable<Tab<T>> {
    

    
	private List<Tab<T>> tabs;
	boolean updateNeeded = true;
	//private int selected = 0;
    private Tab<T> selectedTab = null;
    private T contents = null;
    private Set<ITabListener<T>> listeners = new HashSet<ITabListener<T>>();
    

    
	public TabContainer()
	{
	    tabs = new ArrayList<Tab<T>>(); 
	}
	
    public void addTabListener(ITabListener<T> listener)
    {
        listeners.add(listener);
    }
    
    public void removeTabListener(ITabListener<T> listener)
    {
        listeners.remove(listener);
    }
	
	private synchronized void updateTabStates()
	{
	    if(tabs.size() == 0)
	    {
	        return;
	    }
	    
	    int index = 0;
	    int selectedIndex = tabs.indexOf(selectedTab);
	    for(Tab<T> tab : tabs)
	    {
	        tab.setDistanceFromSelected(index - selectedIndex);
	        
	        index++;
	    }
	    
	    updateNeeded = true;
	}
	
	public synchronized void setSelected(Tab<T> newSelected)
	{
	    if(!tabs.contains(newSelected))
	    {
	        throw new IllegalArgumentException("Tab newSelected must be already added");
	    }
	    
	    changedSelected(newSelected);
	    updateTabStates();
	    redraw();
	}
	
	private synchronized void changedSelected(Tab<T> newSelected)
	{
	    selectedTab = newSelected;
	    if(contents != null)
	    {
	    	contents.hide();
	    	removeChild(contents);
	    }
	    
	    if(newSelected != null)
	    {
		    contents = newSelected.getContents();
		    addChild(contents);
		    
		    contents.setPosition(new Point(0, Tab.largeTabHeight));
		    contents.setSizeFillBoth(new Point(0, 0));
		    if(isVisible())
		    {
		        contents.show();
		    }
		    else
		    {
		        contents.hide();
		    }
		      
		    for(ITabListener<T> listener : listeners)
		    {
		        listener.TabSelected(newSelected);
		    }
	    }
	}
	
	public synchronized void addTab(Tab<T> t)
	{
	    if(tabs.contains(t) || !t.isRemoved())
	    {
	        throw new IllegalArgumentException("Tab t is already added");
	    }
	    
	    if(t == null)
	    {
	        throw new IllegalArgumentException("Tab t may not be null");
	    }
	    
	    tabs.add(t);
	    t.setTabContainer(this);
	    
	    Collections.sort(tabs, new Comparator<Tab<T>>() {

            public int compare(Tab<T> arg0, Tab<T> arg1) {
                return arg0.getName().compareTo(arg1.getName());
            }
	        
	    });
	    
	    if(tabs.size() == 1)
	    {
	        changedSelected(t);
	    }
	    
	    addChild(t);
	    updateTabStates();
	    
	    redraw();
	}
	
	public void removeSelectedTab() {
	    
	    if (selectedTab != null) {
	        
	        removeTab(selectedTab);
	        
	    }
	    
	}
	
	public synchronized void removeTab(Tab<T> t)
	{
	    if(!tabs.contains(t))
	    {
	        throw new IllegalArgumentException("t not a member of tabs");
	    }
	    
	    int currentIndex = tabs.indexOf(t);   
	    
	    tabs.remove(t);
	    t.setTabContainer(null);
	    
	    if(t == selectedTab)
	    {
	        if(currentIndex >= tabs.size())
	        {
	            currentIndex--;
	            if(currentIndex < 0)
	            {
	                currentIndex = 0;
	            }
	        }
	        
	        if(currentIndex < tabs.size())
	        {
	            changedSelected(tabs.get(currentIndex));
	        }
	        else
	        {
	            changedSelected(null);
	        }
	    }
	    
	    removeChild(t);
	    updateTabStates();
	    
	    for(ITabListener<T> listener : listeners)
	    {
	        listener.TabRemoved(t);
	    }
	    
	    redraw();
	}
	
    public synchronized Iterator<Tab<T>> iterator() {
        return new TabIterator();
    }
    
    private class TabIterator implements Iterator<Tab<T>>
    {
        public Iterator<Tab<T>> backend = tabs.iterator();
        
        public boolean hasNext() {
            return backend.hasNext();
        }

        public Tab<T> next() {
            return backend.next();
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
        
    }
	
	private void updateTabs(GC gc)
	{
	    gc.setFont(FontCache.get("Arial", 10, SWT.BOLD));
	    int position = 2;
	    
	    for(Tab<T> tab : tabs)
	    {
	        tab.refreshTabState(gc);
	        if(tab.getDistanceFromSelected() == 0)
	        {
	            tab.setPosition(new Point(position, 0));
	        }
	        else
	        {
	            tab.setPosition(new Point(position, Tab.depthDifference));
	        }
	        
	        position += tab.getSize().x;
	    }
	}
	
	public synchronized void drawBefore(GC gc, Rectangle redrawArea) {
	    
	    if(updateNeeded)
	    {
	        updateTabs(gc);
	        updateNeeded = false;
	    }
	    
	       //gc.setBackground(ColorCache.get(255,0,0));
	       //gc.fillRectangle(0 - redrawArea.x, 0 - redrawArea.y, getSize().x, getSize().y);
	    
	    //gc.setBackground(ColorCache.get(0, 208, 200));
		//gc.setBackground(ColorCache.get(212, 208, 200));
		//gc.fillRectangle(0 - redrawArea.x, 0 - redrawArea.y, getSize().x, getSize().y);
		
		/*Utils.drawHorizontalBar(gc, "tabPanelTop", new Rectangle(
		        0 - redrawArea.x, 
		        0 - redrawArea.y, 
		        getSize().x, 
		        Tab.largeTabHeight), false, false);*/
    }

    public synchronized int getTabPosition(Tab<T> t)
    {
    	return tabs.indexOf(t);
    }
    
    public synchronized int numberOfTabs()
    {
        return tabs.size();
    }
    
    public synchronized Tab<T> getSelectedTab()
    {
        return selectedTab;
    }
    
    public synchronized Tab<T> getTabAt(int pos)
    {
        return tabs.get(pos);
    }
    
    public void show()
    {
        super.show();
        for(ADrawableObject o : getChildren())
        {
            o.show();
        }
    }
    
    public void hide()
    {
        super.hide();
        for(ADrawableObject o : getChildren())
        {
            o.hide();
        }
    }
}
