#include "Type.h"

Namespace NAMES;

Type* VOID_TYPE;
Type* BOOLEAN_TYPE;
Type* BYTE_TYPE;
Type* CHAR_TYPE;
Type* INT_TYPE;
Type* LONG_TYPE;
Type* FLOAT_TYPE;
Type* DOUBLE_TYPE;
Type* STRING_TYPE;
Type* CHAR_SEQUENCE_TYPE;
Type* TEXT_UTILS_TYPE;
Type* REMOTE_EXCEPTION_TYPE;
Type* RUNTIME_EXCEPTION_TYPE;
Type* IBINDER_TYPE;
Type* IINTERFACE_TYPE;
Type* BINDER_NATIVE_TYPE;
Type* BINDER_PROXY_TYPE;
Type* PARCEL_TYPE;
Type* PARCELABLE_INTERFACE_TYPE;
Type* MAP_TYPE;
Type* LIST_TYPE;
Type* CLASSLOADER_TYPE;

Expression* NULL_VALUE;
Expression* THIS_VALUE;
Expression* SUPER_VALUE;
Expression* TRUE_VALUE;
Expression* FALSE_VALUE;

void
register_base_types()
{
    VOID_TYPE = new BasicType("void", "XXX", "XXX", "XXX", "XXX", "XXX");
    NAMES.Add(VOID_TYPE);

    BOOLEAN_TYPE = new BooleanType();
    NAMES.Add(BOOLEAN_TYPE);

    BYTE_TYPE = new BasicType("byte", "writeByte", "readByte",
                "writeByteArray", "createByteArray", "readByteArray");
    NAMES.Add(BYTE_TYPE);

    CHAR_TYPE = new CharType();
    NAMES.Add(CHAR_TYPE);

    INT_TYPE = new BasicType("int", "writeInt", "readInt",
                "writeIntArray", "createIntArray", "readIntArray");
    NAMES.Add(INT_TYPE);

    LONG_TYPE = new BasicType("long", "writeLong", "readLong",
                "writeLongArray", "createLongArray", "readLongArray");
    NAMES.Add(LONG_TYPE);

    FLOAT_TYPE = new BasicType("float", "writeFloat", "readFloat",
                "writeFloatArray", "createFloatArray", "readFloatArray");
    NAMES.Add(FLOAT_TYPE);

    DOUBLE_TYPE = new BasicType("double", "writeDouble", "readDouble",
                "writeDoubleArray", "createDoubleArray", "readDoubleArray");
    NAMES.Add(DOUBLE_TYPE);

    STRING_TYPE = new StringType();
    NAMES.Add(STRING_TYPE);

    CHAR_SEQUENCE_TYPE = new CharSequenceType();
    NAMES.Add(CHAR_SEQUENCE_TYPE);

    MAP_TYPE = new MapType();
    NAMES.Add(MAP_TYPE);

    LIST_TYPE = new ListType();
    NAMES.Add(LIST_TYPE);

    TEXT_UTILS_TYPE = new Type("android.text", "TextUtils",
                                    Type::BUILT_IN, false, false);
    NAMES.Add(TEXT_UTILS_TYPE);

    REMOTE_EXCEPTION_TYPE = new RemoteExceptionType();
    NAMES.Add(REMOTE_EXCEPTION_TYPE);

    RUNTIME_EXCEPTION_TYPE = new RuntimeExceptionType();
    NAMES.Add(RUNTIME_EXCEPTION_TYPE);

    IBINDER_TYPE = new IBinderType();
    NAMES.Add(IBINDER_TYPE);

    IINTERFACE_TYPE = new IInterfaceType();
    NAMES.Add(IINTERFACE_TYPE);

    BINDER_NATIVE_TYPE = new BinderType();
    NAMES.Add(BINDER_NATIVE_TYPE);

    BINDER_PROXY_TYPE = new BinderProxyType();
    NAMES.Add(BINDER_PROXY_TYPE);

    PARCEL_TYPE = new ParcelType();
    NAMES.Add(PARCEL_TYPE);

    PARCELABLE_INTERFACE_TYPE = new ParcelableInterfaceType();
    NAMES.Add(PARCELABLE_INTERFACE_TYPE);

    CLASSLOADER_TYPE = new ClassLoaderType();
    NAMES.Add(CLASSLOADER_TYPE);

    NULL_VALUE = new LiteralExpression("null");
    THIS_VALUE = new LiteralExpression("this");
    SUPER_VALUE = new LiteralExpression("super");
    TRUE_VALUE = new LiteralExpression("true");
    FALSE_VALUE = new LiteralExpression("false");

    NAMES.AddGenericType("java.util", "List", 1);
    NAMES.AddGenericType("java.util", "Map", 2);
}

