/*******************************************************************************
 * Copyright (c) 2004, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Devin Steffler (IBM Corporation) - initial API and implementation
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.c;

import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.c.ICASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.c.ICArrayType;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.parser.ISerializableType;
import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer;
import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer;
import org.eclipse.cdt.internal.core.dom.parser.Value;
import org.eclipse.core.runtime.CoreException;

public class CArrayType implements ICArrayType, ITypeContainer, ISerializableType {
	IType type;
    private IASTExpression sizeExpression;
    private IValue value= Value.NOT_INITIALIZED;
    private boolean isConst;
    private boolean isVolatile;
    private boolean isRestrict;
    private boolean isStatic;
	private boolean isVariableSized;
	
	public CArrayType(IType type) {
		this.type = type;
	}

	public CArrayType(IType type, boolean isConst, boolean isVolatile, boolean isRestrict, IValue size) {
		this.type= type;
		this.isConst= isConst;
		this.isVolatile= isVolatile;
		this.isRestrict= isRestrict;
		this.value= size;
	}
	
	public void setIsStatic(boolean val) {
		isStatic= val;
	}
	public void setIsVariableLength(boolean val) {
		isVariableSized= val;
	}

    @Override
	public boolean isSameType(IType obj) {
        if (obj == this)
            return true;
        if (obj instanceof ITypedef)
            return obj.isSameType(this);
        if (obj instanceof ICArrayType) {
        	ICArrayType at = (ICArrayType) obj;
        	if (isConst() != at.isConst()) return false;
			if (isRestrict() != at.isRestrict()) return false;
			if (isStatic() != at.isStatic()) return false;
			if (isVolatile() != at.isVolatile()) return false;
			if (isVariableLength() != at.isVariableLength()) return false;

			return at.getType().isSameType(type) && hasSameSize(at);
        }
    	return false;
    }
    
	private boolean hasSameSize(IArrayType rhs) {
		IValue s1 = getSize();
		IValue s2 = rhs.getSize();
		if (s1 == s2)
			return true;
		if (s1 == null || s2 == null)
			return false;
		return CharArrayUtils.equals(s1.getSignature(), s2.getSignature());
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.dom.ast.IArrayType#getType()
	 */
	@Override
	public IType getType() {
		return type;
	}
	
	@Override
	public void setType(IType t) {
	    this.type = t;
	}
	
	public void setModifier(ICASTArrayModifier mod) {
		isConst= mod.isConst();
		isVolatile= mod.isVolatile();
		isRestrict= mod.isRestrict();
		isStatic= mod.isStatic();
		isVariableSized= mod.isVariableSized();
		sizeExpression= mod.getConstantExpression();
	}

	@Override
	public boolean isConst() {
		return isConst;
	}

	@Override
	public boolean isRestrict() {
		return isRestrict;
	}

	@Override
	public boolean isVolatile() {
		return isVolatile;
	}

	@Override
	public boolean isStatic() {
		return isStatic;
	}

	@Override
	public boolean isVariableLength() {
		return isVariableSized;
	}

    @Override
	public IValue getSize() {
    	if (value != Value.NOT_INITIALIZED)
    		return value;
    	
    	if (sizeExpression == null)
    		return value= null;

    	return value= Value.create(sizeExpression, Value.MAX_RECURSION_DEPTH);
    }

    @Override
	public Object clone() {
        IType t = null;
   		try {
            t = (IType) super.clone();
        } catch (CloneNotSupportedException e) {
            // Not going to happen
        }
        return t;
    }

	@Override
	public String toString() {
		return ASTTypeUtil.getType(this);
	}

	@Override
	public void marshal(ITypeMarshalBuffer buffer) throws CoreException {
		int firstByte= ITypeMarshalBuffer.ARRAY_TYPE;
		int flags= 0;
		long nval= -1;
		IValue val= null;

		if (isConst()) flags |= 0x01;
		if (isVolatile()) flags |= 0x02;
		if (isRestrict()) flags |= 0x04;
		if (isStatic()) flags |= 0x08;
		if (isVariableLength()) flags |= 0x10;
		if (flags != 0) {
			firstByte |= ITypeMarshalBuffer.FLAG1;
		}

		val= getSize();
		if (val != null) {
			firstByte |= ITypeMarshalBuffer.FLAG2;
			Long num= val.numericalValue();
			if (num != null) {
				nval= num;
				if (nval >= 0) {
					firstByte |= ITypeMarshalBuffer.FLAG3;
				} 
			}
		}
		buffer.putByte((byte) firstByte);
		if (flags != 0) {
			buffer.putByte((byte) flags);
		}
		if (nval >= 0) {
			buffer.putLong(nval);
		} else if (val != null) {
			buffer.marshalValue(val);
		}
		buffer.marshalType(getType());
	}

	public static IType unmarshal(int firstByte, ITypeMarshalBuffer buffer) throws CoreException {
		int flags= 0;
		IValue value= null;
		if ((firstByte & ITypeMarshalBuffer.FLAG1) != 0) {
			flags= buffer.getByte();
		}
		if ((firstByte & ITypeMarshalBuffer.FLAG3) != 0) {
			value = Value.create(buffer.getLong());
		} else if ((firstByte & ITypeMarshalBuffer.FLAG2) != 0) {
			value = buffer.unmarshalValue();
		}
		IType nested= buffer.unmarshalType();		
		CArrayType result= new CArrayType(nested, (flags & 0x01) != 0, (flags & 0x02) != 0, (flags & 0x04) != 0, value);
		result.setIsStatic((flags & 0x08) != 0);
		result.setIsVariableLength((flags & 0x10) != 0);
		return result;
	}
	
	@Override
	@Deprecated
    public IASTExpression getArraySizeExpression() {
        if (sizeExpression != null)
            return sizeExpression;
        return null;
    }
}
