/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jena.atlas.io;

import static org.apache.jena.atlas.io.IO.EOF;

import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;

/**
 * Buffering reader without the (hidden) sync overhead in BufferedReader
 *
 * @see java.io.BufferedReader
 */

public final class CharStreamBuffered extends CharStreamReader {
    /* package */ static final int CB_SIZE = 128 * 1024;

    private final char[] chars;            // CharBuffer?
    private int buffLen = 0;
    private int idx = 0;

    private final Source source;

    public CharStreamBuffered(Reader r) {
        this(r, CB_SIZE);
    }

    /**
     * @param r
     * @param buffSize
     */
    public CharStreamBuffered(Reader r, int buffSize) {
        super();
        source = new SourceReader(r);
        chars = new char[buffSize];
    }

    // Local adapter/encapsulation
    private interface Source {
        int fill(char[] array);
        void close();
    }

    static final class SourceReader implements Source {
        final private Reader reader;
        private boolean isClosed = false;
        SourceReader(Reader r) {
            reader = r;
        }

        @Override
        public void close() {
            try {
                isClosed = true;
                reader.close();
            } catch (IOException ex) {
                IO.exception(ex);
            }
        }

        @Override
        public int fill(char[] array) {
            if ( isClosed )
                return -1;
            try {
                return reader.read(array);
            } catch (EOFException ex) {
                close();
                return -1;
            } catch (IOException ex) {
                close();
                IO.exception(ex);
                return -1;
            }
        }
    }

    @Override
    public final int advance() {
        if ( idx >= buffLen )
            // Points outside the array. Refill it
            fillArray();

        // Advance one character.
        if ( buffLen >= 0 ) {
            char ch = chars[idx];
            // Advance the lookahead character
            idx++;
            return ch;
        } else
            // Buffer empty, end of stream.
            return EOF;
    }

    private long total = 0 ;
    private int fillArray() {
        int x = source.fill(chars);
        if ( x >= 0 )
            total += x;
        idx = 0;
        buffLen = x;   // Maybe -1
        return x;
    }

    @Override
    public void closeStream() {
        source.close();
    }
}