static Type*
make_generic_type(const string& package, const string& name,
                    const vector<Type*>& args)
{
    if (package == "java.util" && name == "List") {
        return new GenericListType("java.util", "List", args);
    }
    return NULL;
    //return new GenericType(package, name, args);
}

// ================================================================

Type::Type(const string& name, int kind, bool canWriteToParcel, bool canBeOut)
    :m_package(),
     m_name(name),
     m_declFile(""),
     m_declLine(-1),
     m_kind(kind),
     m_canWriteToParcel(canWriteToParcel),
     m_canBeOut(canBeOut)
{
    m_qualifiedName = name;
}

Type::Type(const string& package, const string& name,
            int kind, bool canWriteToParcel, bool canBeOut,
            const string& declFile, int declLine)
    :m_package(package),
     m_name(name),
     m_declFile(declFile),
     m_declLine(declLine),
     m_kind(kind),
     m_canWriteToParcel(canWriteToParcel),
     m_canBeOut(canBeOut)
{
    if (package.length() > 0) {
        m_qualifiedName = package;
        m_qualifiedName += '.';
    }
    m_qualifiedName += name;
}

Type::~Type()
{
}

bool
Type::CanBeArray() const
{
    return false;
}

string
Type::ImportType() const
{
    return m_qualifiedName;
}

string
Type::CreatorName() const
{
    return "";
}

string
Type::InstantiableName() const
{
    return QualifiedName();
}


void
Type::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%sn",
            __FILE__, __LINE__, m_qualifiedName.c_str());
    addTo->Add(new LiteralExpression("/* WriteToParcel error "
                + m_qualifiedName + " */"));
}

void
Type::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
            __FILE__, __LINE__, m_qualifiedName.c_str());
    addTo->Add(new LiteralExpression("/* CreateFromParcel error "
                + m_qualifiedName + " */"));
}

void
Type::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
            __FILE__, __LINE__, m_qualifiedName.c_str());
    addTo->Add(new LiteralExpression("/* ReadFromParcel error "
                + m_qualifiedName + " */"));
}

void
Type::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
            __FILE__, __LINE__, m_qualifiedName.c_str());
    addTo->Add(new LiteralExpression("/* WriteArrayToParcel error "
                + m_qualifiedName + " */"));
}

void
Type::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
                            Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
            __FILE__, __LINE__, m_qualifiedName.c_str());
    addTo->Add(new LiteralExpression("/* CreateArrayFromParcel error "
                + m_qualifiedName + " */"));
}

void
Type::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
            __FILE__, __LINE__, m_qualifiedName.c_str());
    addTo->Add(new LiteralExpression("/* ReadArrayFromParcel error "
                + m_qualifiedName + " */"));
}

void
Type::SetQualifiedName(const string& qualified)
{
    m_qualifiedName = qualified;
}

Expression*
Type::BuildWriteToParcelFlags(int flags)
{
    if (flags == 0) {
        return new LiteralExpression("0");
    }
    if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0) {
        return new FieldVariable(PARCELABLE_INTERFACE_TYPE,
                "PARCELABLE_WRITE_RETURN_VALUE");
    }
    return new LiteralExpression("0");
}

// ================================================================

