/*
 * GifDescriptor.java
 *
 * Created on 20 August 2003, 15:25
 */

package org.bodington.servlet.gif;

import java.io.*;
import java.util.*;
import java.util.logging.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
/**
 *
 * @author  jrm
 */
public class GifDescriptor
{
    private static Hashtable cache = new Hashtable();
    
    File source, xml_file;
    long last_modified=0L, xml_last_modified=0L;
    
    private GifColourStatement[] statements;
    private byte[] data;
    private int colours;
    private boolean data_cached;
    
    private static final int MAX_BUFFER = 8*1024;
    private static final int MAX_HEADER = 13+(256*3);
    private static DocumentBuilderFactory docbuilderfactory;
    private static org.xml.sax.EntityResolver entity_resolver;
    static
    {
        docbuilderfactory = DocumentBuilderFactory.newInstance();
        docbuilderfactory.setValidating( true );
        entity_resolver = new org.bodington.xml.EntityResolver( "bodington.home", "dtd" );
    }
    
    
    /** Creates a new instance of GifDescriptor */
    private GifDescriptor( File source )
    {
        this.source = source;
        xml_file = new File( source.getParent(), source.getName() + "x" );
        statements = null;
    }
    
    public static GifDescriptor getGifDescriptor( File file )
    {
        GifDescriptor gd=null;
        String path;
        try
        {
            path = file.getCanonicalPath();
        }
        catch ( IOException ioe )
        {
            return null;
        }
        
        synchronized( cache )
        {
            gd = (GifDescriptor)cache.get( path );
            if ( gd == null && file.exists() )
            {
                gd = new GifDescriptor( file );
                // only cache it if the GIF file exists
                cache.put( path, gd );
            }
        }
        
        gd.load();
        
        return gd;
    }
    
    private synchronized void load()
    {
        long m = source.lastModified();
        
        if ( xml_file.exists() )
        {
            long xm = xml_file.lastModified();
            if ( xm != xml_last_modified )
            {
                statements = loadStatements();
                xml_last_modified = xm;
            }
        }
        else
            statements = null;
        
        if ( data == null || last_modified != m )
        {
            Logger.getLogger( "org.bodington" ).fine( "Loading GIF header. " + source.getAbsolutePath() );
            
            last_modified = m;
            long l = source.length();
            
            data_cached = l <= (long)MAX_BUFFER;
            data = new byte[data_cached?(int)l:MAX_HEADER];
            
            FileInputStream fin=null;
            
            try
            {
                int n;
                
                fin = new FileInputStream( source );
                
                n = fin.read( data );
                if ( n != data.length )
                    data = null;
                
                colours = 2 << (data[10] & 0x7);
                
            }
            catch ( IOException ioex )
            {
                data = null;
                Logger.getLogger( "org.bodington" ).logp(
                Level.SEVERE,
                "GifTemplate",
                "getStatements",
                ioex.getMessage(),
                ioex );
                data=null;
            }
            finally
            {
                if ( fin != null )
                    try
                    {
                        fin.close();
                    }
                    catch ( Exception e )
                    {
                        Logger.getLogger( "org.bodington" ).logp(
                        Level.SEVERE,
                        "GifTemplate",
                        "getStatements",
                        e.getMessage(),
                        e );
                    }
            }
            
        }
    }
    
    
    private GifColourStatement[] loadStatements()
    {
        try
        {
            Logger.getLogger( "org.bodington" ).fine( "Loading dynamic GIF XML file. " + xml_file.getAbsolutePath() );
            
            DocumentBuilder docbuilder = docbuilderfactory.newDocumentBuilder();
            docbuilder.setEntityResolver( entity_resolver );
            Document doc =  docbuilder.parse( xml_file );
            
            Element template_element = doc.getDocumentElement();
            if ( !"dynamicgif".equals( template_element.getTagName() ) )
                throw new IOException( "Invalid template file - no dynamicgif element at root." );
            
            NodeList nlist = template_element.getElementsByTagName( "colourtable" );
            if ( nlist.getLength()!=1 )
                throw new IOException( "Invalid template file - must be one colourtable element." );
            
            template_element = (Element)nlist.item( 0 );
            nlist = template_element.getChildNodes();
            Node node;
            int i, n=0;
            for ( i=0; i<nlist.getLength(); i++ )
            {
                node = nlist.item( i );
                if ( node.getNodeType() == Node.ELEMENT_NODE )
                    n++;
            }
            
            if ( n==0 )
                return null;
            
            GifColourStatement[] statements = new GifColourStatement[n];
            n=0;
            String strindex, strcolour, strbool;
            for ( i=0; i<nlist.getLength(); i++ )
            {
                node = nlist.item( i );
                if ( node.getNodeType() == Node.ELEMENT_NODE )
                {
                    template_element = (Element)node;
                    statements[n] = new GifColourStatement();
                    strindex = template_element.getAttribute( "index" );
                    if ( "all".equals( strindex ) )
                        statements[n].index = GifColourStatement.ALL;
                    else
                    {
                        try
                        {
                            statements[n].index = Integer.parseInt( strindex );
                        }
                        catch ( NumberFormatException nfex )
                        {
                        }
                    }
                    strcolour = template_element.getAttribute( "plane" );
                    if ( "background".equals( strcolour ) )
                        statements[n].plane = GifColourStatement.BACKGROUND;
                    else
                        statements[n].plane = GifColourStatement.FOREGROUND;
                    
                    strbool = template_element.getAttribute( "authorcanmodify" );
                    statements[n].author_can_modify = "true".equals( strbool );
                    strbool = template_element.getAttribute( "usercanmodify" );
                    statements[n].user_can_modify = "true".equals( strbool );
                    n++;
                }
            }
            return statements;
            
        }
        catch ( Throwable th )
        {
            Logger.getLogger( "org.bodington" ).logp(
            Level.SEVERE,
            "GifTemplate",
            "getStatements",
            th.getMessage(),
            th );
            return null;
        }
    }

    public GifColourStatement[] getGifColourStatements()
    {
        return statements;
    }
    
    public int getColourCount()
    {
        return colours;
    }
    
    public byte[] getData()
    {
        return data;
    }
    
    public File getFile()
    {
        return source;
    }
    
    public boolean isDataCached()
    {
        return data_cached;
    }
    
    public long getLastModified()
    {
        if ( last_modified > xml_last_modified )
            return last_modified;
        return xml_last_modified;
    }
}
