package projectEQ2440.QRcode.decode;

import projectEQ2440.QRcode.ReedSolomon.RSdecoder;
import projectEQ2440.QRcode.info.Version;

/**
 * <b>public class Interleaver</b><br/>
 * Class to manage the treatment of the interleaving and apply the reed solomon algorithm 
 */
public class Interleaver {
	
	// The Version
	private Version version;
	
	// The number of each length in interleaved message
	private int nbrTotal;
	private int nbrShort;
	private int nbrLong;
	
	// The length of blocks and correction byte
	private int lengthCorrection;
	private int lengthShort;
	private int lengthLong;
	
	// The interleaved message
	private byte[][] interMessage;
	
	// Reed Solomon decoder
	private RSdecoder rsDecoder;
	
	/**
	 * <b>public Interleaver(Version version, int correctionLevel)</b><br/>
	 * Constructor based on the version and the correction level
	 * 
	 * @param version : the version
	 * @param correctionLevel : the correction level
	 */
	public Interleaver(Version version, int correctionLevel) {
		this.version = version;

		int[] nbr = version.nbrEachInterleavingBlock(correctionLevel);
		nbrTotal = nbr[0] + nbr[1];
		nbrShort = nbr[0];
		nbrLong = nbr[1];
		
		interMessage = new byte[nbrTotal][];
		
		lengthCorrection = version.nbrCorrectionByte(correctionLevel)/nbrTotal;
		lengthShort = version.messageLength()/nbrTotal-lengthCorrection;
		lengthLong = lengthShort + 1;
		
		int k;
		for (k=0; k<nbrShort; k++) interMessage[k] = new byte[lengthShort+lengthCorrection];
		for (k=nbrShort; k<nbrTotal; k++) interMessage[k] = new byte[lengthLong+lengthCorrection];
		
		rsDecoder = new RSdecoder(lengthCorrection/2);
	}
	
	/**
	 * <b>public byte[] decode(byte[] messageBrut)</b><br/>
	 * Apply the interleaving and reed solomon
	 * 
	 * @param messageBrut : the message to decode
	 * @return the message decoded
	 */
	public byte[] decode(byte[] messageBrut) {
		if (!interleave(messageBrut)) return null;
		
		int k;
		for (k=0; k<nbrTotal; k++) {
			if (!rsDecoder.decode(interMessage[k])) return null;
		}
		
		return uninterleave();
	}
	
	/**
	 * <b>private boolean interleave(byte[] messageBrut)</b><br/>
	 * interleave the message
	 * 
	 * @param messageBrut : the message
	 * @return if it worked
	 */
	private boolean interleave(byte[] messageBrut) {
		if (messageBrut == null) return false;
		if (messageBrut.length != version.messageLength()) return false;
		
		int i, j, k=0;
		for (j=0; j<lengthShort; j++) {
			for (i=0; i<nbrTotal; i++) {
				interMessage[i][j] = messageBrut[k];
				k++;
			}
		}
		for (i=nbrShort; i<nbrTotal; i++) {
			interMessage[i][lengthLong-1] = messageBrut[k];
			k++;
		}
		for (j=lengthShort; j<lengthShort+lengthCorrection; j++) {
			for (i=0; i<nbrTotal; i++) {
				if (i < nbrShort) interMessage[i][j] = messageBrut[k];
				else interMessage[i][j+1] = messageBrut[k];
				k++;
			}
		}
		
		return true;
	}
	
	/**
	 * <b>private byte[] uninterleave()</b><br/>
	 * unify the blocks of interleaved message 
	 * 
	 * @return the resulting message
	 */
	private byte[] uninterleave() {
		byte[] result = new byte[lengthShort*nbrShort+lengthLong*nbrLong];
		
		int i, j, k=0;
		
		for (i=0; i<nbrShort; i++) { 
			for (j=0; j<lengthShort; j++) { 
				result[k] = interMessage[i][j];
				k++;
			}
		}
		
		for (i=nbrShort; i<nbrTotal; i++) { 
			for (j=0; j<lengthLong; j++) { 
				result[k] = interMessage[i][j];
				k++;
			}
		}
		
		return result;
	}
	
}