BasicType::BasicType(const string& name, const string& marshallMethod,
                     const string& unmarshallMethod,
                     const string& writeArray, const string& createArray,
                     const string& readArray)
    :Type(name, BUILT_IN, true, false),
     m_marshallMethod(marshallMethod),
     m_unmarshallMethod(unmarshallMethod),
     m_writeArrayMethod(writeArray),
     m_createArrayMethod(createArray),
     m_readArrayMethod(readArray)
{
}

void
BasicType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, m_marshallMethod, 1, v));
}

void
BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new Assignment(v, new MethodCall(parcel, m_unmarshallMethod)));
}

bool
BasicType::CanBeArray() const
{
    return true;
}

void
BasicType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, m_writeArrayMethod, 1, v));
}

void
BasicType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
                            Variable* parcel)
{
    addTo->Add(new Assignment(v, new MethodCall(parcel, m_createArrayMethod)));
}

void
BasicType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new MethodCall(parcel, m_readArrayMethod, 1, v));
}


// ================================================================

BooleanType::BooleanType()
    :Type("boolean", BUILT_IN, true, false)
{
}

void
BooleanType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeInt", 1, 
                new Ternary(v, new LiteralExpression("1"),
                    new LiteralExpression("0"))));
}

void
BooleanType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new Assignment(v, new Comparison(new LiteralExpression("0"),
                    "!=", new MethodCall(parcel, "readInt"))));
}

bool
BooleanType::CanBeArray() const
{
    return true;
}

void
BooleanType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeBooleanArray", 1, v));
}

void
BooleanType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
                            Variable* parcel)
{
    addTo->Add(new Assignment(v, new MethodCall(parcel, "createBooleanArray")));
}

void
BooleanType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new MethodCall(parcel, "readBooleanArray", 1, v));
}


// ================================================================

CharType::CharType()
    :Type("char", BUILT_IN, true, false)
{
}

void
CharType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeInt", 1, 
                    new Cast(INT_TYPE, v)));
}

void
CharType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new Assignment(v, new MethodCall(parcel, "readInt"), this));
}

bool
CharType::CanBeArray() const
{
    return true;
}

void
CharType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeCharArray", 1, v));
}

void
CharType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
                            Variable* parcel)
{
    addTo->Add(new Assignment(v, new MethodCall(parcel, "createCharArray")));
}

void
CharType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new MethodCall(parcel, "readCharArray", 1, v));
}

// ================================================================

StringType::StringType()
    :Type("java.lang", "String", BUILT_IN, true, false)
{
}

string
StringType::CreatorName() const
{
    return "android.os.Parcel.STRING_CREATOR";
}

void
StringType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeString", 1, v));
}

void
StringType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new Assignment(v, new MethodCall(parcel, "readString")));
}

bool
StringType::CanBeArray() const
{
    return true;
}

void
StringType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeStringArray", 1, v));
}

void
StringType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
                            Variable* parcel)
{
    addTo->Add(new Assignment(v, new MethodCall(parcel, "createStringArray")));
}

void
StringType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new MethodCall(parcel, "readStringArray", 1, v));
}

// ================================================================

CharSequenceType::CharSequenceType()
    :Type("java.lang", "CharSequence", BUILT_IN, true, false)
{
}

string
CharSequenceType::CreatorName() const
{
    return "android.os.Parcel.STRING_CREATOR";
}

void
CharSequenceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    // if (v != null) {
    //     parcel.writeInt(1);
    //     v.writeToParcel(parcel);
    // } else {
    //     parcel.writeInt(0);
    // }
    IfStatement* elsepart = new IfStatement();
    elsepart->statements->Add(new MethodCall(parcel, "writeInt", 1,
                                new LiteralExpression("0")));
    IfStatement* ifpart = new IfStatement;
    ifpart->expression = new Comparison(v, "!=", NULL_VALUE);
    ifpart->elseif = elsepart;
    ifpart->statements->Add(new MethodCall(parcel, "writeInt", 1,
                                new LiteralExpression("1")));
    ifpart->statements->Add(new MethodCall(TEXT_UTILS_TYPE, "writeToParcel",
                                3, v, parcel, BuildWriteToParcelFlags(flags)));

    addTo->Add(ifpart);
}

