/*
 * 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.imaging.formats.tiff;

/**
 * Provides a simple container for numeric-raster data. Some TIFF files are used
 * to store floating-point or integer data rather than images. This class is
 * intended to support access to those TIFF files.
 * <p>
 * <strong>Note:</strong> The getData() and getIntData() methods can return
 * direct references to the internal arrays stored in instances of this class.
 * Because these are not safe copies of the data, an application that modified
 * the arrays returned by these methods will change the content of the
 * associated instance. This approach is used for purposes of efficiency when
 * dealing with very large TIFF images.
 * <p>
 * <strong>Data layout:</strong> The elements in the returned array are stored
 * in row-major order. In cases where the data contains multiple samples per
 * raster cell (pixel), the data is organized into blocks of data one sample at
 * a time. The first block contains width*height values for the first sample for
 * each cell, the second block contains width*height values for the second
 * sample for each cell, etc. Thus, the array index for a particular value is
 * computed as
 * <pre>
 *    index = y*width + x + iSample * width *height;
 * </pre>
 */
public abstract class TiffRasterData {

    protected final int width;
    protected final int height;
    protected final int samplesPerPixel;
    protected final int nCells;
    protected final int planarOffset;


    /**
     * Constructs an instance allocating memory for the specified dimensions.
     *
     * @param width a value of 1 or greater
     * @param height a value of 1 or greater
     * @param samplesPerPixel a value of 1 or greater
     */
    public TiffRasterData(final int width, final int height, final int samplesPerPixel) {
        if (width <= 0 || height <= 0) {
            throw new IllegalArgumentException(
                    "Raster dimensions less than or equal to zero are not supported");
        }
        if (samplesPerPixel <= 0){
            throw new IllegalArgumentException(
                    "Raster samples-per-pixel specification must be at least 1");
        }
        this.width = width;
        this.height = height;
        this.samplesPerPixel = samplesPerPixel;
        nCells = width*height*samplesPerPixel;
        planarOffset = width * height;
    }

    protected final int checkCoordinatesAndComputeIndex(final int x, final int y, final int i) {
        if (x < 0 || x >= width || y < 0 || y >= height) {
            throw new IllegalArgumentException(
                    "Coordinates out of range (" + x + ", " + y + ")");
        }
        if (i < 0 || i >= samplesPerPixel) {
            throw new IllegalArgumentException(
                    "Sample index out of range, value " + i
                    + " where valid range is (0,"
                    + (samplesPerPixel - 1) + ")");
        }
        return y * width + x + i * planarOffset;
    }

    /**
     * Returns the content stored as an array in this instance. Note that in
     * many cases, the returned array is <strong>not</strong> a safe copy of the
     * data but a direct reference to the member element. In such cases,
     * modifying it would directly affect the content of the instance. While
     * this design approach carries some risk in terms of data security, it was
     * chosen for reasons of performance and memory conservation. TIFF images
     * that contain floating-point data are often quite large. Sizes of 100
     * million raster cells are common. Making a redundant copy of such a large
     * in-memory object might exceed the resources available to a Java
     * application.
     * <p>
     * See the class API documentation above for notes on accessing array
     * elements.
     *
     * @return the data content stored in this instance.
     */
    public abstract float[] getData();

    /**
     * Gets the raster data type from the instance.
     *
     * @return a valid enumeration value.
     */
    public abstract TiffRasterDataType getDataType();

    /**
     * Gets the height (number of rows) of the raster.
     *
     * @return the height of the raster.
     */
    public final int getHeight() {
        return height;
    }

    /**
     * Returns the content stored as an array in this instance. Note that in
     * many cases, the returned array is <strong>not</strong> a safe copy of the
     * data but a direct reference to the member element. In such cases,
     * modifying it would directly affect the content of the instance. While
     * this design approach carries some risk in terms of data security, it was
     * chosen for reasons of performance and memory conservation. TIFF images
     * that contain floating-point data are often quite large. Sizes of 100
     * million raster cells are common. Making a redundant copy of such a large
     * in-memory object might exceed the resources available to a Java
     * application.
     * <p>
     * See the class API documentation above for notes on accessing array
     * elements.
     *
     * @return the data content stored in this instance.
     */
    public abstract int[] getIntData();

    /**
     * Gets the value stored at the specified raster coordinates.
     *
     * @param x integer coordinate in the columnar direction
     * @param y integer coordinate in the row direction
     * @return the value stored at the specified location
     */
    public abstract int getIntValue(int x, int y);

    /**
     * Gets the value stored at the specified raster coordinates.
     *
     * @param x integer coordinate in the columnar direction
     * @param y integer coordinate in the row direction
     * @param i integer sample index (for data sets giving multiple samples per
     * raster cell).
     * @return the value stored at the specified location
     */
    public abstract int getIntValue(int x, int y, int i);

    /**
     * Gets the number of samples per pixel.
     *
     * @return a value of 1 or greater.
     */
    public final int getSamplesPerPixel() {
        return samplesPerPixel;
    }

    /**
     * Tabulates simple statistics for the raster and returns an instance
     * containing general metadata.
     *
     * @return a valid instance containing a safe copy of the current simple
     * statistics for the raster.
     */
    public abstract TiffRasterStatistics getSimpleStatistics();

    /**
     * Tabulates simple statistics for the raster excluding the specified value
     * and returns an instance containing general metadata.
     *
     * @param valueToExclude exclude samples with this specified value.
     * @return a valid instance.
     */
    public abstract TiffRasterStatistics getSimpleStatistics(float valueToExclude);

    /**
     * Gets the value stored at the specified raster coordinates.
     *
     * @param x integer coordinate in the columnar direction
     * @param y integer coordinate in the row direction
     * @return the value stored at the specified location; potentially a
     * Float&#46;NaN.
     */
    public abstract float getValue(int x, int y);

    /**
     * Gets the value stored at the specified raster coordinates.
     *
     * @param x integer coordinate in the columnar direction
     * @param y integer coordinate in the row direction
     * @param i integer sample index
     * @return the value stored at the specified location; potentially a
     * Float&#46;NaN.
     */
    public abstract float getValue(int x, int y, int i);

    /**
     * Gets the width (number of columns) of the raster.
     *
     * @return the width of the raster
     */
    public final int getWidth() {
        return width;
    }

    /**
     * Sets the value stored at the specified raster coordinates.
     *
     * @param x integer coordinate in the columnar direction
     * @param y integer coordinate in the row direction
     * @param value the value to be stored at the specified location.
     */
    public abstract void setIntValue(int x, int y, int value);

    /**
     * Sets the value stored at the specified raster coordinates.
     *
     * @param x integer coordinate in the columnar direction
     * @param y integer coordinate in the row direction
     * @param i integer sample index (for data sets giving multiple samples per
     * raster cell).
     * @param value the value to be stored at the specified location.
     */
    public abstract void setIntValue(int x, int y, int i, int value);

    /**
     * Sets the value stored at the specified raster coordinates.
     *
     * @param x integer coordinate in the columnar direction
     * @param y integer coordinate in the row direction
     * @param value the value to be stored at the specified location;
     * potentially a Float&#46;NaN.
     */
    public abstract void setValue(int x, int y, float value);

    /**
     * Sets the value stored at the specified raster coordinates.
     *
     * @param x integer coordinate in the columnar direction
     * @param y integer coordinate in the row direction
     * @param i integer sample index (for data sets giving multiple samples per
     * raster cell).
     * @param value the value to be stored at the specified location;
     * potentially a Float&#46;NaN.
     */
    public abstract void setValue(int x, int y, int i, float value);
}
