//
//  LibraryView.swift
//  Ryujinx
//
//  Created by Isaac Marovitz on 31/12/2023.
//

import SwiftUI
import UniformTypeIdentifiers
#if !targetEnvironment(simulator)
import LibRyujinx
#endif

struct LibraryView: View {
    @EnvironmentObject var settings: Settings
    @EnvironmentObject var games: Games

    @State var search: String = ""
    @State var isFirmwareInstalled: Bool = false
    @State var showingGameImport = false

    private let gridLayout = [GridItem(.adaptive(minimum: 100))]
    private var searchedGames: [Game] {
        guard !search.isEmpty else { return games.games }
        return games.games.filter({ $0.titleName.localizedCaseInsensitiveContains(search) })
    }

    var body: some View {
        NavigationStack {
            Group {
                if games.games.isEmpty {
                    VStack {
                        Spacer()
                        Group {
                            Text("No games installed.\n Press the ") + Text(Image(systemName: "plus")) + Text(" button to add some.")
                        }
                            .multilineTextAlignment(.center)
                        Spacer()
                    }
                } else {
                    ScrollView {
                        LazyVGrid(columns: gridLayout, spacing: 10) {
                            ForEach(searchedGames, id: \.id) { game in
                                NavigationLink {
                                    SDLView(game: game)
                                } label: {
                                    
                                    GameView(game: game)
                                        .environmentObject(games)
                                }

                            }
                        }
                        .padding(.horizontal)
                    }
                }
            }
            .toolbar {
                ToolbarItemGroup(placement: .topBarLeading) {
                    Text("Library")
                        .font(.headline)
                        .padding(.leading, 5)
                }
                ToolbarItemGroup(placement: .primaryAction) {
                    Button {
                        showingGameImport.toggle()
                    } label: {
                        Image(systemName: "plus")
                            .foregroundStyle(.primary)
                    }
                }
                ToolbarItemGroup(placement: .secondaryAction) {
                    Button {
                        print("Open Mii Editor")
                    } label: {
                        Label {
                            Text("Open Mii Editor")
                        } icon: {
                            Image(systemName: "person")
                        }
                    }
                    .disabled(!isFirmwareInstalled)
                }
            }
            .navigationBarTitleDisplayMode(.inline)
            .searchable(text: $search, placement: .navigationBarDrawer(displayMode: .always))
            .fileImporter(isPresented: $showingGameImport, allowedContentTypes: [.nca, .nro, .nso, .nsp], allowsMultipleSelection: false) { result in
                switch result {
                case .success(let urls):
                    do {
                        try urls.forEach { url in
                            let gotAccess = url.startAccessingSecurityScopedResource()
                            if !gotAccess {
                                print("Failed to get access to imported game at \(url)!")
                                return
                            }

                            // Check if the game is valid before copying it
                            let handle = try FileHandle(forReadingFrom: url)
                            #if !targetEnvironment(simulator)
                            // Something something LibRyujinx
                            #endif
                            try handle.close()

                            let documentsFolder = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                            let gameFiles = documentsFolder.appending(path: "GameFiles")

                            // We can't use title ID as a unique file name as many homebrew just have all 0s
                            let gameFolder = gameFiles.appending(path: UUID().uuidString)

                            if !FileManager.default.fileExists(atPath: gameFolder.path(percentEncoded: false)) {
                                try FileManager.default.createDirectory(at: gameFolder, withIntermediateDirectories: true)
                            }

                            let finalLocation = gameFolder.appending(path: "Title").appendingPathExtension(url.pathExtension)

                            if FileManager.default.fileExists(atPath: finalLocation.path(percentEncoded: false)) {
                                try FileManager.default.removeItem(at: finalLocation)
                            }

                            try FileManager.default.copyItem(at: url, to: finalLocation)

                            url.stopAccessingSecurityScopedResource()
                            loadGames()
                        }
                    } catch {
                        print(error)
                    }
                case .failure(let error):
                    print(error)
                }
            }
        }
        .animation(.easeInOut(duration: 0.2), value: searchedGames)
        .animation(.easeInOut(duration: 0.2), value: search)
        .onAppear {
            loadGames()
            checkFirmware()
        }
    }

