/* Copyright (C) 2005-2011 Fabio Riccardi */

/*
 * $RCSfile: SunCachedTile.java,v $
 *
 * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * $Revision: 1.1 $
 * $Date: 2005/02/11 04:57:02 $
 * $State: Exp $
 */
package com.lightcrafts.jai.utils;

import lombok.Getter;

import javax.media.jai.CachedTile;
import javax.media.jai.PlanarImage;
import javax.media.jai.remote.SerializableRenderedImage;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.lang.ref.WeakReference;
import java.math.BigInteger;

/**
 * Information associated with a cached tile.
 *
 * <p> This class is used by LCTileCache to create an object that
 * includes all the information associated with a tile, and is put
 * into the tile cache.
 *
 * @see LCTileCache
 *
 */
final class LCCachedTile implements CachedTile {

    // Soft or Weak references need to be used, or the objects
    // never get garbage collected.   The OpImage finalize
    // method calls removeTiles().  It was suggested, that
    // the owner be a weak reference.
    @Getter
    private final Raster tile;                // the tile to be cached

    private final WeakReference<RenderedImage> owner;        // the RenderedImage this tile belongs to

    private final int tileX;			// tile X index
    private final int tileY;			// tile Y index

    @Getter
    private final Object tileCacheMetric;     // Metric for weighting tile computation cost

    @Getter
    long tileTimeStamp;		// the last time this tile is accessed

    final Object key;			// the key used to hash this tile

    @Getter
    long tileSize;		// the memory used by this tile in bytes

    @Getter
    int action = 0;             // add, remove, update from tile cache

    /**
     * Constructor that takes a tile cache metric
     * @since 1.1
     */
    LCCachedTile(RenderedImage owner,
                  int tileX,
                  int tileY,
                  Raster tile,
                  Object tileCacheMetric) {

        this.owner = new WeakReference<>(owner);
        this.tile  = tile;
        this.tileX = tileX;
        this.tileY = tileY;

        this.tileCacheMetric = tileCacheMetric;  // may be null

        key = hashKey(owner, tileX, tileY);

        // tileMemorySize(Raster tile) inlined for performance
        DataBuffer db = tile.getDataBuffer();
        tileSize = DataBuffer.getDataTypeSize(db.getDataType()) / 8L *
                     db.getSize() * db.getNumBanks();

    }

    static Object fastHashKey(RenderedImage owner,
                              int tileX,
                              int tileY) {
        return owner.hashCode() + tileY * owner.getNumXTiles() + tileX;
    }

    /**
     * Returns the hash table "key" as a <code>Object</code> for this
     * tile.  For <code>PlanarImage</code> and
     * <code>SerializableRenderedImage</code>, the key is generated by
     * the method <code>ImageUtilgenerateID(Object) </code>.  For the
     * other cases, a <code>Long</code> object is returned.
     * The upper 32 bits for this <code>Long</code> is the tile owner's
     * hash code, and the lower 32 bits is the tile's index.
     */
    static Object hashKey(RenderedImage owner,
                              int tileX,
                              int tileY) {
        long idx = tileY * (long)owner.getNumXTiles() + tileX;

        BigInteger imageID = null;
        if (owner instanceof PlanarImage)
            imageID = (BigInteger)((PlanarImage)owner).getImageID();
        else if (owner instanceof SerializableRenderedImage)
            imageID = (BigInteger)((SerializableRenderedImage)owner).getImageID();

        if (imageID != null) {
            byte[] buf = imageID.toByteArray();
            int length = buf.length;
            byte[] buf1 = new byte[length + 8];
            System.arraycopy(buf, 0, buf1, 0, length);
            for (int i = 7, j = 0; i >= 0; i--, j += 8)
                buf1[length++] = (byte)(idx >> j);
            return new BigInteger(buf1);
        }

        idx = idx & 0x00000000ffffffffL;
        return ((long) owner.hashCode() << 32) | idx;
    }

    /*
     *  Special version of hashKey for use in LCTileCache.removeTiles().
     *  Minimizes the overhead of repeated calls to
     *  hashCode and getNumTiles(). Note that this causes a
     *  linkage between the CachedTile and LCTileCache classes
     *  in that LCTileCache now has to understand how the
     *  tileIndex is calculated.
     */
/*
    static Long hashKey(int ownerHashCode,
                        int tileIndex) {
        long idx = (long)tileIndex;
        idx = idx & 0x00000000ffffffffL;
        return new Long(((long)ownerHashCode << 32) | idx);
    }
*/
    /* Returns the owner's hash code. */
/*    static long getOwnerHashCode(Long key) {
        return key.longValue() >>> 32;
    }
*/
    /** Returns a string representation of the class object. */
    public String toString() {
        RenderedImage o = getOwner();
        String ostring = o == null ? "null" : o.toString();

        Raster t = getTile();
        String tstring = t == null ? "null" : t.toString();

        return getClass().getName() + "@" + Integer.toHexString(hashCode()) +
               ": owner = " + ostring +
               " tileX = " + tileX +
               " tileY = " + tileY +
               " tile = " + tstring +
               " key = " + ((key instanceof Long)? Long.toHexString((Long) key) : key.toString()) +
               " tileSize = " + tileSize +
               " timeStamp = " + tileTimeStamp;
    }

    /** Returns the owner of the cached tile. */
    public RenderedImage getOwner() {
        return owner.get();
    }
}
