package com.uvigo.mhp.activeImage;

import com.uvigo.mhp.*;
import com.uvigo.mhp.xml.activeImage.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.net.*;
import javax.xml.bind.*;
import org.havi.ui.*;
import com.uvigo.tMaestro.SCOCreator.tool.MainFrame;

// Permitir selecciones multiples
// Permitir definir la granularidad del desplazamiento, para evitar parpadeos con im�genes irregulares

// (Hereda de HContainer porque otros objetos no funcionan bien al hacer scroll dentro
// de un HScrollPane)
public class ActiveImage extends HContainer implements java.io.Serializable, ContextHandler {
     public static final int TIME_ALWAYS = 0;
    public static final int TIME_OK = 1;
    public static final int TIME_WRONG = 2;
    public static final int TIME_DELAYED = 4;
     public static final int TIME_LIMITED = 6;
     
    public static final int H_LEFT=0;
    public static final int H_RIGHT=1;
    public static final int H_CENTER=2;
    public static final int V_TOP=0;
    public static final int V_BOTTOM=1;
    public static final int V_CENTER=2;
    public static final String LEFT_STR="LEFT";
    public static final String RIGHT_STR="RIGHT";
    public static final String CENTER_STR="CENTER";
    public static final String TOP_STR="TOP";
    public static final String BOTTOM_STR="BOTTOM";
    
    public static final int CLICK_TO_SELECT=0;
    public static final int SELECTION_FOLLOWS_MOUSE=1;
    public static final String CLICK_TO_SELECT_STR="Click-to-select";
    public static final String SELECTION_FOLLOWS_MOUSE_STR="Selection-follows-mouse";
    
    private boolean mouseEnabled;
    private int mouseBehaviour;
    private int hAlignment, vAlignment;
    
    private transient Image backgroundImage;
    private transient Vector images;
    private transient int width, height;
    private transient int x, y, hpos, vpos, alpha, temp[];
    
    private JAXBContext jc;
    private String configurationFile;
    private ActiveImageConfiguration conf;
    private java.util.List zones;
    private MediaTracker trk;
    private int delayedTimeMode;
    private int delayedTime;
    
    private int current;
    private ContextManager contextManager;
    private Vector listeners;
    private String context;
    private String subcontext;
    private boolean cyclic;
    
    private BufferedImage offscreen;
    private Graphics off;
    