void
CharSequenceType::CreateFromParcel(StatementBlock* addTo, Variable* v,
                                Variable* parcel)
{
    // if (0 != parcel.readInt()) {
    //     v = TextUtils.createFromParcel(parcel)
    // } else {
    //     v = null;
    // }
    IfStatement* elsepart = new IfStatement();
    elsepart->statements->Add(new Assignment(v, NULL_VALUE));

    IfStatement* ifpart = new IfStatement();
    ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
                new MethodCall(parcel, "readInt"));
    ifpart->elseif = elsepart;
    ifpart->statements->Add(new Assignment(v,
                new MethodCall(TEXT_UTILS_TYPE,
                                    "CHAR_SEQUENCE_CREATOR.createFromParcel", 1, parcel)));

    addTo->Add(ifpart);
}


// ================================================================

RemoteExceptionType::RemoteExceptionType()
    :Type("android.os", "RemoteException", BUILT_IN, false, false)
{
}

void
RemoteExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

void
RemoteExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

// ================================================================

RuntimeExceptionType::RuntimeExceptionType()
    :Type("java.lang", "RuntimeException", BUILT_IN, false, false)
{
}

void
RuntimeExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

void
RuntimeExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}


// ================================================================

IBinderType::IBinderType()
    :Type("android.os", "IBinder", BUILT_IN, true, false)
{
}

void
IBinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeStrongBinder", 1, v));
}

void
IBinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new Assignment(v, new MethodCall(parcel, "readStrongBinder")));
}

void
IBinderType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeBinderArray", 1, v));
}

void
IBinderType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
                            Variable* parcel)
{
    addTo->Add(new Assignment(v, new MethodCall(parcel, "createBinderArray")));
}

void
IBinderType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    addTo->Add(new MethodCall(parcel, "readBinderArray", 1, v));
}


// ================================================================

IInterfaceType::IInterfaceType()
    :Type("android.os", "IInterface", BUILT_IN, false, false)
{
}

void
IInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

void
IInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}


// ================================================================

BinderType::BinderType()
    :Type("android.os", "Binder", BUILT_IN, false, false)
{
}

void
BinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

void
BinderType::CreateFromParcel(StatementBlock* addTo, Variable* v,
                                    Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}


// ================================================================

BinderProxyType::BinderProxyType()
    :Type("android.os", "BinderProxy", BUILT_IN, false, false)
{
}

void
BinderProxyType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

void
BinderProxyType::CreateFromParcel(StatementBlock* addTo, Variable* v,
                                    Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}


// ================================================================

ParcelType::ParcelType()
    :Type("android.os", "Parcel", BUILT_IN, false, false)
{
}

void
ParcelType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

void
ParcelType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

// ================================================================

ParcelableInterfaceType::ParcelableInterfaceType()
    :Type("android.os", "Parcelable", BUILT_IN, false, false)
{
}

void
ParcelableInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

void
ParcelableInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
}

// ================================================================

MapType::MapType()
    :Type("java.util", "Map", BUILT_IN, true, true)
{
}

void
MapType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeMap", 1, v));
}

void
MapType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
    addTo->Add(new VariableDeclaration(cl,
        new LiteralExpression("this.getClass().getClassLoader()"),
        CLASSLOADER_TYPE));
    addTo->Add(new Assignment(v, new MethodCall(parcel, "readHashMap", 1, cl)));
}

void
MapType::ReadFromParcel(StatementBlock* addTo, Variable* v,
                    Variable* parcel)
{
    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
    addTo->Add(new VariableDeclaration(cl, 
        new LiteralExpression("this.getClass().getClassLoader()"),
        CLASSLOADER_TYPE));
    addTo->Add(new MethodCall(parcel, "readMap", 2, v, cl));
}


// ================================================================

ListType::ListType()
    :Type("java.util", "List", BUILT_IN, true, true)
{
}

string
ListType::InstantiableName() const
{
    return "java.util.ArrayList";
}

