package de.lighti.model;
import java.util.Vector;
import de.lighti.model.state.StateUtils.SP_Flags;
import de.lighti.model.state.StateUtils.SP_Type;
import de.lighti.model.state.StateUtils.SendProp;
import de.lighti.util.BitInputBuffer;
/**
* This class represents a property of an Entity.
*
* The code is based on the c++ parser library https://github.com/dschleck/edith
*
* @author Tobias Mahlmann
*
* @param <T>
*/
public class Property<T> {
enum FloatType {
FT_None, FT_LowPrecision, FT_Integral,
}
private static int getArrayLengthBits( SendProp prop ) {
int n = prop.getNumberOfElements();
int bits = 0;
while (n > 0) {
++bits;
n >>>= 1;
}
return bits;
};
private static Vector<Property> readArray( BitInputBuffer stream, SendProp prop ) {
if (prop.getArrayProp() == null) {
throw new IllegalArgumentException( "Array prop has no inner prop." );
}
final int count = stream.readBitsAsInt( getArrayLengthBits( prop ) );
final Vector<Property> elements = new Vector();
for (int i = 0; i < count; ++i) {
elements.add( readProperty( stream, prop.getArrayProp() ) );
}
return elements;
}
private static Float readFloat( BitInputBuffer stream, SendProp prop ) {
final int flags = prop.getFlags();
if ((flags & SP_Flags.SP_Coord.getId()) > 0) {
return readFloatCoord( stream );
}
else if ((flags & SP_Flags.SP_CoordMp.getId()) > 0) {
return readFloatCoordMp( stream, FloatType.FT_None );
}
else if ((flags & SP_Flags.SP_CoordMpLowPrecision.getId()) > 0) {
return readFloatCoordMp( stream, FloatType.FT_LowPrecision );
}
else if ((flags & SP_Flags.SP_CoordMpIntegral.getId()) > 0) {
return readFloatCoordMp( stream, FloatType.FT_Integral );
}
else if ((flags & SP_Flags.SP_NoScale.getId()) > 0) {
return readFloatNoScale( stream );
}
else if ((flags & SP_Flags.SP_Normal.getId()) > 0) {
return readFloatNormal( stream );
}
else if ((flags & SP_Flags.SP_CellCoord.getId()) > 0) {
return readFloatCellCoord( stream, FloatType.FT_None, prop.getNumBits() );
}
else if ((flags & SP_Flags.SP_CellCoordLowPrecision.getId()) > 0) {
return readFloatCellCoord( stream, FloatType.FT_LowPrecision, prop.getNumBits() );
}
else if ((flags & SP_Flags.SP_CellCoordIntegral.getId()) > 0) {
return readFloatCellCoord( stream, FloatType.FT_Integral, prop.getNumBits() );
}
else {
final float value = stream.readBitsAsInt( prop.getNumBits() );
float a = value;
float b = (1 << prop.getNumBits()) - 1;
a /= b;
float range = prop.getHighValue() - prop.getLowValue();
b = range;
a *= b;
range = prop.getLowValue();
b = range;
a += b;
return a;
}
}
private static float readFloatCellCoord( BitInputBuffer stream, FloatType type, int bits ) {
final int value = stream.readBitsAsInt( bits );
if (type == FloatType.FT_None || type == FloatType.FT_LowPrecision) {
final boolean lp = type == FloatType.FT_LowPrecision;
int second;
if (!lp) {
second = stream.readBitsAsInt( 5 );
}
else {
second = stream.readBitsAsInt( 3 );
}
float a;
if (!lp) {
a = 0x3FA00000;
}
else {
a = 0x3FC00000;
}
double b = second;
b *= a;
a = 0f;
a = value;
b += a;
a = (float) b;
return a;
}
else if (type == FloatType.FT_Integral) {
float f = value;
if (value >>> 31 > 0) {
f += 4.2949673e9f;
}
return f;
}
else {
throw new IllegalStateException( "unknown float type" );
}
}
private static float readFloatCoord( BitInputBuffer stream ) {
int first = stream.readBitsAsInt( 1 );
int second = stream.readBitsAsInt( 1 );
float a = 0;
float b = 0;
if (first > 0 || second > 0) {
final int third = stream.readBitsAsInt( 1 );
if (first > 0) {
first = stream.readBitsAsInt( 0x0E ) + 1;
}
if (second > 0) {
second = stream.readBitsAsInt( 5 );
}
b = first;
a = second;
final float special = 0x3D000000;
a *= special;
double ad = a;
final double bd = b;
ad *= bd;
a = (float) ad;
if (third > 0) {
final int mask = 0x80000000;
a = (int) a ^ mask;
}
}
return a;
}
private static float readFloatCoordMp( BitInputBuffer stream, FloatType type ) {
int value;
if (type == FloatType.FT_LowPrecision || type == FloatType.FT_None) {
throw new UnsupportedOperationException();
}
else if (type == FloatType.FT_Integral) {
int a = stream.readBitsAsInt( 1 );
final int b = stream.readBitsAsInt( 1 );
a = a + 2 * b;
if (b == 0) {
return 0;
}
else {
if (a > 0) {
value = stream.readBitsAsInt( 12 );
}
else {
value = stream.readBitsAsInt( 15 );
}
if ((value & 1) > 0) {
value = -((value >>> 1) + 1);
}
else {
value = (value >>> 1) + 1;
}
}
}
else {
throw new IllegalArgumentException( "Unknown coord type" );
}
return value;
}
private static float readFloatNormal( BitInputBuffer stream ) {
final boolean first = stream.readBit();
final int second = stream.readBitsAsInt( 11 );
float f = second;
if (second >>> 31 > 0) {
f += 4.2949673e9f;
}
f *= 4.885197850512946e-4f;
return (int) f ^ 0x80000000;
}
private static float readFloatNoScale( BitInputBuffer stream ) {
return stream.readBitsAsFloat( 32 );
}
private static Integer readInteger( BitInputBuffer stream, SendProp prop ) {
if ((prop.getFlags() & SP_Flags.SP_EncodedAgainstTickcount.getId()) > 0) {
if ((prop.getFlags() & SP_Flags.SP_Unsigned.getId()) > 0) {
return stream.readVar35();
}
else {
final int value = stream.readVar35();
return -(value & 1) ^ value >> 1;
}
}
else {
int value = stream.readBitsAsInt( prop.getNumBits() );
final int signer = 0x80000000 >> 32 - prop.getNumBits() & (prop.getFlags() & SP_Flags.SP_Unsigned.getId()) - 1;
value = value ^ signer;
return value - signer;
}
}
private static long readLong( BitInputBuffer stream, SendProp prop ) {
if ((SP_Flags.SP_EncodedAgainstTickcount.getId() & prop.getFlags()) > 0) {
if ((prop.getFlags() & SP_Flags.SP_Unsigned.getId()) > 0) {
return stream.readVar35();
}
else {
final long value = stream.readVar35();
return -(value & 1) ^ value >> 1;
}
}
else {
boolean negate = false;
int second_bits = prop.getNumBits() - 32;
if ((SP_Flags.SP_Unsigned.getId() & prop.getFlags()) == 0) {
--second_bits;
if (stream.readBit()) {
negate = true;
}
}
if (prop.getNumBits() < second_bits) {
throw new IllegalStateException( "Invalid number of bits" );
}
final long a = stream.readBitsAsInt( 32 );
final long b = stream.readBitsAsInt( second_bits );
long value = a << 32 | b;
if (negate) {
value *= -1;
}
return value;
}
}
public static Property<?> readProperty( BitInputBuffer stream, SendProp prop ) {
final String name = prop.getInTable().getName() + "." + prop.getName();
final Property<?> out;
switch (prop.getType()) {
case SP_Array:
final Vector<Property> elements = readArray( stream, prop );
out = new Property<Vector<Property>>( name, SP_Type.SP_Array, elements );
break;
case SP_Float:
out = new Property<Float>( name, SP_Type.SP_Float, readFloat( stream, prop ) );
break;
case SP_Int:
out = new Property<Integer>( name, SP_Type.SP_Int, readInteger( stream, prop ) );
break;
case SP_Int64:
out = new Property<Long>( name, SP_Type.SP_Int64, readLong( stream, prop ) );
break;
case SP_String:
final String stringValue = readString( MAX_STRING_LENGTH, stream, prop );
out = new Property<String>( name, SP_Type.SP_String, stringValue );
break;
case SP_Vector:
final float[] vectorValue = readVector( stream, prop );
out = new Property<float[]>( name, SP_Type.SP_Vector, vectorValue );
break;
case SP_VectorXY:
final float[] xyValue = readVectorXY( stream, prop );
out = new Property<float[]>( name, SP_Type.SP_VectorXY, xyValue );
break;
default:
throw new IllegalArgumentException( "Unsupported send prop type " + prop.getType() );
}
return out;
}
private static String readString( int max_length, BitInputBuffer stream, SendProp prop ) {
final int length = stream.readBitsAsInt( 9 );
if (length > max_length) {
throw new IllegalStateException( "String too long " + length + " > " + max_length );
}
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append( (char) stream.readBitsAsInt( 8 ) );
}
return builder.toString();
}
private static float[] readVector( BitInputBuffer stream, SendProp prop ) {
final float vector[] = new float[3];
vector[0] = readFloat( stream, prop );
vector[1] = readFloat( stream, prop );
if ((prop.getFlags() & SP_Flags.SP_Normal.getId()) > 0) {
final boolean first = stream.readBit();
float a = vector[0] * vector[0];
float b = vector[1] * vector[1];
a += b;
b = 0x3D000000;
if (a <= b) {
a = 0f;
}
else {
b -= a;
a = (float) Math.sqrt( b );
}
if (first) {
final float special = 0x3D000000;
a *= special;
}
vector[2] = a;
}
else {
vector[2] = readFloat( stream, prop );
}
return vector;
}
private static float[] readVectorXY( BitInputBuffer stream, SendProp prop ) {
final float[] vector = new float[2];
vector[0] = readFloat( stream, prop );
vector[1] = readFloat( stream, prop );
return vector;
}
private static final int MAX_STRING_LENGTH = 0x200;
private final String name;
private final SP_Type type;
private final T value;
public Property( String name, SP_Type type, T v ) {
super();
this.name = name;
this.type = type;
this.value = v;
}
public String getName() {
return name;
}
public T getValue() {
return value;
}
@Override
public String toString() {
return "Property [name=" + name + ", type=" + type + ", value=" + value + "]";
}
}