/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2005 Dave Brosius
 * Copyright (C) 2005 University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package edu.umd.cs.findbugs;

import java.util.ArrayList;
import java.util.List;

import edu.umd.cs.findbugs.visitclass.DismantleBytecode;


public class SwitchHandler
{
	private final List<SwitchDetails> switchOffsetStack;

	public SwitchHandler() {
		switchOffsetStack = new ArrayList<SwitchDetails>();
	}

	public void enterSwitch( DismantleBytecode dbc ) {

		SwitchDetails details = new SwitchDetails( dbc.getPC(), dbc.getSwitchOffsets(), dbc.getDefaultSwitchOffset());

		int size = switchOffsetStack.size();
		while (--size >= 0) {
			SwitchDetails existingDetail = switchOffsetStack.get(size);
			if (details.switchPC > (existingDetail.switchPC + existingDetail.swOffsets[existingDetail.swOffsets.length-1])) 
				switchOffsetStack.remove(size);
		}
		switchOffsetStack.add(details);
	}

	public boolean isOnSwitchOffset( DismantleBytecode dbc ) {
		int pc = dbc.getPC();
		if (pc == getDefaultOffset())
			return false;

		return (pc == getNextSwitchOffset(dbc));		
	}

	public int getNextSwitchOffset( DismantleBytecode dbc ) {
		int size = switchOffsetStack.size();
		while (size > 0) {
			SwitchDetails details = switchOffsetStack.get(size-1);

			int nextSwitchOffset = details.getNextSwitchOffset(dbc.getPC());
			if (nextSwitchOffset >= 0)
				return nextSwitchOffset;

			if (dbc.getPC() <= details.getDefaultOffset())
				return -1;
			switchOffsetStack.remove(size-1);
			size--;
		}

		return -1;
	}

	public int getDefaultOffset() {
		int size = switchOffsetStack.size();
		if (size == 0)
			return -1;

		SwitchDetails details = switchOffsetStack.get(size-1);
		return details.getDefaultOffset();
	}

	public static class SwitchDetails
	{
		final int   switchPC;
		final int[] swOffsets;
		final int	  defaultOffset;
		int   nextOffset;

		public SwitchDetails(int pc, int[] offsets, int defOffset) {
			switchPC = pc;
			int uniqueOffsets = 0;
			int lastValue = -1;
			for (int offset : offsets) {
				if (offset != lastValue) {
					uniqueOffsets++;
					lastValue = offset;
				}
			}

			swOffsets = new int[uniqueOffsets];
			int insertPos = 0;
			lastValue = -1;
			for (int offset1 : offsets) {
				if (offset1 != lastValue) {
					swOffsets[insertPos++] = offset1;
					lastValue = offset1;
				}
			}
			defaultOffset = defOffset;
			nextOffset = 0;
		}	

		public int getNextSwitchOffset(int currentPC) {
			while ((nextOffset < swOffsets.length) && (currentPC > (switchPC + swOffsets[nextOffset])))
				nextOffset++;

			if (nextOffset >= swOffsets.length)
				return -1;

			return switchPC + swOffsets[nextOffset];
		}

		public int getDefaultOffset() {
			return switchPC + defaultOffset;
		}
	}
}