void
ListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeList", 1, v));
}

void
ListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
    addTo->Add(new VariableDeclaration(cl, 
        new LiteralExpression("this.getClass().getClassLoader()"),
        CLASSLOADER_TYPE));
    addTo->Add(new Assignment(v, new MethodCall(parcel, "readArrayList", 1, cl)));
}

void
ListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
                    Variable* parcel)
{
    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
    addTo->Add(new VariableDeclaration(cl, 
        new LiteralExpression("this.getClass().getClassLoader()"),
        CLASSLOADER_TYPE));
    addTo->Add(new MethodCall(parcel, "readList", 2, v, cl));
}


// ================================================================

ParcelableType::ParcelableType(const string& package, const string& name,
                        bool builtIn, const string& declFile, int declLine)
    :Type(package, name, builtIn ? BUILT_IN : PARCELABLE, true, true,
            declFile, declLine)
{
}

string
ParcelableType::CreatorName() const
{
    return QualifiedName() + ".CREATOR";
}

void
ParcelableType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    // if (v != null) {
    //     parcel.writeInt(1);
    //     v.writeToParcel(parcel);
    // } else {
    //     parcel.writeInt(0);
    // }
    IfStatement* elsepart = new IfStatement();
    elsepart->statements->Add(new MethodCall(parcel, "writeInt", 1,
                                new LiteralExpression("0")));
    IfStatement* ifpart = new IfStatement;
    ifpart->expression = new Comparison(v, "!=", NULL_VALUE);
    ifpart->elseif = elsepart;
    ifpart->statements->Add(new MethodCall(parcel, "writeInt", 1,
                                new LiteralExpression("1")));
    ifpart->statements->Add(new MethodCall(v, "writeToParcel", 2,
                                parcel, BuildWriteToParcelFlags(flags)));

    addTo->Add(ifpart);
}

void
ParcelableType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    // if (0 != parcel.readInt()) {
    //     v = CLASS.CREATOR.createFromParcel(parcel)
    // } else {
    //     v = null;
    // }
    IfStatement* elsepart = new IfStatement();
    elsepart->statements->Add(new Assignment(v, NULL_VALUE));

    IfStatement* ifpart = new IfStatement();
    ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
                new MethodCall(parcel, "readInt"));
    ifpart->elseif = elsepart;
    ifpart->statements->Add(new Assignment(v,
                new MethodCall(v->type, "CREATOR.createFromParcel", 1, parcel)));

    addTo->Add(ifpart);
}

void
ParcelableType::ReadFromParcel(StatementBlock* addTo, Variable* v,
                    Variable* parcel)
{
    // TODO: really, we don't need to have this extra check, but we
    // don't have two separate marshalling code paths
    // if (0 != parcel.readInt()) {
    //     v.readFromParcel(parcel)
    // }
    IfStatement* ifpart = new IfStatement();
    ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
                new MethodCall(parcel, "readInt"));
    ifpart->statements->Add(new MethodCall(v, "readFromParcel", 1, parcel));
    addTo->Add(ifpart);
}

bool
ParcelableType::CanBeArray() const
{
    return true;
}

void
ParcelableType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    addTo->Add(new MethodCall(parcel, "writeTypedArray", 2, v,
                BuildWriteToParcelFlags(flags)));
}

void
ParcelableType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
                            Variable* parcel)
{
    string creator = v->type->QualifiedName() + ".CREATOR";
    addTo->Add(new Assignment(v, new MethodCall(parcel,
                "createTypedArray", 1, new LiteralExpression(creator))));
}

void
ParcelableType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    string creator = v->type->QualifiedName() + ".CREATOR";
    addTo->Add(new MethodCall(parcel, "readTypedArray", 2,
                    v, new LiteralExpression(creator)));
}


// ================================================================

InterfaceType::InterfaceType(const string& package, const string& name,
                        bool builtIn, bool oneway,
                        const string& declFile, int declLine)
    :Type(package, name, builtIn ? BUILT_IN : INTERFACE, true, false,
                        declFile, declLine)
    ,m_oneway(oneway)
{
}

