/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
 * Copyright (C) 2000 ymnk, JCraft,Inc.
 *  
 * Written by: 2000 ymnk<ymnk@jcraft.com>
 *   
 * Many thanks to 
 *   Monty <monty@xiph.org> and 
 *   The XIPHOPHORUS Company http://www.xiph.org/ .
 * JOrbis has been based on their awesome works, Vorbis codec.
 *   
 * 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 2 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 com.jcraft.jorbis;

import com.jcraft.jogg.*;

class Mapping0 extends FuncMapping {
	static int seq = 0;

	void free_info(Object imap) {
	};

	void free_look(Object imap) {
	}

	Object look(DspState vd, InfoMode vm, Object m) {
		// System.err.println("Mapping0.look");
		Info vi = vd.vi;
		LookMapping0 look = new LookMapping0();
		InfoMapping0 info = look.map = (InfoMapping0) m;
		look.mode = vm;

		look.time_look = new Object[info.submaps];
		look.floor_look = new Object[info.submaps];
		look.residue_look = new Object[info.submaps];

		look.time_func = new FuncTime[info.submaps];
		look.floor_func = new FuncFloor[info.submaps];
		look.residue_func = new FuncResidue[info.submaps];

		for (int i = 0; i < info.submaps; i++) {
			int timenum = info.timesubmap[i];
			int floornum = info.floorsubmap[i];
			int resnum = info.residuesubmap[i];

			look.time_func[i] = FuncTime.time_P[vi.time_type[timenum]];
			look.time_look[i] = look.time_func[i].look(vd, vm,
					vi.time_param[timenum]);
			look.floor_func[i] = FuncFloor.floor_P[vi.floor_type[floornum]];
			look.floor_look[i] = look.floor_func[i].look(vd, vm,
					vi.floor_param[floornum]);
			look.residue_func[i] = FuncResidue.residue_P[vi.residue_type[resnum]];
			look.residue_look[i] = look.residue_func[i].look(vd, vm,
					vi.residue_param[resnum]);

		}

		if (vi.psys != 0 && vd.analysisp != 0) {
			// ??
		}

		look.ch = vi.channels;

		return (look);
	}

	void pack(Info vi, Object imap, Buffer opb) {
		InfoMapping0 info = (InfoMapping0) imap;

		/*
		 * another 'we meant to do it this way' hack... up to beta 4, we packed
		 * 4 binary zeros here to signify one submapping in use. We now redefine
		 * that to mean four bitflags that indicate use of deeper features;
		 * bit0:submappings, bit1:coupling, bit2,3:reserved. This is backward
		 * compatable with all actual uses of the beta code.
		 */

		if (info.submaps > 1) {
			opb.write(1, 1);
			opb.write(info.submaps - 1, 4);
		} else {
			opb.write(0, 1);
		}

		if (info.coupling_steps > 0) {
			opb.write(1, 1);
			opb.write(info.coupling_steps - 1, 8);
			for (int i = 0; i < info.coupling_steps; i++) {
				opb.write(info.coupling_mag[i], Util.ilog2(vi.channels));
				opb.write(info.coupling_ang[i], Util.ilog2(vi.channels));
			}
		} else {
			opb.write(0, 1);
		}

		opb.write(0, 2); /* 2,3:reserved */

		/* we don't write the channel submappings if we only have one... */
		if (info.submaps > 1) {
			for (int i = 0; i < vi.channels; i++)
				opb.write(info.chmuxlist[i], 4);
		}
		for (int i = 0; i < info.submaps; i++) {
			opb.write(info.timesubmap[i], 8);
			opb.write(info.floorsubmap[i], 8);
			opb.write(info.residuesubmap[i], 8);
		}
	}

	// also responsible for range checking
	Object unpack(Info vi, Buffer opb) {
		InfoMapping0 info = new InfoMapping0();

		if (opb.read(1) != 0) {
			info.submaps = opb.read(4) + 1;
		} else {
			info.submaps = 1;
		}

		if (opb.read(1) != 0) {
			info.coupling_steps = opb.read(8) + 1;

			for (int i = 0; i < info.coupling_steps; i++) {
				int testM = info.coupling_mag[i] = opb.read(Util
						.ilog2(vi.channels));
				int testA = info.coupling_ang[i] = opb.read(Util
						.ilog2(vi.channels));

				if (testM < 0 || testA < 0 || testM == testA
						|| testM >= vi.channels || testA >= vi.channels) {
					// goto err_out;
					info.free();
					return (null);
				}
			}
		}

		if (opb.read(2) > 0) { /* 2,3:reserved */
			info.free();
			return (null);
		}

		if (info.submaps > 1) {
			for (int i = 0; i < vi.channels; i++) {
				info.chmuxlist[i] = opb.read(4);
				if (info.chmuxlist[i] >= info.submaps) {
					info.free();
					return (null);
				}
			}
		}

		for (int i = 0; i < info.submaps; i++) {
			info.timesubmap[i] = opb.read(8);
			if (info.timesubmap[i] >= vi.times) {
				info.free();
				return (null);
			}
			info.floorsubmap[i] = opb.read(8);
			if (info.floorsubmap[i] >= vi.floors) {
				info.free();
				return (null);
			}
			info.residuesubmap[i] = opb.read(8);
			if (info.residuesubmap[i] >= vi.residues) {
				info.free();
				return (null);
			}
		}
		return info;
	}

	float[][] pcmbundle = null;
	int[] zerobundle = null;
	int[] nonzero = null;
	Object[] floormemo = null;