    func checkFirmware() {
#if !targetEnvironment(simulator)
        let cstring = device_get_installed_firmware_version()
        let version = String(cString: cstring!)
        if version != String() {
            isFirmwareInstalled = true
        }
#endif
    }

    func loadGames() {
        games.games.removeAll()

        do {
            let documentsFolder = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            let gameFiles = documentsFolder.appendingPathComponent("GameFiles")

            let gameEnumerator = FileManager.default.enumerator(at: gameFiles,
                                                                includingPropertiesForKeys: [.isDirectoryKey],
                                                                options: [.skipsSubdirectoryDescendants])
            while let gameFolderURL = gameEnumerator?.nextObject() as? URL {
                var type: UTType = .nsp
                var titleName = gameFolderURL.lastPathComponent
                var developer = ""
                var fileURL: URL? = nil

                let filesEnumerator = FileManager.default.enumerator(at: gameFolderURL,
                                                                     includingPropertiesForKeys: [.isRegularFileKey])
                
                while let fileURLCandidate = filesEnumerator?.nextObject() as? URL {
                    let urlExtension = fileURLCandidate.pathExtension.lowercased()

                    // Supported game file extensions
                    if ["nsp", "xci"].contains(urlExtension) {
                        fileURL = fileURLCandidate
                        type = UTType(filenameExtension: urlExtension) ?? .nsp
                    }

    #if !targetEnvironment(simulator)
                    // Check if the file is the Title file for extracting game info
                    let titleUrl = fileURLCandidate.deletingPathExtension()
                    if titleUrl.lastPathComponent == "Title" {
                        let handle = try FileHandle(forReadingFrom: fileURLCandidate)
                        let fileExtension = (fileURLCandidate.pathExtension as NSString).utf8String
                        let extensionPtr = UnsafeMutablePointer<CChar>(mutating: fileExtension)

                        var gameInfo = get_game_info(handle.fileDescriptor, extensionPtr)

                        titleName = withUnsafePointer(to: &gameInfo.TitleName) {
                            $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
                                String(cString: $0)
                            }
                        }

                        developer = withUnsafePointer(to: &gameInfo.Developer) {
                            $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
                                String(cString: $0)
                            }
                        }

                        try handle.close()
                    }
    #endif
                }

                if let validFileURL = fileURL {
                    // Append the game to the list with the proper fileURL
                    games.games.append(Game(containerFolder: gameFolderURL,
                                            fileType: type,
                                            fileURL: validFileURL,
                                            titleName: titleName,
                                            titleId: "",
                                            developer: developer,
                                            version: ""))
                }
            }
        } catch {
            print(error)
        }
    }
}

#Preview {
    LibraryView()
}

func startEmulation(game: Game) {
    do {
        let gotAccess = game.fileURL.startAccessingSecurityScopedResource()
        if !gotAccess {
            print("Failed to get access to game file at \(game.fileURL.path)")
            return
        }

        // Initialize the device/emulation
        let handle = try FileHandle(forReadingFrom: game.fileURL)
        
        #if !targetEnvironment(simulator)
        let fileDescriptor = handle.fileDescriptor
        let isInitialized = device_initialize(false, true, 0, 0, false, true, true, true, 0, true)
        if isInitialized {
            let isLoaded = device_load(Int(fileDescriptor))
            if isLoaded {
                print("Game \(game.titleName) loaded successfully!")
                
                // Start the rendering loop
            } else {
                print("Failed to load the game \(game.titleName).")
            }
        } else {
            print("Failed to initialize the device for \(game.titleName).")
        }
        #endif

        try handle.close()
        game.fileURL.stopAccessingSecurityScopedResource()
    } catch {
        print("Error starting emulation for game \(game.titleName): \(error)")
    }
}