bool
InterfaceType::OneWay() const
{
    return m_oneway;
}

void
InterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    // parcel.writeStrongBinder(v != null ? v.asBinder() : null);
    addTo->Add(new MethodCall(parcel, "writeStrongBinder", 1, 
                new Ternary(
                    new Comparison(v, "!=", NULL_VALUE),
                    new MethodCall(v, "asBinder"),
                    NULL_VALUE)));
}

void
InterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    // v = Interface.asInterface(parcel.readStrongBinder());
    string type = v->type->QualifiedName();
    type += ".Stub";
    addTo->Add(new Assignment(v,
                new MethodCall( NAMES.Find(type), "asInterface", 1,
                    new MethodCall(parcel, "readStrongBinder"))));
}


// ================================================================

GenericType::GenericType(const string& package, const string& name,
                         const vector<Type*>& args)
    :Type(package, name, BUILT_IN, true, true)
{
    m_args = args;

    m_importName = package + '.' + name;

    string gen = "<";
    int N = args.size();
    for (int i=0; i<N; i++) {
        Type* t = args[i];
        gen += t->QualifiedName();
        if (i != N-1) {
            gen += ',';
        }
    }
    gen += '>';
    m_genericArguments = gen;
    SetQualifiedName(m_importName + gen);
}

string
GenericType::GenericArguments() const
{
    return m_genericArguments;
}

string
GenericType::ImportType() const
{
    return m_importName;
}

void
GenericType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    fprintf(stderr, "implement GenericType::WriteToParcel\n");
}

void
GenericType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    fprintf(stderr, "implement GenericType::CreateFromParcel\n");
}

void
GenericType::ReadFromParcel(StatementBlock* addTo, Variable* v,
                            Variable* parcel)
{
    fprintf(stderr, "implement GenericType::ReadFromParcel\n");
}


// ================================================================

GenericListType::GenericListType(const string& package, const string& name,
                         const vector<Type*>& args)
    :GenericType(package, name, args),
     m_creator(args[0]->CreatorName())
{
}

string
GenericListType::CreatorName() const
{
    return "android.os.Parcel.arrayListCreator";
}

string
GenericListType::InstantiableName() const
{
    return "java.util.ArrayList" + GenericArguments();
}

void
GenericListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
{
    if (m_creator == STRING_TYPE->CreatorName()) {
        addTo->Add(new MethodCall(parcel, "writeStringList", 1, v));
    } else if (m_creator == IBINDER_TYPE->CreatorName()) {
        addTo->Add(new MethodCall(parcel, "writeBinderList", 1, v));
    } else {
        // parcel.writeTypedListXX(arg);
        addTo->Add(new MethodCall(parcel, "writeTypedList", 1, v));
    }
}

void
GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
{
    if (m_creator == STRING_TYPE->CreatorName()) {
        addTo->Add(new Assignment(v,
                   new MethodCall(parcel, "createStringArrayList", 0)));
    } else if (m_creator == IBINDER_TYPE->CreatorName()) {
        addTo->Add(new Assignment(v,
                   new MethodCall(parcel, "createBinderArrayList", 0)));
    } else {
        // v = _data.readTypedArrayList(XXX.creator);
        addTo->Add(new Assignment(v,
                   new MethodCall(parcel, "createTypedArrayList", 1,
                   new LiteralExpression(m_creator))));
    }
}

void
GenericListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
                            Variable* parcel)
{
    if (m_creator == STRING_TYPE->CreatorName()) {
        addTo->Add(new MethodCall(parcel, "readStringList", 1, v));
    } else if (m_creator == IBINDER_TYPE->CreatorName()) {
        addTo->Add(new MethodCall(parcel, "readBinderList", 1, v));
    } else {
        // v = _data.readTypedList(v, XXX.creator);
        addTo->Add(new MethodCall(parcel, "readTypedList", 2,
                       v,
                       new LiteralExpression(m_creator)));
    }
}

