/*
 * $Revision: 1.6 $ $Date: 2005/05/04 15:06:46 $
 * (C) 2004 SUGIMOTO Ken-ichi
 * 作成日： 2004/05/17
 */

package feat.v1;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Set;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.MatchingTask;

import feat.v1.config.BasicFileLocator;
import feat.v1.config.CommandDeclaration;
import feat.v1.config.FeatConfig;
import feat.v1.config.FeatureConfig;
import feat.v1.config.FeatureDeclaration;
import feat.v1.config.FileLocator;
import feat.v1.config.ResponseDeclaration;

public class FeatTask extends MatchingTask {

    private File appdir;
    private File destdir;
    private String mode;

    public FeatTask() {
        appdir = null;
        destdir = null;
    }

    public void setAppdir(File appdir) {
        log("appdir:"+appdir);
        this.appdir = appdir;
    }

    public void setDestdir(File destdir) {
        log("destdir:"+destdir);
        this.destdir = destdir;
    }

    public void setMode(String mode) {
        this.mode = mode;
    }

    public void execute() throws BuildException {
        if ( appdir == null )
            throw new BuildException("appdirが指定されていません");
        if ( destdir == null )
            throw new BuildException("destdirが指定されていません");
        if ( mode == null )
            throw new BuildException("modeが指定されていません");
        if ( !mode.equals("generate") && !mode.equals("check") )
            throw new BuildException("modeの設定値がgenerateまたはcheckではありません");

        try {
            // 設定ファイルの読み込み
            FileLocator locator = new BasicFileLocator(new File(appdir, "WEB-INF/feat-config.xml"));
            FileLocator appRoot = new BasicFileLocator(appdir);
            FeatConfig config = ConfigReader.parseFeatConfig(appRoot, locator);

            if ( mode.equals("generate") ) {
                // コードの生成
                generate(config);
            } else if ( mode.equals("check") ) {
                config.validate();
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
            throw new BuildException(ex);
        }
    }

    private void generate(FeatConfig config) throws IOException {
        for(Iterator i=config.getFeatureConfigs(); i.hasNext(); ) {
            FeatureDeclaration featureDec = ((FeatureConfig)i.next()).getFeatureDeclaration();

            // リソースをロード
            String code = loadCodeTemplate("feature-codeTemplate.txt");

            // ポインタのリスト
            String pointerList = generatePointerList(featureDec.getResponses());
            String pointerMap = generatePointerMap(featureDec.getResponses());

            // リソースのリスト
            String resourceList = generateResourceList(featureDec.getResourceNames());

            // 定数のリストを作る
            log("generate: "+featureDec.getName());

            // テンプレートに値を埋め込む
            String b_featureName = StringUtil.capitalize(featureDec.getName());
            code = StringUtil.replace(code, "${pointer-list}", pointerList);
            code = StringUtil.replace(code, "${pointer-map}", pointerMap);
            code = StringUtil.replace(code, "${resource-list}", resourceList);
            code = StringUtil.replace(code, "${package}", featureDec.getPackage());
            code = StringUtil.replace(code, "${feature-name}", featureDec.getName());
            code = StringUtil.replace(code, "${Bfeature-name}", b_featureName);

            // コードの出力
            writeSourceFile(destdir, featureDec.getPackage(), b_featureName+"FeatureConstants.java", code);

            // コマンドの定数
            String[] commandNames = featureDec.getCommandNames();
            for(int j=0; j<commandNames.length; j++) {
                CommandDeclaration commandDecl = featureDec.getCommand(commandNames[j]);
                generateCommandSourceFile(destdir, featureDec.getPackage(), commandDecl);
            }
        }
    }

    private void generateCommandSourceFile(File destdir, String pkg, CommandDeclaration commandDecl) throws IOException {
        String code = loadCodeTemplate("command-codeTemplate.txt");
        String[] names = commandDecl.getResponseNames();
        StringBuffer buf = new StringBuffer();
		for(int i=0; i<names.length; i++) {
		    buf.append("    public static final String RESPONSE_").append(names[i]);
		    buf.append(" = \"").append(names[i]).append("\";\n");
		}
		String commandName = commandDecl.getName();
		String b_commandName = StringUtil.capitalize(commandName);
		String ret = StringUtil.replaceParameters(
		        code,
		        new String[] {"package", "command-name", "Bcommand-name", "response-name-list"},
		        new String[] {pkg, commandName, b_commandName, buf.toString()});
		writeSourceFile(destdir, pkg, b_commandName+"CommandConstants.java", ret);
    }

    private String generatePointerList(Iterator it) {
        StringBuffer buf = new StringBuffer();
        while(it.hasNext()) {
            ResponseDeclaration responseDec = (ResponseDeclaration)it.next();
            String name = responseDec.getName();
            buf.append("    public static final ResponsePointer POINTER_");
            buf.append(name);
            buf.append(" = new ResponsePointer(\"${feature-name}\", \"");
            buf.append(name);
            buf.append("\");\n");
        }
        return buf.toString();
    }

    private String generatePointerMap(Iterator it) {
        StringBuffer buf = new StringBuffer();
        while(it.hasNext()) {
            ResponseDeclaration responseDec = (ResponseDeclaration)it.next();
            String name = responseDec.getName();
            buf.append("        ${feature-name}PointerMap.put(\"");
            buf.append(name);
            buf.append("\", POINTER_").append(name).append(");\n");
        }
        return buf.toString();
    }

    private String generateResourceList(Set names) {
        if ( names == null )
            return "";
        StringBuffer buf = new StringBuffer();
        for(Iterator it=names.iterator(); it.hasNext(); ) {
            String name = (String)it.next();
            String escaped = StringUtil.replace(name, ".", "_");
            buf.append("    public static final String RESOURCE_").append(escaped);
            buf.append(" = \"").append(name).append("\";\n");
        }
        return buf.toString();
    }


    private String loadCodeTemplate(String filename) throws IOException {
        String ret = null;
        InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int len;
            try {
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
            }
            finally {
                out.close();
            }
            ret = out.toString("MS932");
        }
        finally {
            in.close();
        }
        return ret;
    }

    private void writeSourceFile(File destdir, String pkg, String filename, String code) throws IOException {
        pkg = pkg.replace('.', File.separatorChar);
        File dir = new File(destdir, pkg);
        if ( !dir.exists() )
            dir.mkdirs();
        File file = new File(dir, filename);
        OutputStream out = new FileOutputStream(file);
        try {
            out.write(code.getBytes("MS932"));
        }
        finally {
            out.close();
        }
    }

}


