/*******************************************************************************
* 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 25-Dec-2004
 *
 */
package uk.ac.ic.doc.kevlar.gui.showers.image;

import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;

import uk.ac.ic.doc.kevlar.Kevlar;
import uk.ac.ic.doc.kevlar.gui.internal.ADrawableObject;
import uk.ac.ic.doc.kevlar.gui.internal.caches.ImageCache;
import uk.ac.ic.doc.kevlar.gui.internal.caches.Utils;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.IClickListener;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.IDeselectListener;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.IScrollingWindow;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.RollOverButtonWithDisable;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.dropdownbox.DropDownBox;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.scrollbars.AScrollbar;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.scrollbars.HorizontalScrollBar;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.scrollbars.VerticalScrollBar;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.swing.ConvertSwtAwt;
import uk.ac.ic.doc.kevlar.gui.showers.AbstractShower;
import uk.ac.ic.doc.kevlar.gui.showers.IShowerProvider;
import uk.ac.ic.doc.kevlar.publicInterfaces.IImageType;
import uk.ac.ic.doc.kevlar.publicInterfaces.IType;

/**
 * @author Daniel Burke
 *
 */
public class ImageShower extends AbstractShower implements IScrollingWindow
{
    private static final int TEXT_OFFSET = 10;
    
    private static final String IMAGE_TOOLBAR = "imageToolbar";
    private static final String BACK_BUTTON = "imageToolbarBack";
    private static final String FORWARD_BUTTON = "imageToolbarForward";
    private static final int imageToolbarHeight;
    private static final int navButtonWidth;
    private static final int navButtonHeight;
    private static final int BUFFER_SPACE = 10;
    static
    {
        int imageToolbarH = 0;
        int navButtonW = 0;
        int navButtonH = 0;
        
        try
        {

            imageToolbarH = ImageCache.get(
                    IMAGE_TOOLBAR + Utils.MIDDLE).getImageData().height;
            
            navButtonW = ImageCache.get(
                    BACK_BUTTON + Utils.NORMAL).getImageData().width;
            
            navButtonH = ImageCache.get(
                    BACK_BUTTON + Utils.NORMAL).getImageData().height;
        }
        catch (FileNotFoundException fnfe)
        {
            if(Kevlar.DEBUG)
            {
                fnfe.printStackTrace();
            }
        }

        imageToolbarHeight = imageToolbarH;
        navButtonWidth = navButtonW;
        navButtonHeight = navButtonH;
    }
    
    private ImageArea imageHolder;
    private ImageClipRegion imageRegion;
    private ViewerRegion viewerRegion;
    private VerticalScrollBar vertScrollbar;
    private HorizontalScrollBar horizScrollbar;
    
    private RollOverButtonWithDisable back;
    private RollOverButtonWithDisable forward;
    private DropDownBox zoomBox;   
    
    private int toolbarY = 0; 
    
    private List<Image> images;
    private int selected = 0;
    
    private double xRatio = 0;
    private double yRatio = 0;
    private double zoom = 1.0;
    
    private boolean needHBar = false;
    private boolean needVBar = false;
    