// ================================================================

ClassLoaderType::ClassLoaderType()
    :Type("java.lang", "ClassLoader", BUILT_IN, false, false)
{
}


// ================================================================

Namespace::Namespace()
{
}

Namespace::~Namespace()
{
    int N = m_types.size();
    for (int i=0; i<N; i++) {
        delete m_types[i];
    }
}

void
Namespace::Add(Type* type)
{
    Type* t = Find(type->QualifiedName());
    if (t == NULL) {
        m_types.push_back(type);
    }
}

void
Namespace::AddGenericType(const string& package, const string& name, int args)
{
    Generic g;
        g.package = package;
        g.name = name;
        g.qualified = package + '.' + name;
        g.args = args;
    m_generics.push_back(g);
}

Type*
Namespace::Find(const string& name) const
{
    int N = m_types.size();
    for (int i=0; i<N; i++) {
        if (m_types[i]->QualifiedName() == name) {
            return m_types[i];
        }
    }
    return NULL;
}

Type*
Namespace::Find(const char* package, const char* name) const
{
    string s;
    if (package != NULL) {
        s += package;
        s += '.';
    }
    s += name;
    return Find(s);
}

static string
normalize_generic(const string& s)
{
    string r;
    int N = s.size();
    for (int i=0; i<N; i++) {
        char c = s[i];
        if (!isspace(c)) {
            r += c;
        }
    }
    return r;
}

Type*
Namespace::Search(const string& name)
{
    // an exact match wins
    Type* result = Find(name);
    if (result != NULL) {
        return result;
    }

    // try the class names
    // our language doesn't allow you to not specify outer classes
    // when referencing an inner class.  that could be changed, and this
    // would be the place to do it, but I don't think the complexity in
    // scoping rules is worth it.
    int N = m_types.size();
    for (int i=0; i<N; i++) {
        if (m_types[i]->Name() == name) {
            return m_types[i];
        }
    }

    // we got to here and it's not a generic, give up
    if (name.find('<') == name.npos) {
        return NULL;
    }

    // remove any whitespace
    string normalized = normalize_generic(name);

    // find the part before the '<', find a generic for it
    ssize_t baseIndex = normalized.find('<');
    string base(normalized.c_str(), baseIndex);
    const Generic* g = search_generic(base);
    if (g == NULL) {
        return NULL;
    }

    // For each of the args, do a recursive search on it.  We don't allow
    // generics within generics like Java does, because we're really limiting
    // them to just built-in container classes, at least for now.  Our syntax
    // ensures this right now as well.
    vector<Type*> args;
    size_t start = baseIndex + 1;
    size_t end = start;
    while (normalized[start] != '\0') {
        end = normalized.find(',', start);
        if (end == normalized.npos) {
            end = normalized.find('>', start);
        }
        string s(normalized.c_str()+start, end-start);
        Type* t = this->Search(s);
        if (t == NULL) {
            // maybe we should print a warning here?
            return NULL;
        }
        args.push_back(t);
        start = end+1;
    }

    // construct a GenericType, add it to our name set so they always get
    // the same object, and return it.
    result = make_generic_type(g->package, g->name, args);
    if (result == NULL) {
        return NULL;
    }

    this->Add(result);
    return this->Find(result->QualifiedName());
}

const Namespace::Generic*
Namespace::search_generic(const string& name) const
{
    int N = m_generics.size();

    // first exact match
    for (int i=0; i<N; i++) {
        const Generic& g = m_generics[i];
        if (g.qualified == name) {
            return &g;
        }
    }

    // then name match
    for (int i=0; i<N; i++) {
        const Generic& g = m_generics[i];
        if (g.name == name) {
            return &g;
        }
    }

    return NULL;
}

void
Namespace::Dump() const
{
    int n = m_types.size();
    for (int i=0; i<n; i++) {
        Type* t = m_types[i];
        printf("type: package=%s name=%s qualifiedName=%s\n",
                t->Package().c_str(), t->Name().c_str(),
                t->QualifiedName().c_str());
    }
}
