/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package net.sourceforge.phpdt.internal.compiler.ast;

import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
import net.sourceforge.phpdt.internal.compiler.impl.Constant;
import net.sourceforge.phpdt.internal.compiler.impl.DoubleConstant;
import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;

public class LongLiteral extends NumberLiteral {
	long value;

	static final Constant FORMAT_ERROR = new DoubleConstant(1.0 / 0.0); // NaN;

	public LongLiteral(char[] token, int s, int e) {
		super(token, s, e);
	}

	public LongLiteral(char[] token, int s, int e, long value) {
		this(token, s, e);
		this.value = value;
	}

	public void computeConstant() {
		// the overflow (when radix=10) is tested using the fact that
		// the value should always grow during its computation

		int length = source.length - 1; // minus one because the last char is
										// 'l' or 'L'

		long computedValue;
		if (source[0] == '0') {
			if (length == 1) {
				constant = Constant.fromValue(0L);
				return;
			}
			final int shift, radix;
			int j;
			if ((source[1] == 'x') | (source[1] == 'X')) {
				shift = 4;
				j = 2;
				radix = 16;
			} else {
				shift = 3;
				j = 1;
				radix = 8;
			}
			int nbDigit = 0;
			while (source[j] == '0') {
				j++; // jump over redondant zero
				if (j == length) { // watch for 0000000000000L
					constant = Constant.fromValue(value = 0L);
					return;
				}
			}

			int digitValue;
			if ((digitValue = Character.digit(source[j++], radix)) < 0) {
				constant = FORMAT_ERROR;
				return;
			}
			if (digitValue >= 8)
				nbDigit = 4;
			else if (digitValue >= 4)
				nbDigit = 3;
			else if (digitValue >= 2)
				nbDigit = 2;
			else
				nbDigit = 1; // digitValue is not 0
			computedValue = digitValue;
			while (j < length) {
				if ((digitValue = Character.digit(source[j++], radix)) < 0) {
					constant = FORMAT_ERROR;
					return;
				}
				if ((nbDigit += shift) > 64)
					return /* constant stays null */;
				computedValue = (computedValue << shift) | digitValue;
			}
		}

		else { // -----------case radix=10-----------------
			long previous = computedValue = 0;
			for (int i = 0; i < length; i++) {
				int digitValue;
				if ((digitValue = Character.digit(source[i], 10)) < 0)
					return /* constant stays null */;
				previous = computedValue;
				computedValue = 10 * computedValue + digitValue;
				if (previous > computedValue)
					return /* constant stays null */;
			}
		}

		constant = Constant.fromValue(value = computedValue);
	}

	/**
	 * Code generation for long literal
	 * 
	 * @param currentScope
	 *            net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
	 * @param codeStream
	 *            net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
	 * @param valueRequired
	 *            boolean
	 */
	// public void generateCode(BlockScope currentScope, CodeStream codeStream,
	// boolean valueRequired) {
	// int pc = codeStream.position;
	// if (valueRequired)
	// if ((implicitConversion >> 4) == T_long)
	// codeStream.generateInlinedValue(value);
	// else
	// codeStream.generateConstant(constant, implicitConversion);
	// codeStream.recordPositionsFrom(pc, this.sourceStart);
	// }
	public TypeBinding literalType(BlockScope scope) {
		return LongBinding;
	}

	public final boolean mayRepresentMIN_VALUE() {
		// a special autorized int literral is 9223372036854775808L
		// which is ONE over the limit. This special case
		// only is used in combinaison with - to denote
		// the minimal value of int -9223372036854775808L

		return ((source.length == 20) && (source[0] == '9')
				&& (source[1] == '2') && (source[2] == '2')
				&& (source[3] == '3') && (source[4] == '3')
				&& (source[5] == '7') && (source[6] == '2')
				&& (source[7] == '0') && (source[8] == '3')
				&& (source[9] == '6') && (source[10] == '8')
				&& (source[11] == '5') && (source[12] == '4')
				&& (source[13] == '7') && (source[14] == '7')
				&& (source[15] == '5') && (source[16] == '8')
				&& (source[17] == '0') && (source[18] == '8'));
	}

	public TypeBinding resolveType(BlockScope scope) {
		// the format may be incorrect while the scanner could detect
		// such error only on painfull tests...easier and faster here

		TypeBinding tb = super.resolveType(scope);
		if (constant == FORMAT_ERROR) {
			constant = NotAConstant;
			scope.problemReporter().constantOutOfFormat(this);
			this.resolvedType = null;
			return null;
		}
		return tb;
	}

	public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope scope) {
		visitor.visit(this, scope);
		visitor.endVisit(this, scope);
	}
}
