package org.richfaces.demo.components.sh;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.faces.FacesException;
import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.ajax4jsf.javascript.JSFunction;

@FacesComponent(value = "syntaxHighlighter")
@ResourceDependencies({ @ResourceDependency(library = "js", name = "shCore.js"),
    @ResourceDependency(library = "css", name = "shCore.css"),
    @ResourceDependency(library = "css", name = "shThemeDefault.css"),
    @ResourceDependency(library = "js", name = "shBrushJScript.js"),
    @ResourceDependency(library = "js", name = "shBrushJava.js"),
    @ResourceDependency(library = "js", name = "shBrushXml.js"),
    @ResourceDependency(library = "js", name = "shBrushCss.js"),
    @ResourceDependency(library = "js", name = "shBrushPlain.js") })
public class SyntaxHighlighter extends UIComponentBase {
    private static final String COMPONENT_FAMILY = "org.richfaces.SyntaxHighlighter";
    private static final String DEFAULT_SOURCE_TYPE = "xhtml";

    enum propertyKeys {
        sourceType, src, style, styleClass
    };

    @Override
    public String getFamily() {
        return COMPONENT_FAMILY;
    }

    private void renderStream(FacesContext context, InputStream stream) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        InputStreamReader in = new InputStreamReader(stream);
        char[] temp = new char[1024];
        try {
            int bytes;
            while ((bytes = in.read(temp)) > 0) {
                writer.writeText(temp, 0, bytes);
            }
        } catch (IOException e) {
            throw new FacesException(e);
        } finally {
            in.close();
        }
    }

    private void renderContent(FacesContext context) throws IOException {
        ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
        String src = getSrc();
        if (src != null) {
            InputStream stream = ec.getResourceAsStream(src);
            if (stream != null) {
                renderStream(context, stream);
            } else {
                throw new NullPointerException("resource for highlight not found");
            }
        } else {
            throw new NullPointerException("src may not be null");
        }
    }

    @Override
    public void encodeEnd(FacesContext context) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("div", null);
        writer.writeAttribute("id", this.getClientId(), null);
        writer.writeAttribute("class", this.getStyleClass(), null);
        writer.writeAttribute("style", this.getStyle(), null);
        writer.startElement("pre", null);
        writer.writeAttribute("class", new StringBuffer().append("brush: ").append(this.getSourceType()), null);
        renderContent(context);
        writer.endElement("pre");
        JSFunction function = new JSFunction("SyntaxHighlighter.all");
        writer.startElement("script", null);
        writer.write(function.toScript());
        writer.endElement("script");
        writer.endElement("div");
    }

    public String getSourceType() {
        return (String) getStateHelper().eval(propertyKeys.sourceType, DEFAULT_SOURCE_TYPE);
    }

    public void setSourceType(String sourceType) {
        getStateHelper().put(propertyKeys.sourceType, sourceType);
    }

    public String getSrc() {
        return (String) getStateHelper().eval(propertyKeys.src);
    }

    public void setSrc(String src) {
        getStateHelper().put(propertyKeys.src, src);
    }

    public String getStyle() {
        return (String) getStateHelper().eval(propertyKeys.style);
    }

    public void setStyle(String style) {
        getStateHelper().put(propertyKeys.style, style);
    }

    public String getStyleClass() {
        return (String) getStateHelper().eval(propertyKeys.styleClass);
    }

    public void setStyleClass(String styleClass) {
        getStateHelper().put(propertyKeys.styleClass, styleClass);
    }
}
