/*
 * Created on 2004-08-15
 *
 * Part of the Symbian development plug-in for Eclipse
 * Copyright (C) Mikołaj Zalewski, 2004
 */
package pl.zalewski.mikolaj.symbdev;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IIncludeEntry;
import org.eclipse.cdt.core.model.IMacroEntry;
import org.eclipse.cdt.core.model.IPathEntry;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;

import pl.zalewski.mikolaj.symbdev.properties.ProjectProperties;

/**
 * @author mikolaj
 *
 * A project nature for Symbian OS projects 
 */
public class SymbianPrjNature implements IProjectNature {

    public final static String NATURE_ID=SymbdevPlugin.getUniqueIdentifier()+".symbianPrjNature";
    /**
     * Default constructor
     */
    public SymbianPrjNature() {
        super();
    }

    public void configure() throws CoreException {
        addBuilder(getProject(), SymbdevPlugin.BUILDER_NAME);
        System.out.println("configure");
    }

    /* (non-Javadoc)
     * @see org.eclipse.core.resources.IProjectNature#deconfigure()
     */
    public void deconfigure() throws CoreException {
        System.out.println("deconfigure");
    }

    /* (non-Javadoc)
     * @see org.eclipse.core.resources.IProjectNature#getProject()
     */
    public IProject getProject() {
        return myProject;
    }

    /* (non-Javadoc)
     * @see org.eclipse.core.resources.IProjectNature#setProject(org.eclipse.core.resources.IProject)
     */
    public void setProject(IProject project) {
        myProject=project;
    }
    
    public static void addBuilder(IProject project, String builder)
    	throws CoreException
    {
        IProjectDescription desc = project.getDescription();
        ICommand[] commands = desc.getBuildSpec();
        boolean found = false;

        for (int i = 0; i < commands.length; ++i) {
           if (commands[i].getBuilderName().equals(builder)) {
              found = true;
              break;
           }
        }
        if (!found) { 
           //add builder to project
           ICommand command = desc.newCommand();
           command.setBuilderName(builder);
           ICommand[] newCommands = new ICommand[commands.length + 1];

           // Add it before other builders.
           System.arraycopy(commands, 0, newCommands, 1, commands.length);
           newCommands[0] = command;
           desc.setBuildSpec(newCommands);
           project.setDescription(desc, null);
        }        
    }
    
    static public void setupBuilder(IProject project)
    	throws CoreException
    {
        addBuilder(project, SymbdevPlugin.BUILDER_NAME);
    }
    
    static public void applyPaths(IProject project)
    {
		try
		{
	        IPath[] includePaths=getIncludePaths(project);
			ICProject cProject=CoreModel.getDefault().create(project);
			IPathEntry[] entries=cProject.getRawPathEntries();
			List cPaths=new ArrayList(Arrays.asList(entries));
	
			Iterator cpIter=cPaths.iterator();
			while(cpIter.hasNext()) {
				int kind=((IPathEntry)cpIter.next()).getEntryKind();
				if(kind==IPathEntry.CDT_INCLUDE || kind==IPathEntry.CDT_MACRO) {
					cpIter.remove();
				}
			}
			for (int i=0; i<includePaths.length; i++) {
				IIncludeEntry include=CoreModel.newIncludeEntry(project.getFullPath()/*new Path(".")*/, null, includePaths[i]);
				if (!cPaths.contains(include)) {
					cPaths.add(include);
				}
			}
			
			String[] platformDefines=getPlatformDefines(project);
			String[] defines=new String[platformDefines.length+4];
			System.arraycopy(platformDefines, 0, defines, 4, platformDefines.length);
			defines[0]="_UNICODE";
			if (ProjectProperties.getABLDBuild(project).equals(ProjectProperties.BUILD_UDEB))
			    defines[1]="_DEBUG";
				else defines[1]="_NDEBUG";
			defines[2]="__EPOC32__";
			defines[3]="__SYMBIAN32__";
			for (int i=0; i<defines.length; i++) {
				IMacroEntry sym = CoreModel.newMacroEntry(project.getFullPath(), defines[i], "");
				if (!cPaths.contains(sym)) {
					cPaths.add(sym);
				}
			}
		    cProject.setRawPathEntries((IPathEntry[])cPaths.toArray(new IPathEntry[cPaths.size()]), null);
		}
		catch (CoreException e)
		{
		    SymbdevPlugin.getDefault().getLog().log(e.getStatus());
		}
    }
    
    static public IPath[] getIncludePaths(IProject project)
    {
        IPath[] includePaths=new Path[2];
        try {
            if (!project.hasNature(NATURE_ID))
                return null;
        
            IPath prjPath=project.getLocation();
            Path p=new Path(ProjectProperties.getIncPath(project));
            includePaths[0]=prjPath.append(p);
        }
        catch (CoreException e)
        {
            e.printStackTrace();
            return new Path[0];
        }

        try {
        	includePaths[1]=new Path(ProjectProperties.getSymbianSDK(project).epocRoot).append("epoc32/include");
        }
        catch (CoreException e)
        {
            IPath path=includePaths[0];
            includePaths=new IPath[1];
            includePaths[0]=path;
        }
        return includePaths;
    }
    