    public ImageShower(IShowerProvider provider)
    {
        super(provider);
                
        images = new ArrayList<Image>();
        
        viewerRegion = new ViewerRegion();
        viewerRegion.setPosition( new Point(0, imageToolbarHeight) );
        viewerRegion.setSizeFillBoth(new Point(0, 0));
        addChild(viewerRegion);
        
        vertScrollbar = new VerticalScrollBar(this);
        viewerRegion.addChild(vertScrollbar);
        
        horizScrollbar = new HorizontalScrollBar(this);
        viewerRegion.addChild(horizScrollbar);
        
        imageRegion = new ImageClipRegion();
        viewerRegion.addChild(imageRegion);
        
        imageHolder = new ImageArea();
        imageRegion.setImageArea(imageHolder);
        
        back = new RollOverButtonWithDisable(
                BACK_BUTTON + Utils.NORMAL,
                BACK_BUTTON + Utils.ROLLOVER,
                BACK_BUTTON + Utils.ROLLOVER,
                BACK_BUTTON + Utils.NORMAL);
        forward = new RollOverButtonWithDisable(
                FORWARD_BUTTON + Utils.NORMAL,
                FORWARD_BUTTON + Utils.ROLLOVER,
                FORWARD_BUTTON + Utils.ROLLOVER,
                FORWARD_BUTTON + Utils.NORMAL);
        
        back.setPosition( new Point(BUFFER_SPACE, toolbarY + (imageToolbarHeight - navButtonHeight)/2));
        forward.setPosition( new Point(BUFFER_SPACE * 2 + navButtonWidth, toolbarY + (imageToolbarHeight - navButtonHeight)/2));
        
        updateButtons();
        
        back.addListener(new IClickListener() {

            public void clicked()
            {
                synchronized(ImageShower.this)
                {
	                if(selected == 0)
	                {
	                    if(Kevlar.DEBUG)
	                    {
	                        System.out.println("Slightly unexpected selected == 0");
	                    }
	                    return;
	                }
	                
	                setSelected(selected - 1);
                }
            } 
            
        });
        
        forward.addListener(new IClickListener() {

            public void clicked()
            {
                synchronized(ImageShower.this)
                {
	                if(selected == images.size() - 1)
	                {
	                    if(Kevlar.DEBUG)
	                    {
	                        System.out.println("Slightly unexpected selected == 0");
	                    }
	                    return;
	                }
	                
	                setSelected(selected + 1);
                }
            } 
            
        });
        
        addChild(back);
        addChild(forward);
        
        zoomBox = new DropDownBox();
        zoomBox.addOption("50%");
        zoomBox.addOption("75%");
        zoomBox.addOption("100%");
        zoomBox.addOption("150%");
        zoomBox.addOption("200%");
        
        zoomBox.setCurrent(2);
        zoomBox.setSize(new Point(100, 20));
        zoomBox.setPosition(new Point(zoomBox.getSize().x + BUFFER_SPACE, (imageToolbarHeight - zoomBox.getSize().y)/2 ));
        zoomBox.setAlignment(true, false);   
        zoomBox.setDeselectListener( new IDeselectListener()
        {
            public void onDeselect(ADrawableObject object)
            {
                synchronized(ImageShower.this)
                {
                    updateZoom();
                }
            }
        });
        
        addChild(zoomBox);
        
        /*
        try
        {
            //setImage(null);
        }
        catch(FileNotFoundException fnfe)
        {
            if(Kevlar.DEBUG)
            {
                System.err.println("background2.png image not found");
            }
            // do nothing;
        }*/
        
        
        
        update();
    }
    
    void updateZoom()
    {
        String zoom = zoomBox.getCurrent();
        zoom = zoom.substring(0, zoom.length() - 1);
        int zoomPercent = Integer.parseInt(zoom);
        setZoom(zoomPercent / 100.0);
    }
    
    private void updateButtons()
    {
        if(images.size() == 0)
        {
            back.setDisabled(true);
            forward.setDisabled(true);
            return;
        }
        
        if(selected == 0)
        {
            back.setDisabled(true);
        }
        else
        {
            back.setDisabled(false);
        }
        
        if(selected == images.size() - 1)
        {
            forward.setDisabled(true);
        }
        else
        {
            forward.setDisabled(false);
        }
    }
    
    private void setImage(Image newImage)
    {
        imageHolder.setImage(newImage);
        update();
    }

    
    public synchronized void setZoom(double zoom)
    {
        this.zoom = zoom;
        update();
    }
    
