function lldbFrameworkPaths() {
    let toolchain = nova.config.get("lldb.toolchain");
    let toolchainPath = nova.config.get("lldb.toolchain-path");
    
    // Set DYLD framework paths for finding LLDB.framework.
    let frameworkPaths = [];
    
    if (toolchain == "swift") {
        // Swift "latest" toolchain
        frameworkPaths.push("/Library/Developer/Toolchains/swift-latest.xctoolchain/System/Library/PrivateFrameworks/");
    }
    else if (toolchain == "custom" && toolchainPath) {
        // Custom toolchain
        frameworkPaths.push(nova.path.join(toolchainPath, "System/Library/PrivateFrameworks/"));
    }
    
    // Fallback to Xcode and CLI tools
    frameworkPaths.push("/Applications/Xcode-beta.app/Contents/SharedFrameworks/");
    frameworkPaths.push("/Applications/Xcode.app/Contents/SharedFrameworks/");
    frameworkPaths.push("/Library/Developer/CommandLineTools/Library/PrivateFrameworks/");
    
    return frameworkPaths;
}

// Tasks

class TasksAssistant {
    resolveTaskAction(context) {
        if (context.action != Task.Run) {
            return null;
        }
        
        let data = context.data;
        let config = context.config;
        
        let action = new TaskDebugAdapterAction("lldb");
        action.command = nova.path.normalize(nova.path.join(nova.extension.path, "../../NovaLLDBAdapter"));
        action.env = {
            // Set DYLD framework paths for finding LLDB.framework.
            "DYLD_FRAMEWORK_PATH": lldbFrameworkPaths().join(":"),
        };
        
        // Debug Args
        if (data.type == "lldbDebug") {
            // LLDB Debug
            let request = config.get("request", "string");
            if (!request) {
                request = "launch";
            }
            action.debugRequest = request;
            
            let debugArgs = {};
            
            debugArgs.program = config.get("launchPath", "string");
            debugArgs.args = config.get("launchArgs", "array");
            debugArgs.cwd = config.get("cwd", "string");
            debugArgs.runInRosetta = config.get("runInRosetta", "boolean");
            debugArgs.stopOnEntry = config.get("stopAtEntry", "boolean");
            if (request == "attach") {
                debugArgs.waitFor = true;
            }
            
            action.debugArgs = debugArgs;
        }
        else  if (data.type == "lldbRemoteDebug") {
            // LLDB Remote Debug
            let request = config.get("request", "string");
            if (!request) {
                request = "launch";
            }
            action.debugRequest = request;
            
            let debugArgs = {};
            
            debugArgs.host = config.get("host", "string");
            debugArgs.port = config.get("port", "integer");
            debugArgs.platform = config.get("platform", "string");
            debugArgs.program = config.get("launchPath", "string");
            debugArgs.args = config.get("launchArgs", "array");
            debugArgs.cwd = config.get("cwd", "string");
            debugArgs.stopOnEntry = config.get("stopAtEntry", "boolean");
            if (request == "attach") {
                debugArgs.waitFor = true;
            }
            
            let pathMappings = config.get("pathMappings");
            if (pathMappings) {
                // Ensure the local half of mappings are absolute paths.
                let basePath = nova.workspace.path;
                debugArgs.pathMappings = pathMappings.map(mapping => {
                    let local = mapping.localRoot;
                    let remote = mapping.remoteRoot;
                    if (!nova.path.isAbsolute(local)) {
                        local = nova.path.normalize(nova.path.join(basePath, local));
                    }
                    return {"local": local, "remote": remote};
                });
            }
            
            action.debugArgs = debugArgs;
        }
        
        return action;
    }
}

nova.assistants.registerTaskAssistant(new TasksAssistant(), {
    identifier: "lldb",
    name: "LLDB"
});


// Commands

nova.commands.register("lldb.resolvePlatforms", (workspace) => {
    return new Promise((resolve, reject) => {
        let adapterPath = nova.path.normalize(nova.path.join(nova.extension.path, "../../NovaLLDBAdapter"));
        
        let process = new Process(adapterPath, {
            args: ["platforms"],
            env: {
                "DYLD_FRAMEWORK_PATH": lldbFrameworkPaths().join(":"),
            },
        });
        
        var platforms = [];
        
        process.onStdout((line) => {
            let components = line.trim().split(": ", 2);
            if (components.length < 2) {
                return;
            }
            let identifier = components[0];
            if (identifier == "host") {
                return;
            }
            let description = components[1];
            platforms.push([identifier, `${identifier}: ${description}`]);
        });
        
        process.onDidExit(() => {
            resolve(platforms);
        });
        
        try {
            process.start();
        }
        catch (err) {
            reject(err);
        }
    });
});
