package uk.ac.man.cs.img.anttasks; // Generated package name

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;


/**
 * 
 * This task updates a <a href="http://jdee.sunsite.dk">JDEE</project>
 * file so that it is consistent with a ant build. 
 * This was built mostly to support 
 * <a href="http://www.russet.org.uk/antmerge.html">antmerge</a>, so
 * at the moment, it doesn't really make any attempt at being
 * comprehensive, but rather has methods specifically for those
 * variables which would seem to relate to antmerge.
 *
 * It works in a fairly simple minded manner. It adds coded lisp 
 * statements immediately after the "jde-set-variables". It appends to,
 * rather than creates a project file, so you can still set the other
 * values with emacs in the normal way. 
 *
 */
public class JdeeTask extends Task
{
    // set the project files
    private File projectFile;
    private File outFile;
    public void setProjectFile( File projectFile )
    {
        this.projectFile = projectFile;
    }
    
    public void setOutFile( File outFile )
    {
        this.outFile = outFile;
    }

    // minor performance hack which probably provides no advantage.
    private StringBuffer retn;
    private StringBuffer getStringBuffer()
    {
        if( retn == null) retn = new StringBuffer();
        
        retn.setLength( 0 );
        return retn;
    }
    
    // lisp support. Close the parens.
    private void closeParensInLisp( StringBuffer buffer )
    {
        int openParen = 0;
        int closeParen = 0;
        
        // count the number of open parens
        int lastOpenParen = 0;
        while( (lastOpenParen = buffer.indexOf( "(", lastOpenParen )) != -1 ) {            
            lastOpenParen++;
            openParen++;
        }
        
        // count the number of close parens
        int lastCloseParen = 0;
        while( (lastCloseParen = buffer.indexOf( ")", lastCloseParen )) != -1 ){
            lastCloseParen++;
            closeParen++;
        }
        
        // add the difference between the two
        for( int parensToAdd = openParen - closeParen;
             parensToAdd > 0; parensToAdd-- ){
            buffer.append( ")" );
        }
    }
    //add a simple quoted string
    private void appendQuotedString( StringBuffer buffer, String string )
    {
        buffer.append( "\"" );
        buffer.append( string );
        buffer.append( "\" " );
    }
    
    // set the global classpath 
    private String[] jdeGlobalClasspath;
    public void setJdeGlobalClasspathRef( Reference reference )
    {
        Path path = new Path( getProject() );
        path.setRefid( reference );
        jdeGlobalClasspath = path.list();
    }
    
    private String getJdeGlobalClasspathLisp()
    {
        if( jdeGlobalClasspath != null ){
            StringBuffer retn = getStringBuffer();
            retn.append( " '(jde-global-classpath (quote (" );
            for( int i = 0; i < jdeGlobalClasspath.length; i++ ){
                appendQuotedString( retn, jdeGlobalClasspath[ i ] );
            }
            closeParensInLisp( retn );
            return retn.toString();
        }
        return null;
    }
    
    // compile option directory
    private String jdeCompileOptionDirectory;
    public void setJdeCompileOptionDirectory( File file )
    {
        jdeCompileOptionDirectory = file.getAbsolutePath();
    }
    
    private String getJdeCompileOptionDirectoryLisp()
    {
        if( jdeCompileOptionDirectory != null ){
            StringBuffer retn = getStringBuffer();
            retn.append( " '(jde-compile-option-directory " );
            appendQuotedString( retn, jdeCompileOptionDirectory );
            closeParensInLisp( retn );
            return retn.toString();
        }
        return null;
    }
    
    // src directories..this doesn't work properly yet, as it can only
    // cope with a single directory
    private File jdeSourcepath;
    public void setJdeSourcepath( File jdeSourcepath )
    {
        this.jdeSourcepath = jdeSourcepath;
    }
    
    private String getJdeSourcepathLisp()
    {
        if( jdeSourcepath != null ){
            StringBuffer retn = getStringBuffer();
            retn.append( " '(jde-sourcepath (quote ( " );
            appendQuotedString( retn, jdeSourcepath.getAbsolutePath() );
            closeParensInLisp( retn );
            return retn.toString();
        }
        return null;
        //         if( jdeSourcepath != null ){
        //             StringTokenizer tokenizer = new StringTokenizer( jdeSourcepath, ":" );
        //             StringBuffer retn = getStringBuffer();
        //             retn.append( " '(jde-sourcepath (quote (" );
        //             while( tokenizer.hasMoreTokens() ){
        //                 appendQuotedString( retn, tokenizer.nextToken() );
        //             }
        //             closeParensInLisp( retn );
        //         }
        //        return retn.toString();
    }
    

    private int insertionPoint;
    private StringBuffer project;
    
    public void execute()
    {
        if( projectFile == null ){
            throw new BuildException( "projectfile attribute is mandatory" );
        }
        
        project = new StringBuffer();
        
        try{
            BufferedReader reader = new BufferedReader
                ( new InputStreamReader( new FileInputStream( projectFile ) ) );
            
            String line;
            while( (line = reader.readLine() ) != null ){
                project.append( line + "\n" );
            }
            reader.close();
        }
        catch( IOException ioe ){
            throw new BuildException( ioe );
        }
        
        
        // discover previously added settings
        String startOptionsComment = " ;;Options added by JdeeTask";
        String endOptionsComment =   " ;;end options added by JdeeTask";
        
        Pattern commentPattern = Pattern.compile
            ( startOptionsComment + ".*?" + endOptionsComment,
              Pattern.DOTALL );
        Matcher commentMatch = commentPattern.matcher( project.toString() );
        
        if( commentMatch.find() ){
            // delete the old set, following the match (+1 for the new
            // line after the close comment
            project.delete( commentMatch.start(), commentMatch.end() + 1);
        }

        // discover insertion point...
        Pattern pattern = Pattern.compile( "jde-set-variables" );
        Matcher match = pattern.matcher( project.toString() );
        
        
        if( !match.find() ){
            throw new BuildException( "Failed to find \"jde-set-variables\" line" );
        }
        
        insertionPoint = match.end();
        
        // I like doing things backward....
        insert( endOptionsComment );
        insert( getJdeGlobalClasspathLisp() );
        insert( getJdeCompileOptionDirectoryLisp() );
        insert( getJdeSourcepathLisp() );
        insert( startOptionsComment );
        
        try{
            if( outFile == null ){
                outFile = projectFile;
            }
            FileWriter writer = new FileWriter( outFile );
            writer.write( project.toString() );
            writer.close();
        }
        catch( IOException ioe ){
            throw new BuildException( ioe );
        }
    }
    
    private void insert( String lisp )
    {
        if( lisp != null ){
            project.insert( insertionPoint, lisp );
            project.insert( insertionPoint, "\n" );
        }
    }
}


