import java.awt.*;
import java.awt.image.*;
import java.util.HashMap;

/** Represents a vertical stripe of a texture. */
public class TextureColumn {
	private int[] src;
	private Color mean;
	private int sOffs;
	private int sScan;
	private int sHeight;
	private int sLastOffs;

	TextureColumn(int[] src, int sOffs, int sScan, int sHeight,
			Color mean) {
		this.src = src;
		this.sOffs = sOffs;
		this.sScan = sScan;
		this.sHeight = sHeight;
		this.sLastOffs = sOffs + sScan * sHeight;
		this.mean = mean;
	}

	/** Returns the number of pixels this stripe contains. */
	public int getHeight() { return sHeight; }

	/** Copies this stripe into the given pixels array. The
	 * column will be scaled to fit into the array as
	 * specified. Both the starting point and the ending point
	 * may be outside of the actual array; in this case, only
	 * the appropriate portion of the vertical stripe will be
	 * scaled to fit into the array.
	 *
	 * @param dst the array into which the vertical stripe
	 * should be copied.
	 * @param dOffs the offset within the array where the top
	 * pixel of the texture should be placed. Note that this
	 * may be a negative value if the top portion of the
	 * texture should not appear at all.
	 * @param dScan the number of array entries separating
	 * adjacent pixels in a column.
	 * @param dHeight the height of the column in the array
	 * into which this texture column should be mapped.
	 * @param intensity a multiplier between 0.0 and 1.0
	 * representing how bright the copy should be.
	 */
	public void fill(int[] dst, int dOffs, int dScan, int dHeight,
			double intensity) {
		double scale = (double) dHeight / sHeight;
		int dLastOffs = Math.min(dst.length, dOffs + dScan * dHeight);

		if(intensity > 1.0) intensity = 1.0;
		int meanComps = (mean.getRed() + mean.getGreen() * 5 / 4 + mean.getBlue()) / 3;
		if(scale >= 1.0) {
			// Each texture pixel maps to at least one screen pixel
			double delta = 0.0;
			double tr = 0;
			double tg = 0;
			double tb = 0;
			int d = dOffs;
			int s = sOffs;
			if(d < 0) {
				int dSkip = -dOffs / dScan;
				int sSkip = (int) Math.round(dSkip / scale);
				d += dSkip * dScan;
				s += (sSkip % sHeight) * sScan;
			}
			while(d < dLastOffs) {
				// read off pixel
				int r = (src[s] >> 16) & 0xFF;
				int g = (src[s] >>  8) & 0xFF;
				int b = (src[s]      ) & 0xFF;
				s += sScan; if(s == sLastOffs) s = sOffs;

				// first handle leftover fraction delta
				double mult = 1.0 - delta;
				tr += r * mult;
				tg += g * mult;
				tb += b * mult;
				if(d >= 0) dst[d] = toRgb((int) tr, (int) tg, (int) tb, intensity);
				d += dScan;

				delta = scale - mult; // number of pixels left to fill
				
				// then fill any full pixels
				int p = toRgb(r, g, b, intensity);
				while(delta >= 1.0 && d < dLastOffs) {
					if(d >= 0) dst[d] = p;
					d += dScan;
					delta -= 1.0;
				}

				// and keep any remaining color for next iteration
				tr = r * delta;
				tg = g * delta;
				tb = b * delta;
			}
		} else if(scale > 0.0) {
			// Each texture pixel maps to a fraction of a screen pixel
			double init_delta = 1.0 / scale;
			double delta = init_delta;
			double mult = scale * intensity;
			int tr = 0;
			int tg = 0;
			int tb = 0;
			int d = dOffs;
			int s = sOffs;
			while(d < dLastOffs) {
				// read off pixel
				int r = (src[s] >> 16) & 0xFF;
				int g = (src[s] >>  8) & 0xFF;
				int b = (src[s]      ) & 0xFF;
				s += sScan; if(s == sLastOffs) s = sOffs;

				if(delta > 1.0) {
					// average entire pixel into total
					tr += r; tg += g; tb += b;
					delta -= 1.0;
				} else {
					// only part of pixel will be averaged in

					// add in fraction of each component
					int dr = (int) (r * delta); tr += dr;
					int dg = (int) (g * delta); tg += dg;
					int db = (int) (b * delta); tb += db;

					// scale components
					if(d >= 0) dst[d] = toRgb(tr, tg, tb, mult);
					d += dScan;

					// keep remainder
					tr = r - dr;
					tg = g - dg;
					tb = b - db;
					delta = init_delta - (1.0 - delta);
				}
			}
		} else {
			throw new IllegalArgumentException("invalid scale " + scale);
		}
	}

	private static int toRgb(int r, int g, int b, double mult) {
		int tr = (int) (r * mult); if(tr > 255) tr = 255;
		int tg = (int) (g * mult); if(tg > 255) tg = 255;
		int tb = (int) (b * mult); if(tb > 255) tb = 255;
		return 0xFF000000 | (tr << 16) | (tg << 8) | tb;
	}
}
