public CachedProperty(PropertyDefinition property, string cacheFieldName = null)
        {
            Property = property;

            if (cacheFieldName != null)
            {
                CacheFieldName = cacheFieldName;
            }
            else
            {
                string name = property.Name;
                CacheFieldName = "_" + char.ToLower(name[0]) + name.Substring(1);
            }

            Access = RefAccessSpecifier.Private;
        }
        void WriteProperty(PropertyDefinition prop, int level)
        {
            EnsureWhiteSpace(WriteTo.CS);

            WriteTabs(level + 1, WriteTo.CS);
            Write("public ", WriteTo.CS);
            WriteTypeCS(prop.Type);
            Write(' ', WriteTo.CS);
            WriteLine(prop.Name, WriteTo.CS);
            WriteTabs(level + 1, WriteTo.CS);
            WriteLine('{', WriteTo.CS);

            Write(BulletParser.GetTypeGetterCSMarshal(prop, level), WriteTo.CS);

            if (prop.Setter != null)
            {
                WriteTabs(level + 2, WriteTo.CS);
                Write("set { ", WriteTo.CS);
                Write(prop.Parent.FullNameCS, WriteTo.CS);
                Write('_', WriteTo.CS);
                Write(prop.Setter.Name, WriteTo.CS);
                Write("(_native, ", WriteTo.CS);
                Write(BulletParser.GetTypeSetterCSMarshal(prop.Type), WriteTo.CS);
                WriteLine("); }", WriteTo.CS);
            }

            WriteTabs(level + 1, WriteTo.CS);
            WriteLine('}', WriteTo.CS);

            hasCSWhiteSpace = false;
        }
        void WriteProperty(PropertyDefinition prop, int level)
        {
            EnsureWhiteSpace(WriteTo.CS);

            WriteTabs(level + 1, WriteTo.CS);
            Write("public ", WriteTo.CS);
            WriteTypeCS(prop.Type);
            WriteLine($" {prop.Name}", WriteTo.CS);
            WriteTabs(level + 1, WriteTo.CS);
            WriteLine('{', WriteTo.CS);

            if (prop.Parent.CachedProperties.Keys.Contains(prop.Name))
            {
                var cachedProperty = prop.Parent.CachedProperties[prop.Name];
                WriteTabs(level + 2, WriteTo.CS);
                WriteLine($"get {{ return {cachedProperty.CacheFieldName}; }}", WriteTo.CS);

                if (prop.Setter != null)
                {
                    WriteTabs(level + 2, WriteTo.CS);
                    WriteLine("set", WriteTo.CS);
                    WriteTabs(level + 2, WriteTo.CS);
                    WriteLine("{", WriteTo.CS);
                    WriteTabs(level + 3, WriteTo.CS);
                    WriteLine(string.Format("{0}_{1}(_native, {2});",
                        prop.Parent.FullNameC,
                        prop.Setter.Name,
                        BulletParser.GetTypeSetterCSMarshal(prop.Type)), WriteTo.CS);
                    WriteTabs(level + 3, WriteTo.CS);
                    WriteLine($"{cachedProperty.CacheFieldName} = value;", WriteTo.CS);
                    WriteTabs(level + 2, WriteTo.CS);
                    WriteLine("}", WriteTo.CS);
                }
            }
            else
            {
                Write(BulletParser.GetTypeGetterCSMarshal(prop, level), WriteTo.CS);

                if (prop.Setter != null)
                {
                    WriteTabs(level + 2, WriteTo.CS);
                    WriteLine(string.Format("set {{ {0}_{1}(_native, {2}); }}",
                        prop.Parent.FullNameC,
                        prop.Setter.Name,
                        BulletParser.GetTypeSetterCSMarshal(prop.Type)), WriteTo.CS);
                }
            }

            WriteTabs(level + 1, WriteTo.CS);
            WriteLine('}', WriteTo.CS);

            hasCSWhiteSpace = false;
        }
        public static string GetTypeGetterCSMarshal(PropertyDefinition prop, int level)
        {
            StringBuilder output = new StringBuilder();
            TypeRefDefinition type = prop.Type;

            if (!type.IsBasic)
            {
                switch (type.ManagedNameCS)
                {
                    case "Matrix3x3":
                    case "Quaternion":
                    case "Transform":
                    case "Vector3":
                    case "Vector4":
                        output.AppendLine(GetTabs(level + 2) + "get");
                        output.AppendLine(GetTabs(level + 2) + "{");
                        output.AppendLine(GetTabs(level + 3) + GetTypeNameCS(type) + " value;");
                        output.AppendLine(GetTabs(level + 3) + prop.Parent.FullNameCS + '_' + prop.Getter.Name + "(_native, out value);");
                        output.AppendLine(GetTabs(level + 3) + "return value;");
                        output.AppendLine(GetTabs(level + 2) + '}');
                        return output.ToString();
                }
            }

            output.AppendLine(GetTabs(level + 2) + "get { return " +
                BulletParser.GetTypeMarshalConstructorStartCS(prop.Getter) +
                prop.Parent.FullNameCS + '_' + prop.Getter.Name + "(_native)" +
                BulletParser.GetTypeMarshalConstructorEndCS(prop.Getter) + "; }");
            return output.ToString();
        }
        public BulletParser(Dictionary<string, ClassDefinition> classDefinitions, Dictionary<string, HeaderDefinition> headerDefinitions)
        {
            this.classDefinitions = classDefinitions;
            ExternalHeaders = headerDefinitions;

            // Excluded classes
            string[] excludedClassArray = new string[] { "ActionInterface", "AlignedAllocator", "bChunk", "bCommon",
            "bFile", "Box", "BulletFile", "cl_MiniCL_Defs", "cl_platform", "ContactProcessing", "ConvexHull",
            "ConvexHullComputer", "DantzigLCP", "GenericPoolAllocator",
            "gim_array", "gim_bitset", "gim_box_collision", "gim_box_set", "gim_clip_polygon", "gim_contact",
            "gim_geom_types", "gim_hash_table", "gim_memory", "gim_radixsort", "gim_tri_collision", "GjkEpa2",
            "Gpu3DGridBroadphase", "Gpu3DGridBroadphaseSharedTypes", "GpuDefines", "GrahamScan2dConvexHull",
            "HashedSimplePairCache", "HashMap", "HeapManager", "IDebugDraw", "JacobianEntry", "List", "Material",
            "Matrix3x3", "MatrixX", "MiniCLTask", "MiniCLTaskScheduler", "MultiSapBroadphase", "PlatformDefinitions",
            "PolarDecomposition", "PolyhedralContactClipping", "PosixThreadSupport", "PpuAddressSpace", "QuadWord",
            "RaycastCallback", "SequentialThreadSupport", "SimpleDynamicsWorld", "SoftBodyData", "SoftBodyInternals",
            "SoftBodySolvers", "SoftRigidCollisionAlgorithm", "SoftSoftCollisionAlgorithm", "SolveProjectedGaussSeidel",
            "SolverBody", "SolverConstraint", "SpuCollisionObjectWrapper", "SpuCollisionShapes",
            "SpuCollisionTaskProcess", "SpuContactManifoldCollisionAlgorithm", "SpuContactResult",
            "SpuConvexPenetrationDepthSolver", "SpuDoubleBuffer", "SpuGatheringCollisionTask",
            "SpuMinkowskiPenetrationDepthSolver", "SpuSampleTask", "SpuSampleTaskProcess", "SpuSync", "StackAlloc",
            "SubSimplexConvexCast", "Transform", "TrbDynBody", "TrbStateVec", "vectormath_aos", "vmInclude",
            "HacdCircularList", "HacdGraph", "HacdICHull", "HacdManifoldMesh", "HacdVector",
            "Quaternion", "Vector3", "LemkeAlgorithm", "CharacterControllerInterface", "TypedObject" };

            // Managed method names
            methodNameMapping.Add("gimpact_vs_compoundshape", "GImpactVsCompoundShape");
            methodNameMapping.Add("gimpact_vs_concave", "GImpactVsConcave");
            methodNameMapping.Add("gimpact_vs_gimpact", "GImpactVsGImpact");
            methodNameMapping.Add("gimpact_vs_shape", "GImpactVsShape");
            methodNameMapping.Add("collideKDOP", "CollideKdop");
            methodNameMapping.Add("collideOCL", "CollideOcl");
            methodNameMapping.Add("collideTTpersistentStack", "CollideTTPersistentStack");
            methodNameMapping.Add("maxdepth", "MaxDepth");
            methodNameMapping.Add("updateRHS", "UpdateRhs");

            // Managed method parameter names
            parameterNameMapping.Add("bcheckexist", "checkExist");
            parameterNameMapping.Add("childShapeindex", "childShapeIndex");
            parameterNameMapping.Add("dt", "deltaTime");
            parameterNameMapping.Add("fromfaces", "fromFaces");
            parameterNameMapping.Add("i_dataBufferSize", "dataBufferSize");
            parameterNameMapping.Add("i_swapEndian", "swapEndian");
            parameterNameMapping.Add("i_alignedDataBuffer", "alignedDataBuffer");
            parameterNameMapping.Add("o_alignedDataBuffer", "alignedDataBuffer");
            parameterNameMapping.Add("drawflags", "drawFlags");
            parameterNameMapping.Add("idraw", "iDraw");
            parameterNameMapping.Add("indexstride", "indexStride");
            parameterNameMapping.Add("indicestype", "indicesType");
            parameterNameMapping.Add("limot", "limitMotor");
            parameterNameMapping.Add("maxiterations", "maxIterations");
            parameterNameMapping.Add("maxdepth", "maxDepth");
            parameterNameMapping.Add("mindepth", "minDepth");
            parameterNameMapping.Add("nodeindex", "nodeIndex");
            parameterNameMapping.Add("numfaces", "numFaces");
            parameterNameMapping.Add("numindices", "numIndices");
            parameterNameMapping.Add("numverts", "numVerts");
            parameterNameMapping.Add("paircache", "pairCache");
            parameterNameMapping.Add("rbA", "rigidBodyA");
            parameterNameMapping.Add("rbB", "rigidBodyB");
            parameterNameMapping.Add("rbAFrame", "rigidBodyAFrame");
            parameterNameMapping.Add("rbBFrame", "rigidBodyBFrame");
            parameterNameMapping.Add("use32bitIndices", "use32BitIndices");
            parameterNameMapping.Add("use4componentVertices", "use4ComponentVertices");
            parameterNameMapping.Add("vertexbase", "vertexBase");

            // Managed header names
            var headerNameMapping = new Dictionary<string, string>();
            headerNameMapping.Add("btActionInterface", "IAction");
            headerNameMapping.Add("btBox2dBox2dCollisionAlgorithm", "Box2DBox2DCollisionAlgorithm");
            headerNameMapping.Add("btBox2dShape", "Box2DShape");
            headerNameMapping.Add("btCompoundFromGimpact", "CompoundFromGImpact");
            headerNameMapping.Add("btConvex2dConvex2dAlgorithm", "Convex2DConvex2DAlgorithm");
            headerNameMapping.Add("btConvex2dShape", "Convex2DShape");
            headerNameMapping.Add("btMLCPSolver", "MlcpSolver");
            headerNameMapping.Add("btMLCPSolverInterface", "MlcpSolverInterface");
            headerNameMapping.Add("btNNCGConstraintSolver", "NncgConstraintSolver");
            headerNameMapping.Add("btSparseSDF", "SparseSdf");
            headerNameMapping.Add("hacdHACD", "Hacd");

            // Managed class names
            var classNameMapping = new Dictionary<string, string>();
            classNameMapping.Add("btAABB", "Aabb");
            classNameMapping.Add("bt32BitAxisSweep3", "AxisSweep3_32Bit");
            classNameMapping.Add("btActionInterface", "IAction");
            classNameMapping.Add("btBox2dBox2dCollisionAlgorithm", "Box2DBox2DCollisionAlgorithm");
            classNameMapping.Add("btBox2dShape", "Box2DShape");
            classNameMapping.Add("btConvex2dConvex2dAlgorithm", "Convex2DConvex2DAlgorithm");
            classNameMapping.Add("btConvex2dShape", "Convex2DShape");
            classNameMapping.Add("btMLCPSolver", "MlcpSolver");
            classNameMapping.Add("btMLCPSolverInterface", "MlcpSolverInterface");
            classNameMapping.Add("btMultibodyLink", "MultiBodyLink");
            classNameMapping.Add("btNNCGConstraintSolver", "NncgConstraintSolver");
            classNameMapping.Add("HACD", "Hacd");
            classNameMapping.Add("GIM_BVH_DATA", "GimBvhData");
            classNameMapping.Add("GIM_BVH_DATA_ARRAY", "GimBvhDataArray");
            classNameMapping.Add("GIM_BVH_TREE_NODE", "GimBvhTreeNode");
            classNameMapping.Add("GIM_BVH_TREE_NODE_ARRAY", "GimBvhTreeNodeArray");
            classNameMapping.Add("GIM_PAIR", "GimPair");
            classNameMapping.Add("GIM_TRIANGLE_CONTACT", "GimTriangleContact");
            classNameMapping.Add("BT_QUANTIZED_BVH_NODE", "GImpactQuantizedBvhNode");
            classNameMapping.Add("GIM_QUANTIZED_BVH_NODE_ARRAY", "GimGImpactQuantizedBvhNodeArray");
            classNameMapping.Add("btCPUVertexBufferDescriptor", "CpuVertexBufferDescriptor");
            classNameMapping.Add("btBU_Simplex1to4", "BuSimplex1To4");
            classNameMapping.Add("sStkCLN", "StkCln");
            classNameMapping.Add("sStkNN", "StkNN");
            classNameMapping.Add("sStkNP", "StkNP");
            classNameMapping.Add("sStkNPS", "StkNps");
            classNameMapping.Add("sRayCast", "SRayCast");
            classNameMapping.Add("RContact", "RigidContact");
            classNameMapping.Add("SContact", "SoftContact");

            // Classes that shouldn't be instantiated by users
            List<string> hidePublicConstructors = new List<string>() {
                "btActivatingCollisionAlgorithm", "btContactConstraint", "btConvexInternalShape",
                "btConvexInternalAabbCachingShape", "btPolyhedralConvexAabbCachingShape", "btTypedObject",
                "btDbvtProxy", "btSimpleBroadphaseProxy", "btDispatcherInfo", "btTriangleMeshShape",
                "btUsageBitfield", "btSoftBody::Anchor", "btSoftBody::Config", "btSoftBody::Cluster",
                "btSoftBody::Face", "btSoftBody::Tetra", "btSoftBody::Element", "btSoftBody::Feature",
                "btSoftBody::Link", "btSoftBody::Material", "btSoftBody::Node", "btSoftBody::Note",
                "btSoftBody::Pose", "btSoftBody::SolverState", "btSoftBody::Joint::Specs",
                "btSoftBody::AJoint", "btSoftBody::CJoint", "btSoftBody::LJoint", "btSparseSdf",
                "btCompoundShapeChild", "btMultibodyLink"
            };

            // Classes for which no internal constructor is needed
            List<string> hideInternalConstructor = new List<string>() {
                "btBox2dBox2dCollisionAlgorithm", "btBoxBoxCollisionAlgorithm",
                "btBoxBoxDetector", "btBroadphaseRayCallback", "btCollisionAlgorithmConstructionInfo", "btDefaultCollisionConstructionInfo",
                "btCompoundCompoundCollisionAlgorithm", "btContinuousConvexCollision", "btConvex2dConvex2dAlgorithm",
                "btConvexConcaveCollisionAlgorithm", "btConvexConvexAlgorithm",
                "btDefaultMotionState", "btRigidBody", "btDiscreteCollisionDetectorInterface::ClosestPointInput",
                "btEmptyAlgorithm", "btGjkConvexCast", "btGjkEpaPenetrationDepthSolver",
                "btMinkowskiPenetrationDepthSolver", "btPointCollector",
                "btMultiBodyDynamicsWorld",
                "btDefaultVehicleRaycaster", "btRaycastVehicle", "btDefaultSerializer",
                "btSoftBodyRigidBodyCollisionConfiguration", "btCPUVertexBufferDescriptor",
                "btSoftRigidDynamicsWorld",
                "btSphereBoxCollisionAlgorithm",
                "btSphereTriangleCollisionAlgorithm",
                "SpuGatheringCollisionDispatcher", "btConvexSeparatingDistanceUtil",
                "btVehicleRaycaster::btVehicleRaycasterResult", "btOverlapCallback",
                "btRaycastVehicle::btVehicleTuning",
                "btBox2dShape", "btBoxShape", "btCapsuleShapeX", "btCapsuleShapeZ",
                "btCylinderShapeX", "btCylinderShapeZ",
                "btConeShapeX", "btConeShapeZ", "btConvex2dShape", "btConvexHullShape",
                "btConvexPointCloudShape", "btEmptyShape", "btHeightfieldTerrainShape", "btMinkowskiSumShape",
                "btMultiSphereShape", "btMultimaterialTriangleMeshShape", "btScaledBvhTriangleMeshShape",
                "btSphereShape", "btStaticPlaneShape", "btUniformScalingShape",
                "btCollisionWorld::ConvexResultCallback", "btCollisionWorld::ClosestConvexResultCallback",
                "HACD", "btRigidBody::btRigidBodyConstructionInfo",
                "btSoftBody::ImplicitFn", "btTriangleBuffer", "btMaterialProperties",
                "btCollisionWorld::AllHitsRayResultCallback", "btCollisionWorld::ContactResultCallback",
                "btCollisionWorld::ClosestRayResultCallback", "btCollisionWorld::RayResultCallback",
                "btJointFeedback", "btTypedConstraint::btConstraintInfo1", "btTypedConstraint::btConstraintInfo2",
                "btConeTwistConstraint", "btFixedConstraint", "btGearConstraint",
                "btGeneric6DofSpringConstraint", "btHinge2Constraint",
                "btHingeAccumulatedAngleConstraint", "btPoint2PointConstraint",
                "btSliderConstraint", "btUniversalConstraint",
                "btMLCPSolver", "btMultiBodyConstraintSolver", "btNNCGConstraintSolver",
                "btPairCachingGhostObject", "btSortedOverlappingPairCache", "btNullPairCache",
                "btDbvtBroadphase", "btSimpleBroadphase",
                "btShapeHull", "btSoftBody::sRayCast", "btSoftBody::AJoint::Specs", "btSoftBody::LJoint::Specs",
                "btCompoundShape" // constructor needed for CompoundFromGImpact in C++/CLI, but not C#
            };

            // Classes that might be cleaned up by Bullet and not us (use preventDelete to indicate this)
            List<string> preventDelete = new List<string>() {
                "btAABB", "btCollisionAlgorithmCreateFunc",
                "btCollisionObject", "btCollisionObjectWrapper", "btCollisionShape",
                "btCollisionWorld::LocalConvexResult", "btCollisionWorld::LocalRayResult",
                "btConstraintSolver", "btContactSolverInfoData", "btDbvt",
                "btOverlappingPairCache", "btPoolAllocator",
                "btRotationalLimitMotor", "btTranslationalLimitMotor",
                "btRotationalLimitMotor2", "btTranslationalLimitMotor2",
                "btConstraintSetting", "btSimulationIslandManager",
                "btSolve2LinearConstraint", "btIndexedMesh", "btTriangleInfoMap",
                "btAngularLimit", "btContactSolverInfo",
                "btWheelInfo", "btManifoldPoint", "btCollisionWorld::LocalShapeInfo",
                "btSequentialImpulseConstraintSolver"};

            // Classes that have OnDisposing/OnDisposed events
            List<string> trackingDisposable = new List<string>() {
                "btCollisionObject", "btCollisionShape", "btCollisionWorld",
                "btDbvt", "btRaycastVehicle", "btTypedConstraint"};

            excludedClassNames = new Dictionary<string, string>();
            foreach (string c in excludedClassArray)
            {
                excludedClassNames.Add(c, c);
            }

            // Resolve references (match TypeRefDefinitions to ClassDefinitions)
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                // Resolve base class type
                if (c.BaseClass != null)
                {
                    ResolveTypeRef(c.BaseClass);
                    if (c.BaseClass.Target == null)
                    {
                        Console.WriteLine("Base class " + c.BaseClass.Name + " not found!");
                    }
                    else
                    {
                        var targetHeader = c.BaseClass.Target.Header;
                        if (c.Header != targetHeader && !c.Header.Includes.Contains(targetHeader))
                        {
                            c.Header.Includes.Add(targetHeader);
                        }
                    }
                }

                // Resolve typedef
                if (c.TypedefUnderlyingType != null)
                {
                    ResolveTypeRef(c.TypedefUnderlyingType);
                }

                // Resolve method return type and parameter types
                foreach (MethodDefinition method in c.Methods)
                {
                    ResolveTypeRef(method.ReturnType);
                    foreach (ParameterDefinition param in method.Parameters)
                    {
                        ResolveTypeRef(param.Type);
                    }
                }

                // Resolve field types
                foreach (FieldDefinition field in c.Fields)
                {
                    ResolveTypeRef(field.Type);
                }
            }

            // Exclude all overridden methods
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                int i;
                for (i = 0; i < c.Methods.Count; )
                {
                    var method = c.Methods[i];
                    // Check if the method already exists in base classes
                    var baseClass = c.BaseClass;
                    while (baseClass != null && baseClass.Target != null)
                    {
                        foreach (MethodDefinition m in baseClass.Target.Methods)
                        {
                            if (method.Equals(m))
                            {
                                c.Methods.Remove(method);
                                method = null;
                                break;
                            }
                        }
                        if (method == null)
                        {
                            break;
                        }
                        baseClass = baseClass.Target.BaseClass;
                    }
                    if (method != null)
                    {
                        i++;
                    }
                }
            }

            // Exclude constructors of abstract classes
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                if (!c.IsAbstract)
                {
                    continue;
                }

                int i;
                for (i = 0; i < c.Methods.Count; )
                {
                    var method = c.Methods[i];
                    if (method.IsConstructor)
                    {
                        c.Methods.Remove(method);
                        continue;
                    }
                    i++;
                }
            }

            // Exclude duplicate methods
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                for (int i = 0; i < c.Methods.Count; i++)
                {
                    for (int j = i + 1; j < c.Methods.Count; j++)
                    {
                        if (!c.Methods[i].Equals(c.Methods[j]))
                        {
                            continue;
                        }

                        var iType = c.Methods[i].ReturnType;
                        var jType = c.Methods[j].ReturnType;
                        bool iConst = iType.IsConst || (iType.Referenced != null && iType.Referenced.IsConst);
                        bool jConst = jType.IsConst || (jType.Referenced != null && jType.Referenced.IsConst);

                        // Prefer non-const return value
                        if (iConst)
                        {
                            if (!jConst)
                            {
                                c.Methods.RemoveAt(i);
                                i--;
                            }
                        }
                        else
                        {
                            if (jConst)
                            {
                                c.Methods.RemoveAt(j);
                                i--;
                            }
                        }
                        break;
                    }
                }
            }

            // Set managed method/parameter names
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                foreach (var method in c.Methods)
                {
                    method.ManagedName = GetManagedMethodName(method);

                    foreach (var param in method.Parameters)
                    {
                        param.ManagedName = GetManagedParameterName(param);
                    }
                }
            }

            // Turn fields into get/set methods
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                foreach (FieldDefinition field in c.Fields)
                {
                    ResolveTypeRef(field.Type);

                    string name = field.Name;
                    if (name.StartsWith("m_"))
                    {
                        name = name.Substring(2);
                    }
                    name = name.Substring(0, 1).ToUpper() + name.Substring(1); // capitalize

                    // one_two_three -> oneTwoThree
                    string managedName = name;
                    while (managedName.Contains("_"))
                    {
                        int pos = managedName.IndexOf('_');
                        managedName = managedName.Substring(0, pos) + managedName.Substring(pos + 1, 1).ToUpper() + managedName.Substring(pos + 2);
                    }

                    // Generate getter/setter methods
                    string getterName, setterName;
                    string managedGetterName, managedSetterName;
                    if (name.StartsWith("has"))
                    {
                        getterName = name;
                        setterName = "set" + name.Substring(3);
                        managedGetterName = managedName;
                        managedSetterName = "Set" + managedName.Substring(3);
                    }
                    else if (name.StartsWith("is"))
                    {
                        getterName = name;
                        setterName = "set" + name.Substring(2);
                        managedGetterName = managedName;
                        managedSetterName = "Set" + managedName.Substring(2);
                    }
                    else
                    {
                        getterName = "get" + name;
                        setterName = "set" + name;
                        managedGetterName = "Get" + managedName;
                        managedSetterName = "Set" + managedName;
                    }

                    // See if there are already accessor methods for this field
                    MethodDefinition getter = null, setter = null;
                    foreach (var m in c.Methods)
                    {
                        if (m.ManagedName.Equals(managedGetterName) && m.Parameters.Length == 0)
                        {
                            getter = m;
                            continue;
                        }

                        if (m.ManagedName.Equals(managedSetterName) && m.Parameters.Length == 1)
                        {
                            setter = m;
                        }
                    }

                    if (getter == null)
                    {
                        getter = new MethodDefinition(getterName, c, 0);
                        getter.ManagedName = managedGetterName;
                        getter.ReturnType = field.Type;
                        getter.Field = field;
                    }

                    var prop = new PropertyDefinition(getter);

                    // Can't assign value to reference or constant array
                    if (setter == null && !field.Type.IsReference && !field.Type.IsConstantArray)
                    {
                        setter = new MethodDefinition(setterName, c, 1);
                        setter.ManagedName = managedSetterName;
                        setter.ReturnType = new TypeRefDefinition();
                        setter.Field = field;
                        TypeRefDefinition constType;
                        if (!field.Type.IsBasic && !field.Type.IsPointer)
                        {
                            constType = field.Type.Copy();
                            constType.IsConst = true;
                        }
                        else
                        {
                            constType = field.Type;
                        }
                        setter.Parameters[0] = new ParameterDefinition("value", constType);
                        setter.Parameters[0].ManagedName = "value";

                        prop.Setter = setter;
                        prop.Setter.Property = prop;
                    }
                }
            }

            // Turn getters/setters into properties
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                // Getters with return type and 0 arguments
                foreach (var method in c.Methods)
                {
                    if (method.Parameters.Length == 0 && !method.IsVoid &&
                        (method.Name.StartsWith("get", StringComparison.InvariantCultureIgnoreCase) ||
                        method.Name.StartsWith("has", StringComparison.InvariantCultureIgnoreCase) ||
                        method.Name.StartsWith("is", StringComparison.InvariantCultureIgnoreCase)))
                    {
                        if (method.Property == null)
                        {
                            new PropertyDefinition(method);
                        }
                    }
                }

                // Getters with void type and 1 pointer argument for the return value
                // TODO: in general, it is not possible to automatically determine
                // whether such methods can be wrapped by properties or not,
                // so read this info from the project configuration.
                foreach (var method in c.Methods)
                {
                    if (method.Parameters.Length == 1 && method.IsVoid &&
                        method.Name.StartsWith("get", StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (method.Property != null)
                        {
                            continue;
                        }

                        var paramType = method.Parameters[0].Type;
                        if (paramType.IsPointer || paramType.IsReference)
                        {
                            // TODO: check project configuration
                            //if (true)
                            {
                                new PropertyDefinition(method);
                            }
                        }
                    }
                }

                // Setters
                foreach (var method in c.Methods)
                {
                    if (method.Parameters.Length == 1 &&
                        method.Name.StartsWith("set", StringComparison.InvariantCultureIgnoreCase))
                    {
                        string name = method.ManagedName.Substring(3);
                        // Find the property with the matching getter
                        foreach (PropertyDefinition prop in c.Properties)
                        {
                            if (prop.Setter != null)
                            {
                                continue;
                            }

                            if (prop.Name.Equals(name))
                            {
                                prop.Setter = method;
                                method.Property = prop;
                                break;
                            }
                        }
                    }
                }
            }

            // Get managed header names
            foreach (HeaderDefinition header in ExternalHeaders.Values)
            {
                string name = header.Name;
                string mapping;
                if (headerNameMapping.TryGetValue(name, out mapping))
                {
                    header.ManagedName = mapping;
                }
                else if (name.StartsWith("bt"))
                {
                    header.ManagedName = name.Substring(2);
                }
                else if (name.StartsWith("hacd"))
                {
                    header.ManagedName = "Hacd" + name.Substring(4);
                }
            }

            // Apply class properties
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                string name = c.Name;
                string mapping;
                if (classNameMapping.TryGetValue(name, out mapping))
                {
                    c.ManagedName = mapping;
                }
                else if (name.StartsWith("bt") && !name.Equals("btScalar"))
                {
                    c.ManagedName = name.Substring(2);
                }
                else
                {
                    c.ManagedName = name;
                }

                if (hidePublicConstructors.Contains(c.FullName))
                {
                    c.HidePublicConstructors = true;
                }

                if (hideInternalConstructor.Contains(c.FullName))
                {
                    c.NoInternalConstructor = true;
                }
                if (preventDelete.Contains(c.FullName))
                {
                    c.HasPreventDelete = true;
                }
                if (trackingDisposable.Contains(c.FullName))
                {
                    c.IsTrackingDisposable = true;
                }
            }

            // Sort methods and properties alphabetically
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                // Order by name, then fix inheritance, parent classes must appear first
                c.Classes.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
                var classesOrdered = c.Classes;
                for (int i = 0; i < classesOrdered.Count; i++)
                {
                    var thisClass = classesOrdered[i];
                    var baseClass = thisClass.BaseClass;
                    if (baseClass != null && classesOrdered.Contains(baseClass.Target))
                    {
                        int thisIndex = classesOrdered.IndexOf(thisClass);
                        if (thisIndex < classesOrdered.IndexOf(baseClass.Target))
                        {
                            classesOrdered.Remove(baseClass.Target);
                            classesOrdered.Insert(thisIndex, baseClass.Target);
                        }
                    }
                }

                c.Methods.Sort((m1, m2) => m1.Name.CompareTo(m2.Name));
                c.Properties.Sort((p1, p2) => p1.Name.CompareTo(p2.Name));
            }

            // Mark excluded classes
            foreach (ClassDefinition c in classDefinitions.Values)
            {
                if (IsExcludedClass(c))
                {
                    c.IsExcluded = true;
                }
            }

            Console.WriteLine("Parsing complete");
        }
        void CreateFieldSetter(PropertyDefinition prop, string setterName, string managedSetterName)
        {
            // Can't assign value to reference or constant array
            if (prop.Type.IsReference || prop.Type.IsConstantArray) return;

            if (prop.Type.Name != null && prop.Type.Name.StartsWith("btAlignedObjectArray")) return;

            var type = prop.Getter.ReturnType;

            MethodDefinition setter = new MethodDefinition(setterName, prop.Parent, 1);
            setter.ManagedName = managedSetterName;
            setter.ReturnType = new TypeRefDefinition();
            setter.Field = prop.Getter.Field;
            if (!type.IsBasic && !type.IsPointer)
            {
                type = type.Copy();
                type.IsConst = true;
            }
            setter.Parameters[0] = new ParameterDefinition("value", type);
            setter.Parameters[0].ManagedName = "value";

            prop.Setter = setter;
            prop.Setter.Property = prop;
        }
        // Create getters and setters for fields
        void CreateFieldAccessors()
        {
            foreach (var @class in Project.ClassDefinitions.Values)
            {
                foreach (var field in @class.Fields)
                {
                    string name = field.Name;
                    if (name.StartsWith("m_"))
                    {
                        name = name.Substring(2);
                    }
                    name = char.ToUpper(name[0]) + name.Substring(1); // capitalize
                    string managedName = ToCamelCase(name, true);

                    // Generate getter/setter methods
                    string getterName, setterName;
                    string managedGetterName, managedSetterName;
                    string verb = _booleanVerbs.FirstOrDefault(v => name.StartsWith(v));
                    if (verb != null && "bool".Equals(field.Type.Name))
                    {
                        getterName = name;
                        setterName = "set" + name.Substring(verb.Length);
                        managedGetterName = managedName;
                        managedSetterName = "Set" + managedName.Substring(verb.Length);
                    }
                    else
                    {
                        getterName = "get" + name;
                        setterName = "set" + name;
                        managedGetterName = "Get" + managedName;
                        managedSetterName = "Set" + managedName;
                    }

                    // See if there are already accessor methods for this field
                    MethodDefinition getter = null, setter = null;
                    foreach (var method in @class.Methods)
                    {
                        if (managedGetterName.Equals(method.ManagedName) && method.Parameters.Length == 0)
                        {
                            getter = method;
                            continue;
                        }

                        if (managedSetterName.Equals(method.ManagedName) && method.Parameters.Length == 1)
                        {
                            setter = method;
                        }
                    }

                    if (getter == null)
                    {
                        getter = new MethodDefinition(getterName, @class, 0);
                        getter.ManagedName = managedGetterName;
                        getter.ReturnType = field.Type;
                        getter.Field = field;
                    }

                    var prop = new PropertyDefinition(getter, GetPropertyName(getter));

                    if (setter == null)
                    {
                        CreateFieldSetter(prop, setterName, managedSetterName);
                    }
                }
            }
        }
        public static string GetTypeGetterCSMarshal(PropertyDefinition prop, int level)
        {
            StringBuilder output = new StringBuilder();
            TypeRefDefinition type = prop.Type;

            // If cached property can only be set in constructor,
            // the getter can simply return the cached value
            // TODO: check if cached value is initialized in all constructors
            CachedProperty cachedProperty;
            if (prop.Parent.CachedProperties.TryGetValue(prop.Name, out cachedProperty))
            {
                if (cachedProperty.Property.Setter == null)
                {
                    output.AppendLine(GetTabs(level + 2) + string.Format("get {{ return {0}; }}", cachedProperty.CacheFieldName));
                    return output.ToString();
                }
            }

            if (!type.IsBasic)
            {
                switch (type.ManagedNameCS)
                {
                    case "Matrix3x3":
                    case "Quaternion":
                    case "Transform":
                    case "Vector3":
                    case "Vector4":
                        output.AppendLine(GetTabs(level + 2) + "get");
                        output.AppendLine(GetTabs(level + 2) + "{");
                        output.AppendLine(GetTabs(level + 3) + GetTypeNameCS(type) + " value;");
                        output.AppendLine(GetTabs(level + 3) + string.Format("{0}_{1}(_native, out value);", prop.Parent.FullNameC, prop.Getter.Name));
                        output.AppendLine(GetTabs(level + 3) + "return value;");
                        output.AppendLine(GetTabs(level + 2) + '}');
                        return output.ToString();
                }
            }

            output.AppendLine(GetTabs(level + 2) + string.Format("get {{ return {0}{1}_{2}(_native){3}; }}",
                GetTypeMarshalConstructorStartCS(prop.Getter),
                prop.Parent.FullNameC, prop.Getter.Name,
                GetTypeMarshalConstructorEndCS(prop.Getter)));
            return output.ToString();
        }