    private void update()
    {
        if(area.width == 0 || area.height == 0)
        {
            return;
        }      
     
        imageHolder.setZoom(zoom);
        
        Image image = imageHolder.getImage();
        if(image == null)
        {
            setScrollbarVisibility(false, false);
            return;
        }
        
        if(image.isDisposed())
        {
            System.out.println("This should be impossible");
        }
        
        int width = image.getImageData().width;
        int height = image.getImageData().height;
        
        int scaledWidth = (int)(width * zoom);
        int scaledHeight = (int)(height * zoom);
        
        int imageHolderWidth = 2 + scaledWidth;
        int imageHolderHeight = 2 + scaledHeight;
        
        imageHolder.setSize( new Point(imageHolderWidth, imageHolderHeight));
        
        int showerWidth = viewerRegion.getArea().width;
        int showerHeight = viewerRegion.getArea().height;
        
        needHBar = false;
        needVBar = false;
        
        int barSize = AScrollbar.SCROLLBAR_SIZE;
        
        if(showerWidth - barSize < imageHolderWidth)
        {
            needHBar = true;
        }
        
        if(showerHeight - barSize < imageHolderHeight)
        {
            needVBar = true;
        }

        int barWidth = AScrollbar.SCROLLBAR_SIZE;
        
        imageRegion.setSizeFillBoth( new Point(
                (needVBar ? barWidth : 0), 
                (needHBar ? barWidth : 0)) );

	    int regionWidth = imageRegion.getArea().width;
	    int regionHeight = imageRegion.getArea().height;
        
	    int newx = 0;
	    int newy = 0;
	    
	    if(imageHolderWidth <= regionWidth)
	    {
	        newx = (regionWidth - imageHolderWidth) / 2;
	    }
	    else
	    {
	        newx = regionWidth/2 - (int)(imageHolderWidth * xRatio);
	        
	        if(newx > 0)
	        {
	            newx = 0;
	        }
	        
	        if(-newx + regionWidth > imageHolderWidth)
	        {
	            newx = -(imageHolderWidth - regionWidth);
	        }
	    }
	    
	    if(imageHolderHeight <= regionHeight)
	    {
	        newy = (regionHeight - imageHolderHeight) / 2;
	    }
	    else
	    {
	        newy = regionHeight/2 - (int)(imageHolderHeight * yRatio);
	        
	        if(newy > 0)
	        {
	            newy = 0;
	        }
	        
	        if(-newy + regionHeight > imageHolderHeight)
	        {
	            newy = -(imageHolderHeight - regionHeight);
	        }
	    }
	    
	    imageHolder.setPosition(new Point(newx, newy));
        
	    setScrollbarVisibility(needHBar, needVBar);
	    
	    horizScrollbar.setMaximum(imageHolderWidth);
        horizScrollbar.setIncrement(Math.min(Math.max(1, imageHolderWidth / 20), 50) );
        horizScrollbar.setSize(regionWidth);
        horizScrollbar.updateFromWindow();
        
        vertScrollbar.setMaximum(imageHolderHeight);
        vertScrollbar.setIncrement(Math.min(Math.max(1, imageHolderHeight / 20), 50) );
        vertScrollbar.setSize(regionHeight);
        vertScrollbar.updateFromWindow();
     
        updateButtons();
    }
    
    private void setScrollbarVisibility(boolean showHBar, boolean showVBar)
    {
        if(showHBar)
        {
            horizScrollbar.show();
        }
        else
        {
            horizScrollbar.hide();
        }
        
        if(showVBar)
        {
            horizScrollbar.setGapRight();
        }
        else
        {
            horizScrollbar.unsetGap();
        }
        
        if(showVBar)
        {
            vertScrollbar.show();
        }
        else
        {
            vertScrollbar.hide();
        }
        
        if(showHBar)
        {
            vertScrollbar.setGapBottom();
        }
        else
        {
            vertScrollbar.unsetGap();
        }
    }

