private JsonSchemaWrapper ResolveSchemaHelper(Uri curr, Uri parent, string data)
        {
            var ids = GetIds(data);

            data = StandardizeReferences(parent, data, ids);
            var definition = new
            {
                csharpType       = string.Empty,
                csharpInterfaces = new string[] { },
                properties       = new Dictionary <string, JObject>()
            };
            var deserialized = JsonConvert.DeserializeAnonymousType(data, definition);
            var dependencies = new List <JsonSchemaWrapper>();

            MatchCollection matches = Regex.Matches(data, @"\""\$ref\""\s*:\s*\""(.*)\""");

            foreach (Match match in matches)
            {
                // Get the full path to the file, and change the reference to match
                string refName  = match.Groups[1].Value;
                var    currPath = new Uri(refName, UriKind.RelativeOrAbsolute);
                if (ids.Contains(refName))
                {
                    //internal reference by ID, no need to load a new schema or add a dependency
                    continue;
                }
                var currUri = IoUtils.GetAbsoluteUri(parent, currPath, true);

                JsonSchemaWrapper schema;

                if (!_schemas.ContainsKey(currUri))
                {
                    // if this is a self reference, no need to load anything or add a dependency
                    if (parent.Equals(currUri))
                    {
                        continue;
                    }

                    schema = ResolveSchemaHelper(parent, currUri);
                    _schemas.Add(currUri, schema);
                }
                else
                {
                    schema = _schemas[currUri];
                }

                // Add schema to dependencies
                dependencies.Add(schema);
            }

            // Go through properties to see if there needs to be more resolving
            if (deserialized != null && deserialized.properties != null)
            {
                foreach (var s in deserialized.properties)
                {
                    var properties = s.Value.Properties();

                    // Check that the property also has a top level key called properties or items
                    foreach (var prop in properties)
                    {
                        var isProp = prop.Name.Equals("properties");
                        var isItem = prop.Name.Equals("items");

                        // TODO ehhhh let's avoid hardcoding this
                        if (isProp || (isItem && prop.Value.ToString().Contains("\"properties\"")))
                        {
                            var propData = isProp ? s.Value.ToString() : prop.Value.ToString();

                            // Create dummy internal Uri
                            var dummyUri = new Uri(new Uri(curr + "/"), s.Key);

                            JsonSchemaWrapper schema = ResolveSchemaHelper(dummyUri, curr, propData);

                            if (!_schemas.ContainsKey(dummyUri))
                            {
                                _schemas.Add(dummyUri, schema);
                            }
                        }
                    }
                }
            }

            // Set up schema and wrapper to return
            JsonSchema parsed = null;

            RemoveDuplicateSchemas();

            // work around the situation where we don't have a schema in our list of schemas,
            // but the resolver itself has already loaded that schema because it's a sub-schema
            // of a schema we've already loaded. In this case, find the already-resolved
            // schema and add that to our list of schemas
            foreach (var schema in _resolver.LoadedSchemas)
            {
                if (schema.Id != null && schema.Id.Equals(curr.ToString(), StringComparison.InvariantCultureIgnoreCase))
                {
                    parsed = schema;
                }
            }
            try
            {
                if (parsed == null)
                {
                    parsed = JsonSchema.Parse(data, _resolver);
                }
            }
            catch (Exception)
            {
                _log.Error("Could not parse the schema: " + curr + "\nMake sure your schema is compatible." +
                           "Examine the stack trace below.");
                throw;
            }

            parsed.Id    = curr.ToString();
            parsed.Title = parsed.Title.SanitizeIdentifier();
            var toReturn = new JsonSchemaWrapper(parsed)
            {
                Namespace = _ns, Dependencies = dependencies
            };

            // If csharpType is specified
            if (deserialized != null && !string.IsNullOrEmpty(deserialized.csharpType))
            {
                // Create directories and set namespace
                int    lastIndex = deserialized.csharpType.LastIndexOf('.');
                string cType     = deserialized.csharpType.Substring(lastIndex == -1 ? 0 : lastIndex + 1);

                toReturn.Namespace    = deserialized.csharpType.Substring(0, lastIndex);
                toReturn.Schema.Title = cType;

                if (_createDirs)
                {
                    IoUtils.CreateDirectoryFromNamespace(_baseDir, toReturn.Namespace);
                }
            }

            // If csharpInterfaces is specified
            if (deserialized != null && deserialized.csharpInterfaces != null)
            {
                foreach (string s in deserialized.csharpInterfaces)
                {
                    // Try to resolve the type
                    Type t = Type.GetType(s, false);

                    // If type cannot be found, create a new type
                    if (t == null)
                    {
                        var builder = new TypeBuilderHelper(toReturn.Namespace);
                        t = builder.GetCustomType(s, !s.Contains("."));
                    }

                    toReturn.Interfaces.Add(t);
                }
            }

            return(toReturn);
        }