/*
 * 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.commons.geometry.io.euclidean.threed.stl;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackInputStream;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Arrays;

import org.apache.commons.geometry.io.core.internal.GeometryIOUtils;
import org.apache.commons.geometry.io.euclidean.threed.FacetDefinitionReader;

/** Utility class with factory methods for constructing {@link FacetDefinitionReader}
 * instances for STL content.
 */
public final class StlFacetDefinitionReaders {

    /** Utility class; no instantiation. */
    private StlFacetDefinitionReaders() { }

    /** Construct a {@link FacetDefinitionReader} for reading STL content from the
     * given input. The format of the input is checked to determine if it is a binary
     * or text file and an appropriate reader is returned.
     * @param in input to read from
     * @param charset charset to use when checking the input for text content;
     *      if null, the input is assumed to use the UTF-8 charset
     * @return facet definition reader
     * @throws IllegalStateException if a parsing error occurs
     * @throws java.io.UncheckedIOException if an I/O error occurs
     */
    public static FacetDefinitionReader create(final InputStream in, final Charset charset) {
        final Charset inputCharset = charset != null ?
                charset :
                StlConstants.DEFAULT_CHARSET;

        final byte[] testBytes = StlConstants.SOLID_START_KEYWORD.getBytes(inputCharset);
        final byte[] actualBytes = new byte[testBytes.length];

        final int read = GeometryIOUtils.applyAsIntUnchecked(in::read, actualBytes);
        if (read < actualBytes.length) {
            throw GeometryIOUtils.parseError(MessageFormat.format(
                    "Cannot determine STL format: attempted to read {0} bytes but found only {1} available",
                    actualBytes.length, read));
        }

        // "unread" the test bytes so that the created readers can start from the
        // beginning of the content
        final PushbackInputStream pushbackInput = new PushbackInputStream(in, actualBytes.length);
        GeometryIOUtils.acceptUnchecked(pushbackInput::unread, actualBytes);

        if (Arrays.equals(testBytes, actualBytes)) {
            // this is a text file
            return new TextStlFacetDefinitionReader(
                    new BufferedReader(new InputStreamReader(pushbackInput, inputCharset)));
        } else {
            // this is a binary file
            return new BinaryStlFacetDefinitionReader(pushbackInput);
        }
    }
}
