// Copyright 2001, FreeHEP.
package org.freehep.util.io;

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

/**
 * The ASCII85InputStream decodes ASCII base-85 encoded data. The exact
 * definition of ASCII base-85 encoding can be found in the PostScript Language
 * Reference (3rd ed.) chapter 3.13.3.
 * 
 * IMPORTANT: inherits from InputStream rather than FilterInputStream so that
 * the correct read(byte[], int, int) method is used.
 * 
 * @author Mark Donszelmann
 * @version $Id: ASCII85InputStream.java 8584 2006-08-10 23:06:37Z duns $
 */
public class ASCII85InputStream extends InputStream implements ASCII85 {

    private boolean endReached;

    private int b[] = new int[4];

    private int bIndex;

    private int bLength;

    private int c[] = new int[5];

    private int lineNo;

    private int prev;

    private InputStream in;

    /**
     * Create an ASCII85 Input Stream from given stream.
     * 
     * @param input input to use
     */
    public ASCII85InputStream(InputStream input) {
        super();
        in = input;
        bIndex = 0;
        bLength = 0;
        endReached = false;
        prev = -1;
        lineNo = 1;
    }

    public int read() throws IOException {

        if (bIndex >= bLength) {
            if (endReached)
                return -1;
            bLength = readTuple();
            if (bLength < 0)
                return -1;
            bIndex = 0;
        }
        int a = b[bIndex];
        bIndex++;

        return a;
    }

    /**
     * @return current line number
     */
    public int getLineNo() {
        return lineNo;
    }

    private int readTuple() throws IOException, EncodingException {
        int cIndex = 0;
        int ch = -1;
        while ((!endReached) && (cIndex < 5)) {
            prev = ch;
            ch = in.read();
            switch (ch) {
            case -1:
                throw new EncodingException(
                        "missing '~>' at end of ASCII85 stream");
            case 'z':
                b[0] = b[1] = b[2] = b[3] = '!';
                return 4;
            case '~':
                if (in.read() != '>')
                    throw new EncodingException("Invalid ASCII85 EOD");
                endReached = true;
                break;
            case '\r':
                lineNo++;
                break;
            case '\n':
                if (prev != '\r') {
                    lineNo++;
                }
                break;
            case ' ':
            case '\t':
            case '\f':
            case 0:
                // ignored
                break;
            default:
                c[cIndex] = ch;
                cIndex++;
                break;
            }
        }

        if (cIndex > 0) {
            // fill the rest
            for (int i = 0; i < c.length; i++) {
                if (i >= cIndex) {
                    c[i] = '!';
                } else {
                    c[i] -= '!';
                }
            }

            // convert
            long d = ((c[0] * a85p4) + (c[1] * a85p3) + (c[2] * a85p2)
                    + (c[3] * a85p1) + c[4]) & 0x00000000FFFFFFFFL;

            b[0] = (int) ((d >> 24) & 0x00FF);
            b[1] = (int) ((d >> 16) & 0x00FF);
            b[2] = (int) ((d >> 8) & 0x00FF);
            b[3] = (int) (d & 0x00FF);
        }
        return cIndex - 1;
    }

    /**
     * Print out ASCII85 of a file
     * 
     * @param args filename
     * @throws Exception when file does not exist
     */
    public static void main(String[] args) throws Exception {
        if (args.length < 1) {
            System.err.println("Usage: ASCII85InputStream filename");
            System.exit(1);
        }
        ASCII85InputStream in = new ASCII85InputStream(new FileInputStream(args[0]));
        int b = in.read();
        while (b != -1) {
            System.out.write(b);
            b = in.read();
        }
        in.close();
        System.out.flush();
    }
}
