/*
 * @(#)PopMessageReader.java
 *
 * Copyright (c) 2001-2002 JScape
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JScape. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JScape.
 */
package com.jscape.inet.pop;

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


/**
 * Reads email message from POP server.  A new instance of this reader is created for each message retrieved using the Pop.getMessageReader method.
 *
 * @see Pop#getMessageReader
 */
public class PopMessageReader
        extends InputStreamReader {
    // Represents a carriage return
    private static final byte CR = 13;

    // Represents a line feed
    private static final byte LF = 10;

    // Represents a period 
    private static final byte PERIOD = 46;

    // Delimiter for ending of an email message
    private static final byte[] DELIM = { CR, LF, PERIOD, CR, LF };

    // number of bytes read
    private int count = 0;

    // current byte read
    private int current = -1;

    // previous byte read
    private int prev = -1;

    // previous byte read before last
    private int prev2 = -1;

    // flag to indicate whether leading period was detected
    private boolean leadingPeriod = false;

    /**
     * Constructs a new PopMessageReader instance.
     *
     * @param input the input stream to read from
     */
    public PopMessageReader(InputStream input) {
        super(input);
    }

    /**
     * Reads characters into a portion of an array.
     *
     * @param cbuf   the destination buffer
     * @param offset the offset at which to start storing characters
     * @param length the maximum number of characters to read
     *
     * @return the number of characters read, or -1 if the end of the stream has been reached
     *
     * @throws IOException if an I/O related error occurs
     */
    public int read(char[] cbuf, int offset, int length)
            throws IOException {
        int count = 0;

        for (int i = 0; i < length; i++) {
            current = read();

            if (current == -1) {
                if (count == 0) {
                    return -1;
                } else {
                    return count;
                }
            }

            count++;
            cbuf[offset++] = (char) current;
        }

        return count;
    }

    /**
     * Reads a single character from stream skipping leading periods.
     *
     * @return the character read or -1 if end of stream has been reached.
     *
     * @throws IOException if an I/O related error occurs
     */
    public int read()
            throws IOException {
        // all delimiters matched .. return EOF
        if (count == DELIM.length) {
            return -1;
        }

        // read next byte
        current = super.read();

        // check if delimiter matched
        if (DELIM[count] == current) {
            count++;
        } else {
            // reset counter
            if (count != 0) {
                count = 0;
            }

            // check again for match
            if (DELIM[count] == current) {
                count++;
            }
        }

        // strip leading periods
        if (current == PERIOD) {
            if ((prev == LF) && (prev2 == CR)) {
                // current byte is a leading period
                leadingPeriod = true;
            } else if (prev == PERIOD) {
                if (leadingPeriod) {
                    // leading period is on and current byte is period .. ignore this period , reset and return next byte
                    leadingPeriod = false;
                    current = read();
                } else {
                    // reset leading period
                    leadingPeriod = false;
                }
            } else {
                // reset leading period
                leadingPeriod = false;
            }
        } else {
            // current byte is not a period

            // reset leading period 
            leadingPeriod = false;
        }

        // update 2 previous bytes
        prev2 = prev;
        prev = current;

        // return value
        return current;
    }
}
