/*
 *  Copyright (C) 2006 Mikhail A. Kryshen
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  $Id: FunctionDataParser.java,v 1.5 2006/12/14 14:39:22 mikhail Exp $
 */

package kryshen.tema;

import java.io.*;
import java.util.*;

import static kryshen.tema.TemplateParser.Result;
import static kryshen.tema.TemplateParser.FunctionData;
import static kryshen.tema.TemplateParser.Terminator;

/**
 * Parser for a function data.
 *
 * @author Mikhail A. Kryshen
 */
public class FunctionDataParser {
    static final char[] ARG_SEPARATORS = TemplateParser.LIST_SEPARATORS;

    private TemplateParser tp;
    private FunctionData fd;
    private TemplateReader in;

    private boolean available = true;
    private boolean first = true;

    FunctionDataParser(TemplateParser tp, FunctionData fd,
                       TemplateReader in) {
        this.tp = tp;
        this.fd = fd;
        this.in = in;
    }

    private TemplateParser.Result parseData(Writer out, boolean argument)
	throws IOException, TemplateException {
        
        if (!available)
            throw new TemplateException
		("Unexpected end of instruction data.", in);

        TemplateParser.Result r;

        if (fd == FunctionData.RECURSIVE) {
            r = tp.parse(in, out, true, 
                         argument ? ARG_SEPARATORS : null,
                         (argument || !first) ? ARG_SEPARATORS : null);
        } else if (fd == FunctionData.NONRECURSIVE) {
            r = tp.parse(in, out, false, 
                         argument ? ARG_SEPARATORS : null,
                         (argument || !first) ? ARG_SEPARATORS : null);
        } else if (fd == FunctionData.SUBFUNCTION && argument) {
            // Subfunction can pass a list of arguments
            StringWriter sw = new StringWriter();
            tp.parseFunction(in, sw);
            sw.close();
            in = new TemplateReader(new StringReader(sw.toString()), in);
            fd = FunctionData.NONRECURSIVE;
            r = parseData(out, true);
        } else if (fd == FunctionData.SUBFUNCTION) {
            r = new Result(null, tp.parseFunction(in, out), false);
        } else {
            throw new IllegalArgumentException("Invalid FunctiodData: " + fd);
        }

        if (r.terminator != TemplateParser.Terminator.SEPARATOR)
            available = false; // No more function data available.

        first = false;
        return r;
    }

    public int parseData(Writer out) throws IOException, TemplateException {
        return parseData(out, false).retCode; 
    }

    public String getData() throws IOException, TemplateException {        
        StringWriter sw = new StringWriter();
        parseData(sw);
        sw.close();
        return sw.toString();
    }

    public int parseNextArg(Writer out) throws IOException, TemplateException {
        TemplateParser.Result r = parseData(out, true);

        // Ignore empty arguments.
        while (r.empty && available) {
            r = parseData(out, true);
        }

        return r.retCode;
    }

    public String getNextArg() throws IOException, TemplateException {        
        StringWriter sw = new StringWriter();
        parseNextArg(sw);
        sw.close();
        return sw.toString();
    }

    public List<String> getArgs() throws IOException, TemplateException {
	List<String> l = new ArrayList<String>();

	while (available) {
	    l.add(getNextArg());
	}
	
	return l;
    }

    public boolean hasMoreData() {
        return available;
    }

    public TemplateParser getTemplateParser() {
        return tp;
    }
    
    public TemplateReader getTemplateReader() {
        return in;
    }
}