/*
 *  Copyright 2006-2009 Mikhail Kryshen
 *
 *  This file is part of Tema.
 *
 *  Tema is free software: you can redistribute it and/or modify it
 *  under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  Tema 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the 
 *  GNU Lesser General Public License along with Tema.  
 *  If not, see <http://www.gnu.org/licenses/>.
 */

package kryshen.tema;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import kryshen.tema.io.TemplateReader;

/**
 * Tema template context.
 *
 * @author Mikhail Kryshen
 */
public class Context {
    private Map<String, Object> definitions = new HashMap<String, Object>();
    private Context superContext;
    private File baseDir;
    
    public Context() {
        this(null, null);
    }
    
    public Context(Context superContext) {
        this(superContext, null);
    }
    
    public Context(File baseDir) {
        this(null, baseDir);
    }
    
    public Context(Context superContext, File baseDir) {
        this.superContext = superContext;
        this.baseDir = baseDir;
        
        definitions.put("this", this);
        
        if (superContext != null)
            definitions.put("super", superContext);
    }
    
    public void clear() {
        definitions.clear();
    }
    
    public boolean export(String name) {
        Object value = definitions.get(name);
        
        if (value == null)
            return false;
        
        export(name, value);
        return true;
    }
    
    public void export(String name, Object value) {
        if (superContext == null) {
            set(name, value);
            return;
        }
        
        definitions.remove(name);
        superContext.export(name, value);
    }
    
    public Object get(String name) throws TemplateException {
        Object value = definitions.get(name);
        
        if (value == null && superContext != null)
            return superContext.get(name);
        
        return value;
    }
    
    /**
     * Set the definition value.
     *
     * @param name Variable name.
     * @param value New variable value.
     */
    public void set(String name, Object value) {
        definitions.put(name, value);
    }
    
    /**
     * Remove the definition recursively.
     * 
     * @param name Definition name.
     * @return true if the definition was found and removed.
     */
    public boolean unset(String name) {
        if (definitions.remove(name) != null)
            return true;
        
        if (superContext != null)
            return superContext.unset(name);
        
        return false;
    }

    int parseInContext(TemplateReader in, Writer out, Context outer)
            throws IOException, TemplateException {

        final String OUTER = "outer";

        boolean hadOuter = definitions.containsKey(OUTER);
        Object oldOuter = null;
        if (hadOuter) {
            oldOuter = definitions.get(OUTER);
        }
        
        definitions.put(OUTER, outer);

        try {
            TemplateParser parser = new TemplateParser(this);
            return parser.parse(in, out);
        } finally {
            if (hadOuter) {
                definitions.put(OUTER, oldOuter);
            } else {
                definitions.remove(OUTER);
            }
        }
    }

    /**
     * Returns base directory which should be used to resolve
     * relative path names in template.
     */
    public File getBaseDirectory() {
        if (baseDir == null && superContext != null)
            return superContext.getBaseDirectory();
        
        return baseDir;
    }
}