	synchronized int inverse(Block vb, Object l) {
		DspState vd = vb.vd;
		Info vi = vd.vi;
		LookMapping0 look = (LookMapping0) l;
		InfoMapping0 info = look.map;
		InfoMode mode = look.mode;
		int n = vb.pcmend = vi.blocksizes[vb.W];

		float[] window = vd.window[vb.W][vb.lW][vb.nW][mode.windowtype];
		if (pcmbundle == null || pcmbundle.length < vi.channels) {
			pcmbundle = new float[vi.channels][];
			nonzero = new int[vi.channels];
			zerobundle = new int[vi.channels];
			floormemo = new Object[vi.channels];
		}

		// time domain information decode (note that applying the
		// information would have to happen later; we'll probably add a
		// function entry to the harness for that later
		// NOT IMPLEMENTED

		// recover the spectral envelope; store it in the PCM vector for now
		for (int i = 0; i < vi.channels; i++) {
			float[] pcm = vb.pcm[i];
			int submap = info.chmuxlist[i];

			floormemo[i] = look.floor_func[submap].inverse1(vb,
					look.floor_look[submap], floormemo[i]);
			if (floormemo[i] != null) {
				nonzero[i] = 1;
			} else {
				nonzero[i] = 0;
			}
			for (int j = 0; j < n / 2; j++) {
				pcm[j] = 0;
			}

		}

		for (int i = 0; i < info.coupling_steps; i++) {
			if (nonzero[info.coupling_mag[i]] != 0
					|| nonzero[info.coupling_ang[i]] != 0) {
				nonzero[info.coupling_mag[i]] = 1;
				nonzero[info.coupling_ang[i]] = 1;
			}
		}

		// recover the residue, apply directly to the spectral envelope

		for (int i = 0; i < info.submaps; i++) {
			int ch_in_bundle = 0;
			for (int j = 0; j < vi.channels; j++) {
				if (info.chmuxlist[j] == i) {
					if (nonzero[j] != 0) {
						zerobundle[ch_in_bundle] = 1;
					} else {
						zerobundle[ch_in_bundle] = 0;
					}
					pcmbundle[ch_in_bundle++] = vb.pcm[j];
				}
			}

			look.residue_func[i].inverse(vb, look.residue_look[i], pcmbundle,
					zerobundle, ch_in_bundle);
		}

		for (int i = info.coupling_steps - 1; i >= 0; i--) {
			float[] pcmM = vb.pcm[info.coupling_mag[i]];
			float[] pcmA = vb.pcm[info.coupling_ang[i]];

			for (int j = 0; j < n / 2; j++) {
				float mag = pcmM[j];
				float ang = pcmA[j];

				if (mag > 0) {
					if (ang > 0) {
						pcmM[j] = mag;
						pcmA[j] = mag - ang;
					} else {
						pcmA[j] = mag;
						pcmM[j] = mag + ang;
					}
				} else {
					if (ang > 0) {
						pcmM[j] = mag;
						pcmA[j] = mag + ang;
					} else {
						pcmA[j] = mag;
						pcmM[j] = mag - ang;
					}
				}
			}
		}

		// /* compute and apply spectral envelope */

		for (int i = 0; i < vi.channels; i++) {
			float[] pcm = vb.pcm[i];
			int submap = info.chmuxlist[i];
			look.floor_func[submap].inverse2(vb, look.floor_look[submap],
					floormemo[i], pcm);
		}

		// transform the PCM data; takes PCM vector, vb; modifies PCM vector
		// only MDCT right now....

		for (int i = 0; i < vi.channels; i++) {
			float[] pcm = vb.pcm[i];
			// _analysis_output("out",seq+i,pcm,n/2,0,0);
			((Mdct) vd.transform[vb.W][0]).backward(pcm, pcm);
		}

		// now apply the decoded pre-window time information
		// NOT IMPLEMENTED

		// window the data
		for (int i = 0; i < vi.channels; i++) {
			float[] pcm = vb.pcm[i];
			if (nonzero[i] != 0) {
				for (int j = 0; j < n; j++) {
					pcm[j] *= window[j];
				}
			} else {
				for (int j = 0; j < n; j++) {
					pcm[j] = 0.f;
				}
			}
		}

		// now apply the decoded post-window time information
		// NOT IMPLEMENTED
		// all done!
		return (0);
	}

	class InfoMapping0 {
		int submaps; // <= 16
		int[] chmuxlist = new int[256]; // up to 256 channels in a Vorbis stream

		int[] timesubmap = new int[16]; // [mux]
		int[] floorsubmap = new int[16]; // [mux] submap to floors
		int[] residuesubmap = new int[16];// [mux] submap to residue
		int[] psysubmap = new int[16]; // [mux]; encode only

		int coupling_steps;
		int[] coupling_mag = new int[256];
		int[] coupling_ang = new int[256];

		void free() {
			chmuxlist = null;
			timesubmap = null;
			floorsubmap = null;
			residuesubmap = null;
			psysubmap = null;

			coupling_mag = null;
			coupling_ang = null;
		}
	}

	class LookMapping0 {
		InfoMode mode;
		InfoMapping0 map;
		Object[] time_look;
		Object[] floor_look;
		Object[] floor_state;
		Object[] residue_look;
		PsyLook[] psy_look;

		FuncTime[] time_func;
		FuncFloor[] floor_func;
		FuncResidue[] residue_func;

		int ch;
		float[][] decay;
		int lastframe; // if a different mode is called, we need to
		// invalidate decay and floor state
	}

}
