package org.jboss.seam.pdf.ui;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.faces.component.UIComponent;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;

import org.jboss.seam.core.Manager;
import org.jboss.seam.navigation.Pages;
import org.jboss.seam.pdf.DocumentData;
import org.jboss.seam.pdf.DocumentStore;
import org.jboss.seam.pdf.ITextUtils;
import org.jboss.seam.pdf.DocumentData.DocumentType;

import com.lowagie.text.DocWriter;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Rectangle;
import com.lowagie.text.html.HtmlWriter;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.rtf.RtfWriter2;

public class UIDocument 
    extends ITextComponent
{
    public static final String COMPONENT_TYPE   = "org.jboss.seam.pdf.ui.UIDocument";

    public static DocumentType PDF  = new DocumentType("pdf",  "application/pdf");        
    public static DocumentType RTF  = new DocumentType("rtf",  "text/rtf");
    public static DocumentType HTML = new DocumentType("html", "text/html");
        
    DocWriter writer;
    Document document;
    ByteArrayOutputStream stream;

    DocumentType documentType;

    String disposition;
    String type;
    String title;
    String subject;
    String keywords;
    String author;
    String creator;
    String orientation; 

    String pageSize;
    String margins;
    Boolean marginMirroring;

    boolean sendRedirect = true;

    UISignature signatureField;

    public void setDisposition(String disposition) {
        this.disposition = disposition;
    }

    public void setType(String type) {
        this.type = type;
    }

    public void setMargins(String margins) {
        this.margins = margins;
    }

    public void setPageSize(String pageSize) {
        this.pageSize = pageSize;
    }

    public void setMarginMirroring(Boolean marginMirroring) {
        this.marginMirroring = marginMirroring;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public void setCreator(String creator) {
        this.creator = creator;
    }

    public void setKeywords(String keywords) {
        this.keywords = keywords;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setOrientation(String orientation) {
        this.orientation = orientation;
    }


    public void setSendRedirect(boolean sendRedirect) {
        this.sendRedirect = sendRedirect;
    }

    public boolean getSendRedirect() {
        return sendRedirect;
    }


    @Override
    public Object getITextObject() {
        return document;
    }

    @Override
    public void createITextObject(FacesContext context) {
        type = (String) valueBinding(context, "type", type);        
        documentType = documentTypeForName(type);

        document = new Document();
        // most of this needs to be done BEFORE document.open();

        pageSize = (String) valueBinding(context, "pageSize", pageSize);
        if (pageSize != null) {
            document.setPageSize(ITextUtils.pageSizeValue(pageSize));
        }

        orientation = (String) valueBinding(context, "orientation", orientation);    
        if (orientation != null) {
            if (orientation.equalsIgnoreCase("portrait")) {
                // do nothing
            } else if (orientation.equalsIgnoreCase("landscape")) {
                Rectangle currentSize = document.getPageSize();
                document.setPageSize(new Rectangle(currentSize.height(),
                                                   currentSize.width()));
            } else {
                throw new RuntimeException("orientation value " + orientation + "unknown");
            }
        }

        margins = (String) valueBinding(context, "margins", margins);
        if (margins != null) {
            float[] vals = ITextUtils.stringToFloatArray(margins);
            if (vals.length != 4) {
                throw new RuntimeException("margins must contain 4 float values");
            }

            document.setMargins(vals[0], vals[1], vals[2], vals[3]);           
        }

        marginMirroring = (Boolean) valueBinding(context, "marginMirroring", marginMirroring);
        if (marginMirroring != null) {
            document.setMarginMirroring(marginMirroring);
        }        
    }

    protected void initMetaData(FacesContext context) {
        title = (String) valueBinding(context, "title", title);
        if (title != null) {
            document.addTitle(title);
        }

        subject = (String) valueBinding(context, "subject", subject);
        if (subject != null) {
            document.addSubject(subject);
        }

        keywords = (String) valueBinding(context, "keywords", keywords);
        if (keywords != null) {
            document.addKeywords(keywords);
        }

        author = (String) valueBinding(context, "author", author);
        if (author != null) {
            document.addAuthor(author);
        }

        creator = (String) valueBinding(context, "creator", creator);
        if (creator != null) {
            document.addCreator(creator);
        }
    }

    @Override
    public void removeITextObject() {
        document = null;
    }

    @Override
    public void handleAdd(Object o) {
        if (o instanceof Element) {
            try {
                document.add((Element) o);
            } catch (DocumentException e) {
                throw new RuntimeException(e);
            }
        } else {
            throw new IllegalArgumentException("cannot add " + o);
        }
    }

    public void addSignature(UISignature signatureField) {
        this.signatureField = signatureField;
    }

    
    @Override
    public void encodeBegin(FacesContext context) 
        throws IOException
    {
        super.encodeBegin(context);

        stream = new ByteArrayOutputStream();

        try {
            writer = createWriterForStream(stream);

            initMetaData(context);
            processHeaders();

            document.open();
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }
    
   
    protected void processHeaders() {
        Object facet = getFacet("header");      

        if (facet == null) {
            return;
        }

        if (facet instanceof UIComponent) {
            try {
                encode(FacesContext.getCurrentInstance(), (UIComponent) facet);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } 
        }
    }



    protected String baseNameForViewId(String viewId) {
        int pos = viewId.lastIndexOf("/");
        if (pos != -1) {
            viewId = viewId.substring(pos+1);
        }

        pos = viewId.lastIndexOf(".");
        if (pos!=-1) {
            viewId = viewId.substring(0,pos);
        }

        return viewId;
    }

    
    @Override
    public void encodeEnd(FacesContext context) 
        throws IOException
    {
        document.close();

        byte[] bytes = stream.toByteArray();

        if (signatureField != null) {
            bytes = signatureField.sign(bytes);
        }
        
        String viewId = Pages.getViewId(context);        
        String baseName = baseNameForViewId(viewId); 
        
        DocumentData documentData = new DocumentData(baseName, documentType, bytes);
        String dispositionValue = (String) valueBinding(context, "disposition", disposition);
        if (dispositionValue != null) {
            documentData.setDisposition(dispositionValue);
        }
        
        if (sendRedirect) {
            DocumentStore store = DocumentStore.instance();
            String id = store.newId();

            String url = store.preferredUrlForContent(baseName, documentType.getExtension(), id);
            url = Manager.instance().encodeConversationId(url, viewId);

            store.saveData(id, documentData);
           
            removeITextObject();

            context.getExternalContext().redirect(url);
        } else {
            UIComponent parent = getParent();

            if (parent instanceof ValueHolder) {
                ValueHolder holder = (ValueHolder) parent;
                holder.setValue(documentData);
            }
        }
    }


    public DocWriter getWriter() {
        return writer;
    }    
    
    public PdfTemplate createPdfTemplate(float width, float height) {        
        PdfWriter writer = (PdfWriter) getWriter();
        return writer.getDirectContent().createTemplate(width, height);
    }
    
    private DocumentType documentTypeForName(String typeName) {    
        if (typeName != null) {
            if (typeName.equalsIgnoreCase("pdf")) {
                return PDF;
            } else if (typeName.equalsIgnoreCase("rtf")) {
                return RTF;
            } else if (typeName.equalsIgnoreCase("html")) {
                return HTML;
            }
        }
        return PDF;
    }
    

    protected DocWriter createWriterForStream(OutputStream stream) 
        throws DocumentException 
    {
        if (documentType == PDF) {
            return PdfWriter.getInstance(document, stream);
        } else if (documentType == RTF) {
            return RtfWriter2.getInstance(document, stream);
        } else if (documentType == HTML) {
            return HtmlWriter.getInstance(document, stream);
        }
        
        throw new IllegalArgumentException("unknown document type");
    }
}