    public ActiveImage() {
        try {
            jc=JAXBContext.newInstance("com.uvigo.mhp.xml.activeImage");
            configurationFile="";
            delayedTimeMode=TIME_ALWAYS;
            delayedTime=0;
            conf=null;
            trk=new MediaTracker(this);
            
            current=-1;
            contextManager=null;
            listeners=new Vector();
            context="";
            subcontext="";
            cyclic=false;
            
            temp=new int[1];
            images=new Vector();
            mouseBehaviour=SELECTION_FOLLOWS_MOUSE;
            setMouseEnabled(true);
            hAlignment=vAlignment=2;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    public int getDelayedTime() {
        return delayedTime;
    }

    public void setDelayedTime(int m) {
        int old = delayedTime;
        if (m != old) {
            delayedTime = m;
            firePropertyChange("delayedTime", new Integer(old), new Integer(m));
        }
    }
     public int getDelayedTimeMode() {
        return delayedTimeMode;
    }

    public void setDelayedTimeMode(int i) {

        int old = delayedTimeMode;
        if (i != old) {
            delayedTimeMode = i;
            repaint();
            firePropertyChange("delayedTimeMode", new Integer(old), new Integer(i));

        }
    }
    public String getConfigurationFile() {
        return configurationFile;
    }
    
    public void setConfigurationFile(String s) {
        String old=configurationFile;
        //if (!(s.equals(old))) {//Necesario eliminarlo, ya que puede cambiar el contenido del fichero y no su ruta
            try {
                if ((s==null) || (s=="")) return;
                
               //M: Direccionamiento relativo a "user.dir"
               String sBase = new String(MainFrame.oMainFrame.sDir+ "/");
                URL url=new URL("file:"+sBase+s);
                
                //Parte de s que corresponde a ruta (que no es fichero)
                String[] split = s.split("/");
                String sRuta=new String();
                int z=0;
                for (z=0;z<split.length-1;z++) {
                    sRuta = sRuta.concat(split[z]+"/");
                }
                
                URLConnection con=url.openConnection();
                
                Unmarshaller u=jc.createUnmarshaller();
                u.setValidating(true);
                conf=(ActiveImageConfiguration) u.unmarshal(con.getInputStream());
                zones=conf.getActiveZone();
                
                backgroundImage=Toolkit.getDefaultToolkit().getImage(new URL("file:"+sBase+sRuta+conf.getBackgroundImage()));
                trk.addImage(backgroundImage,0);
                
                for (int i=0;i<zones.size();i++) {
                    ActiveZoneType azt=(ActiveZoneType) zones.get(i);
                    Image temp=Toolkit.getDefaultToolkit().getImage(new URL("file:"+sBase+sRuta+azt.getLocator()));
                    images.add(temp);
                    trk.addImage(temp,i+1);
                }
                
                trk.waitForAll();
                
                width=backgroundImage.getWidth(null);
                height=backgroundImage.getHeight(null);
                forceResize();
                repaint();
                
                configurationFile=s;
                firePropertyChange("configurationFile",old,s);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        //}
    }
    
    public boolean getMouseEnabled() {
        return mouseEnabled;
    }
    
    public void setMouseEnabled(boolean b) {
        mouseEnabled=b;
        setMouseBehaviour(mouseBehaviour);
    }
    
    public int getMouseBehaviour() {
        return mouseBehaviour;
    }
    
    public void setMouseBehaviour(int i) {
        mouseBehaviour=i;
        if (mouseEnabled) {
            if (i==CLICK_TO_SELECT) {
                enableEvents(AWTEvent.MOUSE_EVENT_MASK);
                disableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
            } else {
                enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
                disableEvents(AWTEvent.MOUSE_EVENT_MASK);
            }
        }
    }
    
    public int getHorizontalAlignment() {
        return hAlignment;
    }
    
    public void setHorizontalAlignment(int alignment) {
        int old=hAlignment;
        if (old!=alignment) {
            hAlignment=alignment;
            repaint();
            firePropertyChange("horizontalAlignment",old,alignment);
        }
    }
    
    public int getVerticalAlignment() {
        return vAlignment;
    }
    
    public void setVerticalAlignment(int alignment) {
        int old=vAlignment;
        if (old!=alignment) {
            vAlignment=alignment;
            repaint();
            firePropertyChange("verticalAlignment",old,alignment);
        }
    }
    
    public void paint(Graphics g) {
        if (backgroundImage==null) return;
        
        hpos=0;
        vpos=0;
        
        if (getSize().width>width) {
            switch (hAlignment) {
                case H_RIGHT:
                    hpos=getSize().width-width;
                    break;
                case H_CENTER:
                    hpos=(getSize().width-width)/2;
                    break;
            }
        }
        
        if (getSize().height>height) {
            switch (vAlignment) {
                case V_BOTTOM:
                    vpos=getSize().height-height;
                    break;
                case V_CENTER:
                    vpos=(getSize().height-height)/2;
                    break;
            }
        }
        
        offscreen=new BufferedImage(getSize().width,getSize().height, BufferedImage.TYPE_INT_ARGB);
        off=offscreen.getGraphics();
        
        off.drawImage(backgroundImage,hpos,vpos,this);
        if (current>=0)
            off.drawImage((Image) images.elementAt(current),hpos,vpos,this);
        
        g.drawImage(offscreen,0,0,this);
    }
    
    public void update(Graphics g) {
        paint(g);
    }
    
    public Dimension getPreferredSize() {
        return new Dimension(width+2,height+2);
    }
    
    public Dimension getMinimumSize() {
        return getPreferredSize();
    }
    
    private void forceResize() {
        Component parent = getParent();
        if (parent != null) {
            parent.invalidate();
            parent.doLayout();
        }
    }
    
    protected void processMouseEvent(MouseEvent e) {
        if (!mouseEnabled) return;
        if (mouseBehaviour!=CLICK_TO_SELECT)
            return;
        
        if ((e.getID()==MouseEvent.MOUSE_ENTERED) || (e.getID()==MouseEvent.MOUSE_EXITED))
            return;
        
        int i=pointed(e);
        if (i!=current)
            switchTo(i);
    }
    
    protected void processMouseMotionEvent(MouseEvent e) {
        if (!mouseEnabled) return;
        if (mouseBehaviour!=SELECTION_FOLLOWS_MOUSE)
            return;
        
        int i=pointed(e);
        if (i!=current)
            switchTo(i);
    }
    
    private int pointed(MouseEvent e) {
        x=e.getX();
        y=e.getY();
        
        // se trasladan las coordenadas de acuerdo con el alineamiento
        if (getSize().width>width) {
            if (hAlignment==H_RIGHT)
                x-=getSize().width-width;
            else if (hAlignment==H_CENTER)
                x-=(getSize().width-width)/2;
        }
        
        if (getSize().height>height) {
            if (vAlignment==V_BOTTOM)
                y-=getSize().height-height;
            else if (vAlignment==V_CENTER)
                y-=(getSize().height-height)/2;
        }
        
        if ((x<0) || (x>width))
            return -1;
        if ((y<0) || (y>height))
            return -1;
        
        // Se comprueban sucesivamente todas las im�genes para comprobar qu� zona activa est� siendo apuntada
        for (byte i=0;i<images.size();i++) {
            PixelGrabber pg=new PixelGrabber((Image) images.elementAt(i), x, y, 1, 1, temp, 0, width);
            try {
                pg.grabPixels();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            
            if ((pg.getStatus() & ImageObserver.ABORT) != 0)
                new Exception().printStackTrace();
            
            alpha=temp[0] & 0xff000000;
            if (alpha==0) continue;
            return i;
        }
        return -1;
    }
    
    ///////////////////////////////////////////////////////////////////////////////7
    // GESTION DE CONTEXTOS
    ///////////////////////////////////////////////////////////////////////////////7
    
    public String getContext() {
        return context;
    }
    
    public void setContext(String s) {
        if ((s==null) || (s.equals("")))
            return;
        
        for (int i=0;i<zones.size();i++) {
            ActiveZoneType azt=(ActiveZoneType) zones.get(i);
            if (azt.getContextName().equals(s)) {
                switchTo(i);
                return;
            }
        }
    }
    
    public void gotoFirstContext() {
        if (zones.size()>0)
            switchTo(0);
    }
    
    public void gotoLastContext() {
        if (zones.size()>0)
            switchTo(zones.size()-1);
    }
    
    public void gotoNextContext() {
        if (zones.size()==0) return;
        
        if ((!cyclic) && (current==(zones.size()-1)))
            return;
        
        switchTo((current+1)%zones.size());
    }
    
    public void gotoPreviousContext() {
        if (zones.size()==0) return;
        
        if ((!cyclic) && (current==0))
            return;
        
        switchTo((current-1+zones.size())%zones.size());
    }
    
    public boolean isCyclic() {
        return cyclic;
    }
    
    public void setCyclic(boolean b) {
        boolean old=cyclic;
        if (old!=b) {
            cyclic=b;
            firePropertyChange("cyclic",new Boolean(old), new Boolean(b));
        }
    }
    
    public ContextManager getContextManager() {
        return contextManager;
    }
    
    public void setContextManager(ContextManager c) {
        ContextManager old=contextManager;
        
        // si se indica el mismo ContextManager que estaba configurado, no se hace nada
        if (c==old)
            return;
        
        // si hab�a alguno configurado, nos damos de baja
        if (old!=null) {
            old.remove(this);
            removeContextListener(old);
        }
        
        // nos registramos en el nuevo ContextManager, pero s�lo si no es null
        if (c!=null) {
            addContextListener(c);
            c.add(this);
        }
        
        contextManager=c;
        firePropertyChange("contextManager",old,c);
    }
    
    public void addContextListener(ContextListener c) {
        if (!(listeners.contains(c)))
            listeners.add(c);
    }
    
    public void removeContextListener(ContextListener c) {
        if (listeners.contains(c))
            listeners.remove(c);
    }
    
    private void switchTo(int i) {
        if (zones==null)
            return;
        
        current=i;
        if (i>=0) {
            ActiveZoneType azt=(ActiveZoneType) zones.get(i);
            context=azt.getContextName();
            subcontext=azt.getComment();
        } else {
            context="";
            subcontext="";
        }
        
        repaint();
        fireEvent();
    }
    
    private void fireEvent() {
        for (int i=0;i<listeners.size();i++) {
            ContextListener c=(ContextListener) listeners.get(i);
            c.contextChanged(new ContextEvent(this, context));
            c.subcontextEntered(new ContextEvent(this, subcontext));
        }
    }
    
    public String getSubcontext() {
        return subcontext;
    }
    
}