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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;

import pl.zalewski.mikolaj.symbdev.SymbdevPlugin;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.BldInfModel;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.IMMPModel;
import pl.zalewski.mikolaj.symbdev.properties.ProjectProperties;
import pl.zalewski.mikolaj.symbdev.wizards.IWizardContentProvider;

/**
 * A template that copies files from a specified directory and translates
 * "${var}" strings.
 * 
 * @author mikolaj
 */
public class SubstitutionsProjectTemplate implements IProjectTemplate
{
    URL fTemplateDir;
    String fName;
    String fDesc;
    ISubstitutionOptionsWizardPage fWizPage;

    /**
     * Constructor
     * @param templateDir The directory with the template data
     * @param name The name
     * @param desc The description
     * @param wizPage An additional wizard page or <code>null</code> if none
     */
    public SubstitutionsProjectTemplate(URL templateDir, String name, String desc,
            ISubstitutionOptionsWizardPage wizPage)
    {
        super();
        fTemplateDir=templateDir;
        fName=name;
        fDesc=desc;
        fWizPage=wizPage;
    }

    /* (non-Javadoc)
     * @see pl.zalewski.mikolaj.symbdev.templates.IProjectTemplate#getName()
     */
    public String getName() {
        return fName;
    }

    /* (non-Javadoc)
     * @see pl.zalewski.mikolaj.symbdev.templates.IProjectTemplate#getDescription()
     */
    public String getDescription() {
        return fDesc;
    }
    
    /* (non-Javadoc)
     * @see pl.zalewski.mikolaj.symbdev.templates.IProjectTemplate#getWizardContents(org.eclipse.jface.wizard.WizardPage, org.eclipse.swt.widgets.Composite)
     */
    public IWizardContentProvider getWizardContentProvider() {
        return fWizPage;
    }
    
    /* (non-Javadoc)
     * @see pl.zalewski.mikolaj.symbdev.templates.IProjectTemplate#hasWizardPages()
     */
    public boolean hasWizardPages() {
        return fWizPage!=null;
    }
    
    protected void checkArgumentsCount(int argCount, String[] tokens) throws CoreException
    {
        if (argCount+1!=tokens.length)
            throw new CoreException(new Status(IStatus.WARNING,
                    SymbdevPlugin.getUniqueIdentifier(), SymbdevPlugin.ERROR_TEMPLATE_PARSE,
                    "Bad argument count in template file. "+(tokens.length-1)+" insteand of "+argCount,
                    null));
    }
    
    protected String translateString(String arg)
    	throws IOException
    {
        SubstitutionStreamReader nameReader=new SubstitutionStreamReader(fSubstMap, new StringReader(arg));
        return nameReader.readString();
    }
    
    protected IMMPModel getModelForArg(IProject project, String arg)
    	throws CoreException, IOException
    {
        String path=translateString(arg);
        IResource file=project.findMember(new Path(path));
        if (file==null)
            throw new CoreException(new Status(IStatus.ERROR, SymbdevPlugin.getUniqueIdentifier(),
                    SymbdevPlugin.ERROR_TEMPLATE_PARSE, "MMP file to be changed not found", null));
        if (!(file instanceof IFile))
            throw new CoreException(new Status(IStatus.ERROR, SymbdevPlugin.getUniqueIdentifier(),
                    SymbdevPlugin.ERROR_TEMPLATE_PARSE, "MMP changing target is not a file", null));
        return BldInfModel.getMMPModelForFile((IFile)file);
    }
    
    protected void commandMkdir(IProject project, String[] tokens) throws CoreException
    {
        checkArgumentsCount(1, tokens);
        Path dir=new Path(tokens[1]);
        project.getFolder(dir).create(false, true, null);
    }

    protected void commandTranslate(IProject project, String[] tokens)
    	throws CoreException, IOException
    {
        checkArgumentsCount(2, tokens);
        InputStream fileRead=appendURL(fTemplateDir, "data/"+tokens[1]).openStream();
        InputStreamReader rint=new InputStreamReader(fileRead, Charset.forName("UTF-8"));
        BufferedReader reader=new BufferedReader(rint);
        
        String targetName=tokens[2];
        SubstitutionStreamReader nameReader=new SubstitutionStreamReader(fSubstMap, new StringReader(targetName));
        targetName=nameReader.readString();
        
        SubstitutionStreamReader subst=new SubstitutionStreamReader(fSubstMap, reader);
        InputStreamFromReader input=new InputStreamFromReader(subst, Charset.forName(project.getDefaultCharset()));
        project.getFile(targetName).create(input, false, null);
    }
    
    protected void commandAddSourceDir(IProject project, String[] tokens)
    	throws CoreException, IOException
    {
        checkArgumentsCount(2, tokens);
        String dir=translateString(tokens[2]);
        IMMPModel m=getModelForArg(project, tokens[1]);
        IPath dirLocation=project.findMember(new Path(dir)).getLocation();
        m.addSourceDir(dirLocation);
    }
    
    /* (non-Javadoc)
     * @see pl.zalewski.mikolaj.symbdev.templates.IProjectTemplate#applyToProject(org.eclipse.core.resources.IProject, org.eclipse.core.runtime.IProgressMonitor)
     */
    public void applyToProject(IProject prj, IProgressMonitor monitor)
            throws CoreException {
        fSubstMap=buildTranslationTable();
        // add other symbols
        fSubstMap.put("epocrootdir", ProjectProperties.getSymbianSDK(prj).epocRoot);
        try {
            InputStream mainFile=appendURL(fTemplateDir, "template").openStream();
            InputStreamReader rint=new InputStreamReader(mainFile, Charset.forName("UTF-8"));
            BufferedReader reader=new BufferedReader(rint);
            String line;
            while ((line=reader.readLine())!=null)
            {
                line=line.trim();
                if (line.equals(""))
                    continue;
                line=line.replaceAll("\\s+", " ");
                String[] tokens=line.split(" ");
                if (tokens.length==0) continue;
                
                if (tokens[0].equals("MKDIR"))
                    commandMkdir(prj, tokens);
                else if (tokens[0].equals("TRANSLATE"))
                    commandTranslate(prj, tokens);
                else if (tokens[0].equals("ADDSOURCEDIR"))
                    commandAddSourceDir(prj, tokens);
                else throw new CoreException(new Status(IStatus.WARNING,
                        SymbdevPlugin.getUniqueIdentifier(), SymbdevPlugin.ERROR_TEMPLATE_PARSE,
                        "Unknown token '"+tokens[0]+"' in template file.", null));
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
            throw new CoreException(new Status(IStatus.ERROR, SymbdevPlugin.getUniqueIdentifier(),
                    SymbdevPlugin.ERROR_TEMPLATE_PARSE, "Error while reading template data", null));
        }
    }
    
    URL appendURL(URL c, String path)
    {
        try {
            return new URL(c, path);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * Builds the map that maps variables into values. E.g. the string "${var}"
     * will be translated to <code>(String)map.get("var")</code>
     * @return The variables -&gt; values map.
     */
    protected Map buildTranslationTable()
    {
        Map map=new HashMap();
        if (fWizPage!=null)
            fWizPage.contributeToTranslationMap(map);
        return map;
    }
    
    Map fSubstMap;
}
