/*
 *  Copyright (c) 2024 - 2025 by Paul Hertz <ignotus@gmail.com>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package net.paulhertz.pixelaudio;

import java.util.ArrayList;

public class DiagonalZigzagGen extends PixelMapGen {
	/*
	 * See instance variables used or generated by this class in abstract class PixelMapGen.
	 * The description variable should shadow the abstract class variable with a description specific to the child class.
	 */
	public final static String description = "DiagonalZigzagGen generates a diagonal zigzag path starting at (0,0) and (0,1), then going diagonally from edge to edge. "
										   + "\nAny width and height greater than 0 are valid for the constructor DiagonalZigzagGen(int width, int height).";



	/**
	 * @param width     PixelMapGen width
	 * @param height    PixelMapGen height
	 * @param type      an AffineTransformType to apply to the coordinates of the PixelMapGen
	 */
	public DiagonalZigzagGen(int width, int height, AffineTransformType type) {
		super(width, height, type);
		this.generate();
	}

	/**
	 * @param width     PixelMapGen width
	 * @param height    PixelMapGen height
	 */
	public DiagonalZigzagGen(int width, int height) {
		super(width, height);
		this.generate();
	}


	@Override
	public String describe() {
		return DiagonalZigzagGen.description;
	}

	/**
	 * Always returns true for width and height greater than 1.
	 */
	@Override
	public boolean validate(int width, int height) {
		// any width and height > 2 will work
		if (width < 1 || height < 1) {
			System.out.println("width and height must be greater than 0.");
			return false;
		}
		return true;
	}

	/**
	 * Initialize this.coords, this.pixelMap, this.sampleMap.
	 * @return  this.pixelMap, the value for PixelAudioMapper.signalToImageLUT.
	 */
	@Override
	public int[] generate() {
		this.coords = this.generateCoordinates();
		// bitmap transforms of coordinates go here
		return this.setMapsFromCoords(this.coords);
	}

	/**
	 * Generically-named method that calls the custom coordinate generation method (here, generateZigzagDiagonalCoordinates).
	 * Consider putting additional initializations here, if required by your coordinate generation method,
	 * rather than in the generate() method, which will then only handle coords initialization and the
	 * built-in pixelMap and sampleMap initializations.
	 *
	 * @return 	An ArrayList of int[], bitmap coordinates in the order the signal mapping would visit them.
	 *
	 */
	private ArrayList<int[]> generateCoordinates() {
		return this.generateZigzagDiagonalCoordinates(this.w, this.h);
	}

	/**
	 * The coordinate generation method for this class. Both lookup tables are derived from the coordinate list created
	 * by this method. The initial step in the algorithm is down (y--). 
	 *
	 * @param   width		width of the 2D bitmap pixel array
	 * @param   height		height of the 2D bitmap pixel array
	 * @return 				an array of coordinate pairs
	 */
	private ArrayList<int[]> generateZigzagDiagonalCoordinates(int width, int height) {
		ArrayList<int[]> coordinates = new ArrayList<int[]>(width * height);
		int x = 0, y = 0;
		boolean movingUp = false;
		while (x < width && y < height) {
			coordinates.add(new int[]{x, y});
			if (movingUp) {                  	// movingUp is true, diagonal step is x++, y--
				if (x == width - 1) {          	// we hit the right edge
					y++;                        // move down 1
					movingUp = false;           // flip the diagonal direction
				}
				else if (y == 0) {             	// we hit the top edge
					x++;                        // move right 1
					movingUp = false;           // flip the diagonal direction
				}
				else {                          // diagonal step
					x++;                        // move right 1
					y--;                        // move up 1
				}
			}
			else {                            	// movingUp is false,  diagonal step is x--, y++
				if (y == height - 1) {          // we hit the bottom edge
					x++;                        // move right 1
					movingUp = true;            // flip the diagonal direction
				}
				else if (x == 0) {              // we hit the left edge
					y++;                        // move down 1
					movingUp = true;            // flip the diagonal direction
				}
				else {                          // diagonal step
					x--;                        // move left 1
					y++;                        // move down 1
				}
			}
		}
		return coordinates;
	}


	/* ------------------------------ GETTERS AND NO SETTERS ------------------------------ */
	/*                                                                                      */
	/*                  See abstract class PixMapGen for additional methods                 */
	/*                                                                                      */
	/* These include: getWidth(), getHeight(), getSize(), getPixelMap(), getPixelMapCopy(), */
	/* getSampleMap(), get SampleMapCopy(), getCoordinates(), getCoordinatesCopy().         */
	/*                                                                                      */
	/* ------------------------------------------------------------------------------------ */

	
	
	/* -------------------------- DIAGONAL ZIGZAG MULTIGEN FACTORIES --------------------------
	
	 * MultiGens create a PixelMapGen from from a list of PixelMapGen 
	 * objects (genList) and coordinate points (offsetList) where they 
	 * will be displayed. A MultiGen creates a single signal path over all
	 * the PixelMapGen objects. The path may be *continuous*, which is to say that
	 * the path through each PixelMapGen object ("gen" for short) only has to step
	 * one pixel up, down, left, or right to connect to the next gen. It may even
	 * create a loop, where the last pixel in the path is one step away from the
	 * first pixel. This is reflected in the naming conventions. 
	 * 
	 * In the method names, "ortho" refers to gens that are aligned in rows (or
	 * columns) where each new row begins one unit down or over from the previous row,
	 * always adding new gens in the same direction. In the "bou" methods 
	 * (named for boustrophodon, a method of writing text in alternating directions), 
	 * each successive row or column goes in the opposite direction from the previous
	 * one. The bou methods may provide continuous paths, the ortho methods are
	 * inherently discontinuous, like row major bitmaps or video scanlines. 
	 * 
	 * Looping methods are are almost always more complex than bou and necessarily 
	 * more complex than ortho methods. Like the fractal curves, they involve
	 * changes in direction reminiscent of folding. Looping methods often have
	 * constraints on the numbers of rows and columns that can produce a loop.
	 * The constraints arise from the connectivity offered by the different
	 * PixelMapGen child classes: Hilbert gens have connections at two adjacent
	 * corners, DiagonalZigzag gens have connections at opposite corners. 
	 * Moore gens are loops to begin with, and have no connections, but are
	 * good for very symmetrical pattern-making. BoustropheGens will vary their
	 * connectivity depending on whether their dimensions are odd or even numbers. 

	---------------------------------------------------------------------------------------- */


	
	/**
	 * @param genW    width of each zigzag gen
	 * @param genH    height of each zigzag gen
	 * @return        a looping MultiGen with 6 rows x 4 columns of DiagonalZigzagGen instances
	 */
	public static MultiGen zigzagLoop6x4(int genW, int genH) {
	    // list of PixelMapGens that create a path through an image using PixelAudioMapper
	    ArrayList<PixelMapGen> genList = new ArrayList<PixelMapGen>(); 
	    // list of x,y coordinates for placing gens from genList
	    ArrayList<int[]> offsetList = new ArrayList<int[]>();
	    int[][] locs = {{0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}, 
	    		{5,1}, {5,2}, {5,3}, {4,3}, {4,2}, {4,1},
	    		{3,1}, {3,2}, {3,3}, {2,3}, {2,2}, {2,1},
	    		{1,1}, {1,2}, {1,3}, {0,3}, {0,2}, {0,1}};
		AffineTransformType[] trans = {r90, fx90, r90, fx90, r90, fx90, 
				                       r270, fx90, r270, fx270, r90, fx270, 
				                       r270, fx90, r270, fx270, r90, fx270, 
				                       r270, fx90, r270, fx270, r90, fx270};
		int i = 0;
		for (AffineTransformType att: trans) {
		    int x = locs[i][0] * genW;
		    int y = locs[i][1] * genH;
		    offsetList.add(new int[] {x,y});
		    // println("locs: ", locs[i][0], locs[i][1]);
		    genList.add(new DiagonalZigzagGen(genW, genH, att));
		    i++;
		}
		return new MultiGen(6 * genW, 4 * genH, offsetList, genList);
	}


}