	/**
	 * Returns macros definied for the current target. This command may run
	 * "bldmake" if they are not cached
	 * @param project
	 * @return Table of definied macros (e.g. WINS, VC7)
	 * @throws CoreException
	 */
    public static String[] getPlatformDefines(IProject project)
		throws CoreException
	{
        SymbianSDKEntry sdk=ProjectProperties.getSymbianSDK(project);
    	Win32ToolEntry tool=ProjectProperties.getWin32Tool(project);
    	if (cachedMacros!=null)
    	{
        	if (cachedForSDK==sdk && cachedForEntry==tool)
            	return cachedMacros;
    	}
    
    	cachedMacros=getPlatformDefinesFromBldmake(project);
    	cachedForEntry=tool;
    	cachedForSDK=sdk;
    	return cachedMacros;
	}

    static String[] cachedMacros;
	static Win32ToolEntry cachedForEntry;
	static SymbianSDKEntry cachedForSDK;


	/**
	 * Starts the "bldmake" command to obtain macros definied for the current
	 * target
	 * @param project
	 * @return Table of definied macros (e.g. WINS, VC7)
	 * @throws CoreException
	 */
	protected static String[] getPlatformDefinesFromBldmake(IProject project)
		throws CoreException
	{
	    System.out.println("Querying bldmake for platform macros.");
    
	    SymbianCommandLauncher launcher=new SymbianCommandLauncher();
	    launcher.setUseGroupPath(false);
	    String[] args=new String[2];
	    args[0]="plat";
	    args[1]=ProjectProperties.getWin32Tool(project).target;
	    Process p=launcher.execute(project, "bldmake", args);
	    try {
	        // Close the input of the Process explicitely.
	        // We will never write to it.
	        p.getOutputStream().close();
	    } catch (IOException e) {
	    }
	
	    InputStream output=p.getInputStream();
	    InputStreamReader reader=new InputStreamReader(output);
	    BufferedReader buf=new BufferedReader(reader);
	
	    // the last line contains the macros
	    String line=null;
	    String prevLine;
	    do {
	        prevLine=line;
	        try {
	            line=buf.readLine();
	        }
	        catch (IOException e)
	        {
	            e.printStackTrace();
	            return null;
	        }
	    } while (line!=null);
	
	    if (prevLine==null)
	        return new String[0];
	    line=prevLine;
	    line=line.replaceAll("\\s+", " ");
	    line=line.trim();
	    String[] macros=line.split(" ");
	    
	    for (int i=0; i<macros.length; i++)
	        macros[i]="__"+macros[i]+"__";
    
	    return macros;
	}
	
	/**
	 * Returns the directory with the *.lib file for the emulator
	 * target for this project. Usually this is
	 * %EPOCROOT%\epoc32\release\(target name)\\udeb\ 
	 * @param project The project to query the path 
	 * @return The path to the directory
	 * @throws CoreException if something goes wrong (e.g.
	 * ERROR_NO_TOOL_CONFIG if no SDK or toolchain configured,
	 * ERROR_NO_SYMBIAN_NATURE if not a Symbian project)
	 */
	static public IPath getLibsDirForProject(IProject project)
		throws CoreException
	{
	    if (!project.hasNature(NATURE_ID))
	        throw new CoreException(SymbdevPlugin.createErrorStatus(
	                SymbdevPlugin.ERROR_NO_SYMBIAN_NATURE,
	                "Project has not a Symbian nature"));
	    Win32ToolEntry tool=ProjectProperties.getWin32Tool(project);
	    SymbianSDKEntry sdk=ProjectProperties.getSymbianSDK(project);
	    return new Path(sdk.epocRoot).append("epoc32/release").
	    	append(tool.target).append("udeb");
	}
    
    
	/**
	 * Utility method for adding a nature to a project.
	 * 
	 * @param proj the project to add the nature to.
	 * @param natureId the id of the nature to assign to the project
	 * @param monitor a progress monitor to indicate the duration of the operation, or
	 * <code>null</code> if progress reporting is not required.
	 */
	public static void addNature(IProject project, IProgressMonitor monitor) throws CoreException {
	    String natureId=NATURE_ID;
		IProjectDescription description = project.getDescription();
		String[] prevNatures = description.getNatureIds();
		for (int i = 0; i < prevNatures.length; i++) {
			if (natureId.equals(prevNatures[i]))
				return;
		}
		String[] newNatures = new String[prevNatures.length + 1];
		System.arraycopy(prevNatures, 0, newNatures, 0, prevNatures.length);
		newNatures[prevNatures.length] = natureId;
		description.setNatureIds(newNatures);
		project.setDescription(description, monitor);
	}    
    
    IProject myProject;

}
