package org.bouncycastle.mime;

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

import org.bouncycastle.mime.encoding.Base64InputStream;
import org.bouncycastle.mime.encoding.QuotedPrintableInputStream;

public class BasicMimeParser
    implements MimeParser
{
    private final InputStream src;
    private final MimeParserContext parserContext;
    private final String defaultContentTransferEncoding;
    private Headers headers;

    private boolean isMultipart = false;
    private final String boundary;

    public BasicMimeParser(
        InputStream src)
        throws IOException
    {
        this(null, new Headers(src, "7bit"), src);
    }

    public BasicMimeParser(
        MimeParserContext parserContext, InputStream src)
        throws IOException
    {
        this(parserContext, new Headers(src, parserContext.getDefaultContentTransferEncoding()), src);
    }

    public BasicMimeParser(
        Headers headers, InputStream content)
    {
        this(null, headers, content);
    }

    public BasicMimeParser(
        MimeParserContext parserContext, Headers headers, InputStream src)
    {
        if (headers.isMultipart())
        {
            isMultipart = true;
            boundary = headers.getBoundary();
        }
        else
        {
            boundary = null;
        }

        this.headers = headers;
        this.parserContext = parserContext;
        this.src = src;
        this.defaultContentTransferEncoding = (parserContext != null) ? parserContext.getDefaultContentTransferEncoding() : "7bit";
    }



    public void parse(MimeParserListener listener)
        throws IOException
    {
        MimeContext context = listener.createContext(parserContext, headers);

        String s;
        if (isMultipart)    // Signed
        {
            MimeMultipartContext mContext = (MimeMultipartContext)context;
            String startBoundary = "--" + boundary;
            boolean startFound = false;
            int partNo = 0;
            LineReader rd = new LineReader(src);
            while ((s = rd.readLine()) != null && !"--".equals(s))
            {
                if (startFound)
                {
                    InputStream inputStream = new BoundaryLimitedInputStream(src, boundary);
                    Headers headers = new Headers(inputStream, defaultContentTransferEncoding);

                    MimeContext partContext = mContext.createContext(partNo++);
                    inputStream = partContext.applyContext(headers, inputStream);

                    listener.object(parserContext, headers, processStream(headers, inputStream));

                    if (inputStream.read() >= 0)
                    {
                        throw new IOException("MIME object not fully processed");
                    }
                }
                else if (startBoundary.equals(s))
                {
                    startFound = true;
                    InputStream inputStream = new BoundaryLimitedInputStream(src, boundary);
                    Headers headers = new Headers(inputStream, defaultContentTransferEncoding);

                    MimeContext partContext = mContext.createContext(partNo++);
                    inputStream = partContext.applyContext(headers, inputStream);

                    listener.object(parserContext, headers, processStream(headers, inputStream));

                    if (inputStream.read() >= 0)
                    {
                        throw new IOException("MIME object not fully processed");
                    }
                }
            }
        }
        else
        {
            InputStream inputStream = context.applyContext(headers, src);

            listener.object(parserContext, headers, processStream(headers, inputStream));
        }
    }

    public boolean isMultipart()
    {
        return isMultipart;
    }

    private InputStream processStream(Headers headers, InputStream inputStream)
    {
        if (headers.getContentTransferEncoding().equals("base64"))
        {
            return new Base64InputStream(inputStream);
        }
        else if (headers.getContentTransferEncoding().equals("quoted-printable"))
        {
            return new QuotedPrintableInputStream(inputStream);
        }
        else
        {
            return inputStream;
        }
    }
}