    protected synchronized void fromGUIThreadExecutionStarted(String programName,
            Calendar startedTime)
    {
        if(Kevlar.DEBUG)
            System.out.println("called: fromGUIThreadExecutionStarted(...)");

    }

    protected synchronized void addObjectFromGUIThread(IType o)
    {
        IImageType image = (IImageType)o;
        BufferedImage awtImage = image.getImage();
        
        // why is it sometimes null?
        Image newImage = new Image(Display.getDefault(), ConvertSwtAwt.convertToSWT(awtImage));
        
        boolean atEnd = false;
        if(images.size() == 0 || selected == images.size() - 1)
        {
            atEnd = true;
        }
        
        images.add(newImage);

        if(atEnd)
        {
            setSelected(images.size() - 1);
        }
    }
    
    public synchronized void setSelected(int pos)
    {
        if(pos < 0 || pos >= images.size())
        {
            throw new AssertionError();
        }
        
        selected = pos;
        setImage(images.get(selected));
        redraw();
    }

    public synchronized void setVerticalPosition(int position)
    {
        if(!vertScrollbar.isVisible())
        {
            return;
        }
        imageHolder.setPosition(new Point(imageHolder.getPosition().x, -position));
        yRatio = (0.0 + position + imageRegion.getArea().height/2) /
        	imageHolder.getImage().getImageData().height;
        
    }

    public synchronized void setHorizontalPosition(int position)
    {
        if(!horizScrollbar.isVisible())
        {
            return;
        }
        imageHolder.setPosition(new Point(-position, imageHolder.getPosition().y));
        xRatio = (0.0 + position + imageRegion.getArea().width/2) /
    	imageHolder.getImage().getImageData().width;
    }

    public synchronized int getVerticalPosition()
    {
        return -imageHolder.getPosition().y;
    }

    public synchronized int getHorizontalPosition()
    {
        return -imageHolder.getPosition().x;
    }

    public synchronized void updateDimensions() {
        
        super.updateDimensions();
        update();
    }
    
    /*
    public void childDimensionsChanged(ADrawableObject child) {
        
        if (child == imageHolder) {
            
            vertScrollbar.setMaximum(imageHolder.getSize().y);
            vertScrollbar.updateFromWindow();
        }
    }*/
        
    public synchronized void show()
    {
        super.show();
        if(needHBar)
        {
            horizScrollbar.show();
        }
        else
        {
            horizScrollbar.hide();
        }
        
        if(needVBar)
        {
            vertScrollbar.show();
        }
        else
        {
            vertScrollbar.hide();
        }   
    }
    
    public synchronized void drawBefore(GC gc, Rectangle redrawArea)
    {
        Rectangle drawArea = new Rectangle(0 - redrawArea.x, 
                0 - redrawArea.y,
                getSize().x, imageToolbarHeight);

        Utils.drawHorizontalBar(gc, IMAGE_TOOLBAR, drawArea, false, false);
                
        //gc.setForeground(ColorCache.get(0,0,0));
        //gc.drawRectangle(0 - redrawArea.x, 0 - redrawArea.y, area.width, area.height);
        
        drawArea.x = BUFFER_SPACE * 2 + 2 * navButtonWidth + TEXT_OFFSET;
        
        if(images.size() > 0)
        {
	        Utils.drawText(
	                gc, 
	                "Image " + (selected + 1) + " of " + images.size() + ".", 
	                drawArea, 0);
        }
        else
        {
	        Utils.drawText(
	                gc, 
	                "No images to show", 
	                drawArea, 0);
        }
    }
    
    public synchronized void setRemoved()
    {
        super.setRemoved();
        dispose();
    }
    
    private synchronized void dispose()
    {
        synchronized(imageHolder)
        {
            imageHolder.setImage(null);
	        for(Image i : images)
	        {
	            i.dispose();
	        }
	        images.clear();
        }
    }
    
    protected void finalize() throws Throwable
    {
        super.finalize();
        dispose();
    }
}
