import UIKit
//import MetalANGLE
import GameController
import AVFoundation
import AudioToolbox
import Foundation
import FirebaseFirestore
import FirebaseAuth

// C function declaration from YabaInterface.mm
// Note: RetroAchievements functions are declared in RetroAchievementsManager.swift
@_silgen_name("GetGamePath")
func GetGamePath() -> UnsafePointer<CChar>?

var scurrentGamePath: UnsafeMutablePointer<Int8>? = nil

protocol GameViewControllerDelegate : AnyObject {
    func didTapMenuButton()
    func didPresentModal(_ viewController: UIViewController)
    func didDismissModal()
}

@objcMembers
class GameViewController: MGLKViewController,MGLKViewControllerDelegate,DraggableCircleViewDelegate, LeaderBoardControllerDelegate, GameUiEvent {



    weak var gdelegate: GameViewControllerDelegate?
    private let feedbackGenerator = UIImpactFeedbackGenerator(style: .medium)

    // MARK: - Properties
    var iPodIsPlaying: UInt32 = 0
    var selectedFile: String?
    var productNumber: String?

    // 現在のゲーム
    var currentGame: BaseGame?

    var context: MGLContext!
    var shareContext: MGLContext!
    var controller: GCController?
    var command: Int32 = 0
    var returnValue: Bool = false
    var keyMapper: KeyMapper!
    var isKeyRemappingMode: Bool = false
    var remapAlertController: UIAlertController?
    var currentlyMappingKey: SaturnKey = .none
    var remapLabelViews: [UILabel] = []
    var isFirst: Bool = true
    var padButtons: [PadButton] = []
    private var controller_edit_mode: Int = 0
    private var canRotateToAllOrientations: Bool = false
    private var _landscape: Bool = false
    private var isAnalogMode: Bool = false
    private var isOnScreenControlHidden: Bool = false
    private var isAnalogAsDpad: Bool = false
    
    // Track previous resize_screen parameters to avoid unnecessary calls
    private var lastResizeParams: (left: Int32, bottom: Int32, width: Int32, height: Int32)?

    // MARK: - IBOutlets
    @IBOutlet weak var leftPanel: UIImageView!
    @IBOutlet weak var rightPanel: UIImageView!
    @IBOutlet weak var startView: UIView!
    @IBOutlet weak var rightView: UIView!
    @IBOutlet weak var leftView: UIView!
    @IBOutlet weak var scaleSlider: UISlider!

    @IBOutlet weak var rightButton: UIView!
    @IBOutlet weak var downButton: UIView!
    @IBOutlet weak var upButton: UIView!
    @IBOutlet weak var leftButton: UIView!
    @IBOutlet weak var leftTrigger: UIView!
    @IBOutlet weak var startButton: UIView!
    @IBOutlet weak var rightTrigger: UIView!
    @IBOutlet weak var aButton: UIView!
    @IBOutlet weak var bButton: UIView!
    @IBOutlet weak var cButton: UIView!
    @IBOutlet weak var xButton: UIView!
    @IBOutlet weak var yButton: UIView!
    @IBOutlet weak var zButton: UIView!
    @IBOutlet weak var menuButton: UIButton!
    @IBOutlet weak var analogPad: DraggableCircleView!

    static let saturnKeyDescriptions: [SaturnKey: String] = [
        .a: "A",
        .b: "B",
        .c: "C",
        .x: "X",
        .y: "Y",
        .z: "Z",
        .leftTrigger: "LT",
        .rightTrigger: "RT",
        .start: "Start"
    ]

    func setAnalogMode( to: Bool ){
        isAnalogMode = to
        if(isAnalogMode){
            SetAnalogMode(1)
        }else{
            SetAnalogMode(0)
        }

        if( isOnScreenControlHidden ){
            analogPad.isHidden = true
        }else{
            analogPad.isHidden = !isAnalogMode
        }

        // Sync to new YabausePadView if active
        syncAnalogModeToYabausePad(to)
    }

    lazy var saturnKeyToViewMappings: [SaturnKey: UIView] = {
        [
            .a: self.aButton,
            .b: self.bButton,
            .c: self.cButton,
            .x: self.xButton,
            .y: self.yButton,
            .z: self.zButton,
            .leftTrigger: self.leftTrigger,
            .rightTrigger: self.rightTrigger,
            .start: self.startButton
        ]
    }()


    // MARK: - Lifecycle Methods
    override func viewDidLoad() {
        super.viewDidLoad()

        self.delegate = self
        loadSettings()

        // Initialize RetroAchievements system
        initializeRetroAchievements()

        // productNumberがセットされていればdetectGameを呼ぶ
        if let productNumber = self.productNumber {
            self.detectGame(productNumber: productNumber)
        }

        //sharedData = self
        _objectForLock = NSObject()
        isFirst = true

        glView?.drawableDepthFormat = MGLDrawableDepthFormat24
        glView?.drawableStencilFormat = MGLDrawableStencilFormat8

        if( scurrentGamePath != nil ){
            scurrentGamePath?.deallocate()
            scurrentGamePath = nil
        }
        let cString = (selectedFile! as NSString).utf8String!
        let bufferSize = Int(strlen(cString)) + 1
        scurrentGamePath = UnsafeMutablePointer<Int8>.allocate(capacity: bufferSize)
        strncpy(scurrentGamePath, cString, bufferSize)
        currentGamePath = UnsafePointer<CChar>(scurrentGamePath)
        // Load RetroAchievements data - wait for login status
        self.loadGameForRetroAchievementsWhenReady()
        YabauseManager.shared.updateCheatCode(nil)
        view.isMultipleTouchEnabled = true
        command = 0

        rightView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
        leftView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
        startView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
        leftButton.alpha = 0
         rightButton.alpha = 0
         upButton.alpha = 0
         downButton.alpha = 0
         aButton.alpha = 0
         bButton.alpha = 0
         cButton.alpha = 0
         xButton.alpha = 0
         yButton.alpha = 0
         zButton.alpha = 0
         leftTrigger.alpha = 0
         rightTrigger.alpha = 0
         startButton.alpha = 0

        // 空の配列を作成
        padButtons = [PadButton]()

        // PadButtons.lastまでの各ボタンに対して新しいPadButtonインスタンスを作成
        for _ in 0...PadButtons.last.rawValue {
            padButtons.append(PadButton())
        }

        // Create and add PadButton objects to the padButtons array
        padButtons[PadButtons.up.rawValue].target = upButton
        padButtons[PadButtons.right.rawValue].target = rightButton
        padButtons[PadButtons.down.rawValue].target = downButton
        padButtons[PadButtons.left.rawValue].target = leftButton
        padButtons[PadButtons.rightTrigger.rawValue].target = rightTrigger
        padButtons[PadButtons.leftTrigger.rawValue].target = leftTrigger
        padButtons[PadButtons.start.rawValue].target = startButton
        padButtons[PadButtons.a.rawValue].target = aButton
        padButtons[PadButtons.b.rawValue].target = bButton
        padButtons[PadButtons.c.rawValue].target = cButton
        padButtons[PadButtons.x.rawValue].target = xButton
        padButtons[PadButtons.y.rawValue].target = yButton
        padButtons[PadButtons.z.rawValue].target = zButton

         preferredFramesPerSecond = 60

         NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)

         context = MGLContext(api: kMGLRenderingAPIOpenGLES3)
         shareContext = MGLContext(api: kMGLRenderingAPIOpenGLES3, sharegroup: context.sharegroup)

         guard let context = context else {
             print("Failed to create ES context")
             return
         }

         g_share_context = shareContext
         g_context = context

        glView!.context = context
         MGLContext.setCurrent(context)
        glLayer = glView?.glLayer

         setup()

         iOSCoreAudioInit()

         // ... (saturnKeyDescriptions and saturnKeyToViewMappings initialization)

         keyMapper = KeyMapper()
         keyMapper.loadFromDefaults()
         remapLabelViews = []

         scaleSlider.isHidden = true
         scaleSlider.minimumValue = 0.5
         scaleSlider.maximumValue = 1.0
         scaleSlider.value = _controller_scale
         scaleSlider.addTarget(self, action: #selector(didValueChanged(_:)), for: .valueChanged)
         controller_edit_mode = 0
         updateControllScale(_controller_scale)

        NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
        analogPad.delegate = self

        // Setup new YabausePad
        // TODO: Add toggle in Settings to switch between classic and new pad
        setupYabausePadView()

        // Sync analog mode state to YabausePadView (loadSettings was called before pad was created)
        syncAnalogModeToYabausePad(isAnalogMode)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        if hasControllerConnected() {
            print("Discovery finished on first pass")
            foundController()
        } else {
            print("Discovery happening patiently")
            patientlyDiscoverController()
        }

        becomeFirstResponder()
        
    }

    // MARK: - Setup Methods
    func setup() {
        MGLContext.setCurrent(context)
        let scale = UIScreen.main.scale
        let newFrame = view.frame
        let safeArea = view.safeAreaInsets

        if isFirst {
            start_emulation(Int32(Float(safeArea.left * scale)), Int32(Float(safeArea.bottom * scale)), Int32(Float((newFrame.width - safeArea.right - safeArea.left) * scale)), Int32(Float((newFrame.height - safeArea.top - safeArea.bottom) * scale)))
            isFirst = false
        } else {
            resize_screen(Int32(Float(safeArea.left * scale)), Int32(Float(safeArea.bottom * scale)), Int32(Float((newFrame.width - safeArea.right - safeArea.left) * scale)), Int32(Float((newFrame.height - safeArea.top - safeArea.bottom) * scale)))
        }
        returnValue = true
    }

    // MARK: - State Management
    func saveState() {
        command = MSG_SAVE_STATE
    }

    func loadState() {
        command = MSG_LOAD_STATE
    }

    func reset() {
        command = MSG_RESET
    }

    // MARK: - Controller Methods
    func hasControllerConnected() -> Bool {
#if targetEnvironment(simulator)
        return false
#else
        return GCController.controllers().count > 0
#endif
    }

    func patientlyDiscoverController() {
        GCController.startWirelessControllerDiscovery(completionHandler: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(foundController), name: .GCControllerDidConnect, object: nil)
    }

    @objc func foundController() {
        #if targetEnvironment(simulator)

        #else
            print("Found Controller")
            NotificationCenter.default.addObserver(self, selector: #selector(controllerDidDisconnect), name: .GCControllerDidDisconnect, object: nil)
            refreshViewsWithKeyRemaps()
            setControllerOverlayHidden(true)
            completionWirelessControllerDiscovery()
            updateSideMenu()
        #endif
    }


    func mglkViewControllerUpdate(_ controller: MGLKViewController) {
        emulateOneFrame()
        
        // Process RetroAchievements frame (Step 7 of rcheevos integration)
        RetroAchievementsBridge.processFrame()
    }

    // MGLKViewDelegate メソッド
      //override func mglkView(_ view: MGLKView, drawIn rect: CGRect) {
          // 描画コード
     // }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        let scale = UIScreen.main.scale
        let newFrame = self.view.frame
        let safeArea = self.view.safeAreaInsets

        // Calculate new parameters
        let newLeft = Int32(Float(safeArea.left * scale))
        let newBottom = Int32(Float(safeArea.bottom * scale))
        let newWidth = Int32(Float((newFrame.width - safeArea.right - safeArea.left) * scale))
        let newHeight = Int32(Float((newFrame.height - safeArea.top - safeArea.bottom) * scale))
        
        // Only call resize_screen if parameters have changed
        if lastResizeParams == nil || 
           lastResizeParams!.left != newLeft ||
           lastResizeParams!.bottom != newBottom ||
           lastResizeParams!.width != newWidth ||
           lastResizeParams!.height != newHeight {
            
            resize_screen(newLeft, newBottom, newWidth, newHeight)
            
            // Update the stored parameters
            lastResizeParams = (left: newLeft, bottom: newBottom, width: newWidth, height: newHeight)
        }

    }

    // MARK: - Emulation Methods
    func emulateOneFrame() {
        if returnValue {
            returnValue = false
        }
        emulation_step(Int32(command))
        command = 0
    }

    func updateControllScale(_ scale: Float) {
        let lfv = leftView
        var tf = CGAffineTransform(scaleX: 1.0, y: 1.0)
        tf = tf.translatedBy(x: -(258.0/2.0), y: (340.0/2.0))
        tf = tf.scaledBy(x: CGFloat(scale), y: CGFloat(scale))
        tf = tf.translatedBy(x: (258.0/2.0), y: -(340.0/2.0))
        lfv!.transform = tf

        let rfv = rightView
        tf = CGAffineTransform(scaleX: 1.0, y: 1.0)
        tf = tf.translatedBy(x: (258.0/2.0), y: (340.0/2.0))
        tf = tf.scaledBy(x: CGFloat(scale), y: CGFloat(scale))
        tf = tf.translatedBy(x: -(258.0/2.0), y: -(340.0/2.0))
        rfv!.transform = tf

        let sfv = startView
        tf = CGAffineTransform(scaleX: 1.0, y: 1.0)
        //tf = tf.translatedBy(x: (258.0/2.0), y: (340.0/2.0))
        tf = tf.scaledBy(x: CGFloat(scale), y: CGFloat(scale))
        //tf = tf.translatedBy(x: -(258.0/2.0), y: -(340.0/2.0))
        sfv!.transform = tf

        let ud = UserDefaults.standard
        ud.set(scale, forKey: "controller scale")
        ud.synchronize()
    }

    func didValueChanged(_ slider: UISlider) {
        let scaleVal = slider.value
        updateControllScale(scaleVal)
    }

    var isControllerEditModeEnabled = false

    func toggleControllerEditMode() {
        isControllerEditModeEnabled.toggle()
        scaleSlider.isHidden = !isControllerEditModeEnabled
    }


    // MARK: - Pause Management
    //func setPaused(_ pause: Bool) {
        // Implement the logic for pausing the game
    //}

    // MARK: - Key Remapping Methods
    func startKeyRemapping() {
        refreshViewsWithKeyRemaps()
        isKeyRemappingMode.toggle()
        setControllerOverlayHidden(!isKeyRemappingMode)
    }

    func isCurrentlyRemappingControls() -> Bool {
        return isKeyRemappingMode
    }

    // MARK: - Helper Methods
    func refreshViewsWithKeyRemaps() {
        if !remapLabelViews.isEmpty {
            remapLabelViews.forEach { $0.removeFromSuperview() }
        }
        remapLabelViews.removeAll()

        for (button, saturnButtonView) in saturnKeyToViewMappings {
            let buttonNumber = button.rawValue
            var buttonNames = ""
            let mfiButtons = keyMapper.getControls(forMappedKey: SaturnKey(rawValue: Int(buttonNumber)) ?? SaturnKey.none )

            for (index, mfiButton) in mfiButtons.enumerated() {
                if index > 0 {
                    buttonNames += " / "
                }
                buttonNames += KeyMapper.controlToDisplayName(KeyMapMappableButton(rawValue: mfiButton.intValue) ?? KeyMapMappableButton.MFI_BUTTON_OPTION)
            }

            if !mfiButtons.isEmpty {
                let mappedLabel = UILabel()
                mappedLabel.text = buttonNames
                mappedLabel.alpha = 0.6
                mappedLabel.font = UIFont.boldSystemFont(ofSize: 16.0)
                mappedLabel.textColor = .red
                mappedLabel.translatesAutoresizingMaskIntoConstraints = false

                view.addSubview(mappedLabel)
                NSLayoutConstraint.activate([
                    mappedLabel.centerXAnchor.constraint(equalTo: (saturnButtonView ).centerXAnchor),
                    mappedLabel.centerYAnchor.constraint(equalTo: (saturnButtonView ).centerYAnchor)
                ])

                view.bringSubviewToFront(mappedLabel)
                remapLabelViews.append(mappedLabel)
                view.setNeedsLayout()
            }
        }
    }

    func setControllerOverlayHidden(_ hidden: Bool) {
        leftPanel.isHidden = hidden
        rightPanel.isHidden = hidden
        leftButton.isHidden = hidden
        rightButton.isHidden = hidden
        upButton.isHidden = hidden
        downButton.isHidden = hidden
        aButton.isHidden = hidden
        bButton.isHidden = hidden
        cButton.isHidden = hidden
        xButton.isHidden = hidden
        yButton.isHidden = hidden
        zButton.isHidden = hidden
        leftTrigger.isHidden = hidden
        rightTrigger.isHidden = hidden
        startButton.isHidden = hidden
        startView.isHidden = hidden


        remapLabelViews.forEach { label in
            label.isHidden = hasControllerConnected() && !hidden ? false : hidden
        }

        isOnScreenControlHidden = hidden
        if( hidden ){
            analogPad.isHidden = true
        }else{
            analogPad.isHidden = !isAnalogMode
        }
    }

    func completionWirelessControllerDiscovery() {
        let mfiButtonHandler: (KeyMapMappableButton, Bool) -> Void = { [weak self] mfiButton, pressed in
            guard let self = self else { return }

            if self.isKeyRemappingMode && self.currentlyMappingKey != SaturnKey.none  {
                self.keyMapper.mapKey(self.currentlyMappingKey, toControl: mfiButton)
                if let remapAlertController = self.remapAlertController {
                    remapAlertController.dismiss(animated: true, completion: nil)
                }
                self.keyMapper.saveKeyMapping()
                self.currentlyMappingKey = SaturnKey.none
                self.refreshViewsWithKeyRemaps()
            } else {
                if let mappedKeyNumber = self.keyMapper.getMappedKey(forControl: mfiButton) {
                    let mappedKey = SaturnKey(rawValue: mappedKeyNumber.intValue) ?? .none
                    if pressed {
                        PerKeyDown(UInt32(mappedKey.rawValue))
                    } else {
                        PerKeyUp(UInt32(mappedKey.rawValue))
                    }
                }
            }
        }

        if let controller = GCController.controllers().first {
            self.controller = controller
            if let gamepad = controller.extendedGamepad {
                gamepad.buttonHome?.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_HOME, pressed)
                }

                gamepad.buttonMenu.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_MENU, pressed)
//                    if let revealViewController = self?.revealViewController as? GameRevealViewController, pressed {
//                        revealViewController.revealToggle(0)
 //                   }
                }

                gamepad.buttonOptions?.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_OPTION, pressed)
                }

                gamepad.buttonA.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_A, pressed)
                }

                gamepad.rightShoulder.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_RS, pressed)
                }

                gamepad.leftShoulder.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_LS, pressed)
                }

                gamepad.leftTrigger.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_LT, pressed)
                }

                gamepad.rightTrigger.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_RT, pressed)
                }

                gamepad.buttonX.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_X, pressed)
                }

                gamepad.buttonY.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_Y, pressed)
                }

                gamepad.buttonB.valueChangedHandler = { _, _, pressed in
                    mfiButtonHandler(.MFI_BUTTON_B, pressed)
                }

                gamepad.dpad.up.valueChangedHandler = { _, _, pressed in
                    if pressed {
                        PerKeyDown(UInt32(SaturnKey.up.rawValue))
                    } else {
                        PerKeyUp(UInt32(SaturnKey.up.rawValue))
                    }
                }

                gamepad.dpad.down.valueChangedHandler = { _, _, pressed in
                    if pressed {
                        PerKeyDown(UInt32(SaturnKey.down.rawValue))
                    } else {
                        PerKeyUp(UInt32(SaturnKey.down.rawValue))
                    }
                }

                gamepad.dpad.left.valueChangedHandler = { _, _, pressed in
                    if pressed {
                        PerKeyDown(UInt32(SaturnKey.left.rawValue))
                    } else {
                        PerKeyUp(UInt32(SaturnKey.left.rawValue))
                    }
                }

                gamepad.dpad.right.valueChangedHandler = { _, _, pressed in
                    if pressed {
                        PerKeyDown(UInt32(SaturnKey.right.rawValue))
                    } else {
                        PerKeyUp(UInt32(SaturnKey.right.rawValue))
                    }
                }


                gamepad.leftThumbstick.valueChangedHandler = { _, xValue, yValue in
                    if( self.isAnalogMode ){

                        PerAxisValue(PERANALOG_AXIS_X, convertDoubleToUInt8((Double(xValue) * 128.0) + 128)); // from 0 to 255
                        PerAxisValue(PERANALOG_AXIS_Y, convertDoubleToUInt8((Double(-yValue) * 128.0) + 128)); // from 0 to 255

                    }else if( self.isAnalogAsDpad ){

                        if( xValue < 0.1 && xValue > -0.1  ){
                            PerKeyUp(UInt32(SaturnKey.left.rawValue))
                            PerKeyUp(UInt32(SaturnKey.right.rawValue))
                        }else if( xValue > 0.5 ){
                            PerKeyUp(UInt32(SaturnKey.left.rawValue))
                            PerKeyDown(UInt32(SaturnKey.right.rawValue))
                        }else if( xValue < -0.5 ){
                            PerKeyDown(UInt32(SaturnKey.left.rawValue))
                            PerKeyUp(UInt32(SaturnKey.right.rawValue))
                        }

                        if( yValue < 0.1 && yValue > -0.1  ){
                            PerKeyUp(UInt32(SaturnKey.up.rawValue))
                            PerKeyUp(UInt32(SaturnKey.down.rawValue))
                        }else if( yValue > 0.5 ){
                            PerKeyDown(UInt32(SaturnKey.up.rawValue))
                            PerKeyUp(UInt32(SaturnKey.down.rawValue))
                        }else if( yValue < -0.5 ){
                            PerKeyUp(UInt32(SaturnKey.up.rawValue))
                            PerKeyDown(UInt32(SaturnKey.down.rawValue))
                        }

                    }
                }

                gamepad.leftTrigger.valueChangedHandler = { _, Value, yValue in
                    if( self.isAnalogMode ){
                        PerAxisValue(PERANALOG_AXIS_LTRIGGER, convertDoubleToUInt8(( (Double(Value)) * 255.0))); // from 0 to 255
                    }
                    if Value > 0.5 {
                        mfiButtonHandler(.MFI_BUTTON_LT, true)
                    }else{
                        mfiButtonHandler(.MFI_BUTTON_LT, false)
                    }
                }

                gamepad.rightTrigger.valueChangedHandler = { _, Value, yValue in
                    if( self.isAnalogMode ){
                        PerAxisValue(PERANALOG_AXIS_RTRIGGER, convertDoubleToUInt8(( (Double(Value)) * 255.0))); // from 0 to 255
                    }
                    if Value > 0.5 {
                        mfiButtonHandler(.MFI_BUTTON_RT, true)
                    }else{
                        mfiButtonHandler(.MFI_BUTTON_RT, false)
                    }
                }


                // Commented out in the original code
                /*
                gamepad.rightThumbstick.valueChangedHandler = { _, xValue, yValue in
                    if yValue >= 0.5 || yValue <= -0.5 {
                        PerKeyDown(.start)
                    } else {
                        PerKeyUp(.start)
                    }
                }
                */
            }
        }
    }

    @IBAction func onMenu(_ sender: Any) {
        gdelegate?.didTapMenuButton()
    }

    func updateSideMenu() {
        // Implement the logic for updating side menu
    }

    // MARK: - Notification Handlers
    @objc func didEnterBackground() {
        enterBackGround()
        self.isPaused = true
    }

    @objc func didBecomeActive() {
        self.view.becomeFirstResponder()
        self.isPaused = false
        self.returnValue = true
    }

    override var canBecomeFirstResponder: Bool {
        return true
    }

    @objc func controllerDidDisconnect() {
        if GCController.controllers().count <= 0 {
            setControllerOverlayHidden(false)
            isKeyRemappingMode = false
            if !remapLabelViews.isEmpty {
                remapLabelViews.forEach { $0.removeFromSuperview() }
            }
            remapLabelViews.removeAll()
        }

    }

    // MARK: settings
    func loadSettings() {
        let path = getSettingFilePath()
        if let dic = NSDictionary(contentsOfFile: path) {
            _bios = ObjCBool((dic["builtin bios"] as? Bool) ?? false)
            _cart = Int32((dic["cartridge"] as? Int) ?? 0)
            _fps = ObjCBool((dic["show fps"] as? Bool) ?? false)
            _frame_skip = ObjCBool((dic["frame skip"] as? Bool) ?? false)
            _aspect_rate = ObjCBool((dic["keep aspect rate"] as? Bool) ?? false)
            _filter = Int32(0)
            _sound_engine = Int32((dic["sound engine"] as? Int) ?? 0)
            _rendering_resolution = Int32((dic["rendering resolution"] as? Int) ?? 0)
            _rotate_screen = ObjCBool((dic["rotate screen"] as? Bool) ?? false)
            isAnalogAsDpad = (dic["analog as dpad"] as? Bool) ?? false
            setAnalogMode(to: (dic["analog mode"] as? Bool) ?? false )
        }

        let ud = UserDefaults.standard
        let defaults: [String: Any] = [
            "controller scale": "0.8",
            "landscape": true
        ]
        ud.register(defaults: defaults)

        _controller_scale = ud.float(forKey: "controller scale")
         self._landscape = ud.bool(forKey: "landscape")


    }

    func getSettingFilePath() -> String {
        let fileManager = FileManager.default
        let fileName = "settings.plist"

        let paths = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
        let libraryDirectory = paths[0]

        let filePath = (libraryDirectory as NSString).appendingPathComponent(fileName)
        print("full path name: \(filePath)")

        // check if file exists
        if fileManager.fileExists(atPath: filePath) {
            print("File exists")
        } else {
            let bundle = Bundle.main
            if let path = bundle.path(forResource: "settings", ofType: "plist") {
                do {
                    try fileManager.copyItem(atPath: path, toPath: filePath)
                } catch {
                    print("Error copying file: \(error)")
                }
            }
        }

        return filePath
    }

    func onChangeTouchPos(x: UInt8, y: UInt8) {
        if( isAnalogMode ){
            PerAxisValue(PERANALOG_AXIS_X, x); // from 0 to 255
            PerAxisValue(PERANALOG_AXIS_Y, y); // from 0 to 255
        }
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//        super.touchesBegan(touches, with: event)
        for touch in touches {
            for btnindex in 0..<PadButtons.last.rawValue {
                if let target = padButtons[Int(btnindex)].target {
                    let point = touch.location(in: target)
                    if target.bounds.contains(point) {
                        padButtons[Int(btnindex)].on(touch)
//                        print("PAD:\(btnindex) on")
                    } else {
                        // print("\(btnindex): [\(Int(target.frame.origin.x)),\(Int(target.frame.origin.y)),\(Int(target.bounds.size.width)),\(Int(target.bounds.size.height))]-[\(Int(point.x)),\(Int(point.y))]")
                    }
                }
            }
        }

        if hasControllerConnected() { return }

        var eventCount = 0
        var startIndex = 0
        if isAnalogMode {
            startIndex = 4
        }
        for btnindex in startIndex..<PadButtons.last.rawValue {
            if padButtons[Int(btnindex)].isOn() {
                PerKeyDown(UInt32(btnindex))
                eventCount += 1
            } else {
                PerKeyUp(UInt32(btnindex))
            }
        }

        if( eventCount > 0 ) {
            switch UIDevice.current.feedbackSupportLevel
            {
            case .feedbackGenerator:
                self.feedbackGenerator.impactOccurred()
            case .basic, .unsupported:
                UIDevice.current.vibrate()
            }
        }


    }

    override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        var handleEvent = 0

        for press in presses {
            guard let key = press.key else { continue }

            switch key.charactersIgnoringModifiers {
            case UIKeyCommand.inputLeftArrow:
                PerKeyDown(UInt32(PadButtons.left.rawValue))
                handleEvent += 1
            case UIKeyCommand.inputRightArrow:
                PerKeyDown(UInt32(PadButtons.right.rawValue))
                handleEvent += 1
            case UIKeyCommand.inputUpArrow:
                PerKeyDown(UInt32(PadButtons.up.rawValue))
                handleEvent += 1
            case UIKeyCommand.inputDownArrow:
                PerKeyDown(UInt32(PadButtons.down.rawValue))
                handleEvent += 1
            case "\r":
                PerKeyDown(UInt32(PadButtons.start.rawValue))
                handleEvent += 1
            case "z":
                PerKeyDown(UInt32(PadButtons.a.rawValue))
                handleEvent += 1
            case "x":
                PerKeyDown(UInt32(PadButtons.b.rawValue))
                handleEvent += 1
            case "c":
                PerKeyDown(UInt32(PadButtons.c.rawValue))
                handleEvent += 1
            case "a":
                PerKeyDown(UInt32(PadButtons.x.rawValue))
                handleEvent += 1
            case "s":
                PerKeyDown(UInt32(PadButtons.y.rawValue))
                handleEvent += 1
            case "d":
                PerKeyDown(UInt32(PadButtons.z.rawValue))
                handleEvent += 1
            case "q":
                PerKeyDown(UInt32(PadButtons.leftTrigger.rawValue))
                handleEvent += 1
            case "e":
                PerKeyDown(UInt32(PadButtons.rightTrigger.rawValue))
                handleEvent += 1
            default:
                break
            }
        }

        if handleEvent == 0 {
            super.pressesBegan(presses, with: event)
        }
    }

    override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        var handleEvent = 0

        for press in presses {
            guard let key = press.key else { continue }

            switch key.charactersIgnoringModifiers {
            case UIKeyCommand.inputLeftArrow:
                PerKeyUp(UInt32(PadButtons.left.rawValue))
                handleEvent += 1
            case UIKeyCommand.inputRightArrow:
                PerKeyUp(UInt32(PadButtons.right.rawValue))
                handleEvent += 1
            case UIKeyCommand.inputUpArrow:
                PerKeyUp(UInt32(PadButtons.up.rawValue))
                handleEvent += 1
            case UIKeyCommand.inputDownArrow:
                PerKeyUp(UInt32(PadButtons.down.rawValue))
                handleEvent += 1
            case "\r":
                PerKeyUp(UInt32(PadButtons.start.rawValue))
                handleEvent += 1
            case "z":
                PerKeyUp(UInt32(PadButtons.a.rawValue))
                handleEvent += 1
            case "x":
                PerKeyUp(UInt32(PadButtons.b.rawValue))
                handleEvent += 1
            case "c":
                PerKeyUp(UInt32(PadButtons.c.rawValue))
                handleEvent += 1
            case "a":
                PerKeyUp(UInt32(PadButtons.x.rawValue))
                handleEvent += 1
            case "s":
                PerKeyUp(UInt32(PadButtons.y.rawValue))
                handleEvent += 1
            case "d":
                PerKeyUp(UInt32(PadButtons.z.rawValue))
                handleEvent += 1
            case "q":
                PerKeyUp(UInt32(PadButtons.leftTrigger.rawValue))
                handleEvent += 1
            case "e":
                PerKeyUp(UInt32(PadButtons.rightTrigger.rawValue))
                handleEvent += 1
            default:
                break
            }
        }

        if handleEvent == 0 {
            super.pressesEnded(presses, with: event)
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

        var eventCount = 0

        for touch in touches {
            if padButtons[Int(PadButtons.down.rawValue)].isOn() && padButtons[Int(PadButtons.down.rawValue)].getPointId() == touch {
                if let target = padButtons[Int(PadButtons.down.rawValue)].target {
                    let point = touch.location(in: target)
                    if point.y < 0 {
                        padButtons[Int(PadButtons.down.rawValue)].off()
                    }
                }
            }

            if padButtons[Int(PadButtons.up.rawValue)].isOn() && padButtons[Int(PadButtons.up.rawValue)].getPointId() == touch {
                if let target = padButtons[PadButtons.up.rawValue].target {
                    let point = touch.location(in: target)
                    if point.y > target.bounds.size.height {
                        padButtons[PadButtons.up.rawValue].off()
                    }
                }
            }

            if let target = padButtons[PadButtons.down.rawValue].target {
                let point = touch.location(in: target)
                if target.bounds.contains(point) {

                    if( !isAnalogMode && padButtons[Int(PadButtons.down.rawValue)].isOn() == false ){
                        eventCount += 1
                    }

                    padButtons[PadButtons.down.rawValue].on(touch)
                }
            }

            if let target = padButtons[PadButtons.up.rawValue].target {
                let point = touch.location(in: target)
                if target.bounds.contains(point) {

                    if( !isAnalogMode && padButtons[Int(PadButtons.up.rawValue)].isOn() == false ){
                        eventCount += 1
                    }

                    padButtons[PadButtons.up.rawValue].on(touch)
                }
            }

            if padButtons[PadButtons.right.rawValue].isOn() && padButtons[PadButtons.right.rawValue].getPointId() == touch {
                if let target = padButtons[PadButtons.right.rawValue].target {
                    let point = touch.location(in: target)
                    if point.x < 0 {
                        padButtons[PadButtons.right.rawValue].off()
                    }
                }
            }

            if padButtons[PadButtons.left.rawValue].isOn() && padButtons[PadButtons.left.rawValue].getPointId() == touch {
                if let target = padButtons[PadButtons.left.rawValue].target {
                    let point = touch.location(in: target)
                    if point.x > target.bounds.size.width {
                        padButtons[PadButtons.left.rawValue].off()
                    }
                }
            }

            if let target = padButtons[PadButtons.left.rawValue].target {
                let point = touch.location(in: target)
                if target.bounds.contains(point) {

                    if( !isAnalogMode && padButtons[Int(PadButtons.left.rawValue)].isOn() == false ){
                        eventCount += 1
                    }


                    padButtons[PadButtons.left.rawValue].on(touch)
                }
            }

            if let target = padButtons[PadButtons.right.rawValue].target {
                let point = touch.location(in: target)
                if target.bounds.contains(point) {

                    if( !isAnalogMode && padButtons[Int(PadButtons.right.rawValue)].isOn() == false ){
                        eventCount += 1
                    }


                    padButtons[PadButtons.right.rawValue].on(touch)
                }
            }

            for btnindex in PadButtons.rightTrigger.rawValue..<PadButtons.last.rawValue {
                if let target = padButtons[btnindex].target {
                    let point = touch.location(in: target)

                    if padButtons[btnindex].getPointId() == touch {
                        if !target.bounds.contains(point) {
                            padButtons[btnindex].off()
                        }
                    } else {
                        if target.bounds.contains(point) {
                            padButtons[btnindex].on(touch)
                        }
                    }
                }
            }
        }

        if hasControllerConnected() { return }

        var startIndex = 0
        if isAnalogMode {
            startIndex = 4
        }
        for btnindex in startIndex..<PadButtons.last.rawValue {
            if padButtons[btnindex].isOn() {
                PerKeyDown(UInt32(btnindex))
            } else {
                PerKeyUp(UInt32(btnindex))
            }
        }


        if( self.isAnalogMode ){
            if( padButtons[PadButtons.leftTrigger.rawValue].isOn() ){
                PerAxisValue(PERANALOG_AXIS_LTRIGGER, 255);
            }else{
                PerAxisValue(PERANALOG_AXIS_LTRIGGER, 0);
            }
            if( padButtons[PadButtons.rightTrigger.rawValue].isOn() ){
                PerAxisValue(PERANALOG_AXIS_RTRIGGER, 255);
            }else{
                PerAxisValue(PERANALOG_AXIS_RTRIGGER, 0);
            }
        }


        if( eventCount > 0 ) {
            switch UIDevice.current.feedbackSupportLevel
            {
            case .feedbackGenerator:
                self.feedbackGenerator.impactOccurred()
            case .basic, .unsupported:
                UIDevice.current.vibrate()
            }
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
            for btnindex in 0..<PadButtons.last.rawValue {
                if padButtons[btnindex].isOn() && padButtons[btnindex].getPointId() == touch {
                    padButtons[btnindex].off()
                    // print("touchesEnded PAD:\(btnindex) Up")
                    if isKeyRemappingMode {
                        showRemapControlAlert(withSaturnKey: SaturnKey(rawValue: btnindex) ?? .none)
                    }
                }
            }
        }

        if hasControllerConnected() { return }

        var startIndex = 0
        if isAnalogMode {
            startIndex = 4
        }
        for btnindex in startIndex..<PadButtons.last.rawValue {
            if padButtons[btnindex].isOn() {
                PerKeyDown(UInt32(btnindex))
            } else {
                PerKeyUp(UInt32(btnindex))
            }
        }
    }

    func showRemapControlAlert(withSaturnKey saturnKey: SaturnKey) {
        let keyDescription = GameViewController.saturnKeyDescriptions[saturnKey] ?? ""
        remapAlertController = UIAlertController(
            title: NSLocalizedString("Remap Key", comment: "Title for remapping a key"),
            message: String(format: NSLocalizedString("Press a button to map the [%@] key", comment: "Message prompting user to press a button to map a key"), keyDescription),
            preferredStyle: .alert
        )


        let cancel = UIAlertAction(
            title: NSLocalizedString("Cancel", comment: "Action to cancel the key remapping"),
            style: .cancel
        ) { [weak self] _ in
            self?.isKeyRemappingMode = false
            self?.setControllerOverlayHidden(true)
            self?.remapAlertController?.dismiss(animated: true, completion: nil)
            self?.currentlyMappingKey = .none
        }

        let unbind = UIAlertAction(
            title: NSLocalizedString("Unbind", comment: "Action to unbind the current key"),
            style: .default
        ) { [weak self] _ in
            self?.keyMapper.unmapKey(saturnKey)
            self?.currentlyMappingKey = .none
            self?.refreshViewsWithKeyRemaps()
            self?.remapAlertController?.dismiss(animated: true, completion: nil)
        }

        isKeyRemappingMode = true
        remapAlertController?.addAction(cancel)
        remapAlertController?.addAction(unbind)
        currentlyMappingKey = saturnKey
        present(remapAlertController!, animated: true, completion: nil)
    }

    func toggleControllSetting(){
        if(hasControllerConnected()){
            startKeyRemapping()
        }else{
            presentPadSettingsViewController()
        }
    }

    func presentPadSettingsViewController() {
        let configuration = PadConfiguration.load() ?? .default
        let padSettingsVC = PadSettingsViewController(configuration: configuration) { [weak self] newConfig in
            // Update the pad view with new configuration
            self?.yabausePadView?.updateConfiguration(newConfig)
        }
        let navController = UINavigationController(rootViewController: padSettingsVC)
        navController.modalPresentationStyle = UIModalPresentationStyle.fullScreen
        present(navController, animated: true)
        gdelegate?.didPresentModal(navController)
    }

    func presentFileSelectViewController() {

        let fsVC = FileSelectController()
        fsVC.isStandalone = true
        fsVC.completionHandler = { data in
            if let data = data {

                self.command = MSG_OPEN_TRAY
                self.isPaused = false

                // OPENの処理が終わってしばらくしてから実行する
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                    self.selectedFile = data
                    if( scurrentGamePath != nil ){
                        scurrentGamePath?.deallocate()
                        scurrentGamePath = nil
                    }
                    let cString = (self.selectedFile! as NSString).utf8String!
                    let bufferSize = Int(strlen(cString)) + 1
                    scurrentGamePath = UnsafeMutablePointer<Int8>.allocate(capacity: bufferSize)
                    strncpy(scurrentGamePath, cString, bufferSize)
                    currentGamePath = UnsafePointer<CChar>(scurrentGamePath)
                    YabauseManager.shared.updateCheatCode(nil)
                    
                    // Notify RetroAchievements about media change and load new game file
                    if let raManager = RetroAchievementsManager.shared {
                        // First change the media
                        raManager.changeMedia(path: data) { success, error in
                            if success {
                                NSLog("RetroAchievements: Media change successful")
                                
                                // Then load the new game file
                                raManager.loadGameFromFile(path: data) { loadSuccess, loadError in
                                    if loadSuccess {
                                        NSLog("RetroAchievements: New game file loaded successfully")
                                    } else {
                                        NSLog("RetroAchievements: Failed to load new game file - \(loadError ?? "Unknown error")")
                                    }
                                }
                            } else {
                                NSLog("RetroAchievements: Media change failed - \(error ?? "Unknown error")")
                            }
                        }
                    }
                    
                    self.command = MSG_CLOSE_TRAY
                }

            }else{
                self.isPaused = false
            }
        }

        let navController = UINavigationController(rootViewController: fsVC)
        navController.modalPresentationStyle = .pageSheet
        present(navController, animated: true, completion: nil)
        
        // Notify delegate that modal was presented
        gdelegate?.didPresentModal(navController)

    }

    func presentBackupFileListViewController() {
        let bfVC = BackupFileListViewController()
        bfVC.completionHandler = {  data in
            self.isPaused = false
        }

        let navController = UINavigationController(rootViewController: bfVC)
        navController.modalPresentationStyle = .pageSheet
        present(navController, animated: true, completion: nil)
        
        // Notify delegate that modal was presented
        gdelegate?.didPresentModal(navController)
    }

    func presentCheatViewController() {
        let tabCheatVC = TabCheatViewController.newInstance(gameId: YSGetCurrentGameCode(), currentCheatCodes: nil)
        tabCheatVC.completionHandler = {
            self.isPaused = false
        }
        let navController = UINavigationController(rootViewController: tabCheatVC)
        navController.modalPresentationStyle = .pageSheet
        present(navController, animated: true, completion: nil)
        
        // Notify delegate that modal was presented
        gdelegate?.didPresentModal(navController)
    }

    func presentLeaderBoardViewController() {
        let gameCode = YSGetCurrentGameCode() ?? "SATURNAPP"
        let leaderBoardVC = LeaderBoardController.newInstance(gameCode: gameCode)
        leaderBoardVC.closeDelegate = self
        let navController = UINavigationController(rootViewController: leaderBoardVC)
        navController.modalPresentationStyle = .pageSheet
        present(navController, animated: true, completion: nil)
        
        // Notify delegate that modal was presented
        gdelegate?.didPresentModal(navController)
    }

    // MARK: - LeaderBoardControllerDelegate

    func onLeaderboardClose() {
        self.isPaused = false
    }

    // MARK: - GameUiEvent

    func onNewRecord(leaderBoardId: String) {
        // 新記録が出たときの処理
        DispatchQueue.main.async {
            let alert = UIAlertController(
                title: NSLocalizedString("New Record!", comment: "New record alert title"),
                message: NSLocalizedString("You've set a new record!", comment: "New record alert message"),
                preferredStyle: .alert
            )
            alert.addAction(UIAlertAction(
                title: NSLocalizedString("OK", comment: "OK button"),
                style: .default
            ))
            self.present(alert, animated: true)
        }
    }

    // MARK: - Game Detection

    func detectGame() {
        // ゲームコードを取得
        guard let gameCode = YSGetCurrentGameCode() else { return }
        self.detectGame(productNumber: gameCode)
    }

    func detectGame(productNumber: String) {
        // productNumberに基づいてゲームを検出
        switch productNumber {
        case "MK-81800", "GS-9170": // Sonic R
            currentGame = SonicR(gameCode: productNumber)
            currentGame?.setUiEvent(self)
            currentGame?.initGameId(productNumber)
            print("Detected game: Sonic R")
        case "GS-9047", "MK-81207", "GS-9116", "MK-81215": // Sega Rally
            currentGame = SegaRally(gameCode: productNumber)
            currentGame?.setUiEvent(self)
            currentGame?.initGameId(productNumber)
            print("Detected game: Sega Rally")
        default:
            // Create OtherGame instance for RetroAchievements support
            // This ensures Firebase leaderboard submission works for all games
            currentGame = OtherGame(gameCode: productNumber)
            currentGame?.setUiEvent(self)
            print("Created OtherGame instance for RetroAchievements support: \(productNumber)")
        }
        
        // Notify RetroAchievements manager about the current game
        RetroAchievementsManager.shared?.setCurrentGame(currentGame)
    }

    // MARK: - RetroAchievements Integration

    private func initializeRetroAchievements() {
        // Initialize RetroAchievements Manager
        RetroAchievementsManager.initialize()
        
        // Initialize native layer
        YabauseRA_Initialize()
        
        // Setup notification system
        RetroAchievementsNotificationView.setViewController(self)
        
        print("RetroAchievements system initialized")
        
        // Auto-login is now handled in AppDelegate at app startup
    }
    
    private func loadGameForRetroAchievementsWhenReady() {
        // Check login status with retry mechanism
        var retryCount = 0
        let maxRetries = 10
        let retryInterval: TimeInterval = 0.5
        
        func checkLoginAndLoad() {
            guard let manager = RetroAchievementsManager.shared else {
                if retryCount < maxRetries {
                    retryCount += 1
                    DispatchQueue.main.asyncAfter(deadline: .now() + retryInterval) {
                        checkLoginAndLoad()
                    }
                } else {
                    print("RetroAchievements: Manager not initialized after \(maxRetries) retries")
                }
                return
            }
            
            // Check if user is logged in
            if manager.isUserLoggedIn {
                // User is logged in, load the game
                self.loadGameForRetroAchievements()
            } else if retryCount < maxRetries {
                // Not logged in yet, retry
                retryCount += 1
                print("RetroAchievements: Waiting for login status... (attempt \(retryCount)/\(maxRetries))")
                DispatchQueue.main.asyncAfter(deadline: .now() + retryInterval) {
                    checkLoginAndLoad()
                }
            } else {
                print("RetroAchievements: User not logged in after \(maxRetries) retries, skipping game load")
            }
        }
        
        // Start checking
        checkLoginAndLoad()
    }
    
    private func loadGameForRetroAchievements() {
        guard let manager = RetroAchievementsManager.shared else { 
            print("RetroAchievements: Manager not initialized")
            return 
        }
        
        // Check if user is logged in
        guard manager.isUserLoggedIn else {
            print("RetroAchievements: User not logged in, skipping game load")
            return
        }
        
        // Get the game path from the native layer
        let gamePath = GetGamePath()
        if let path = gamePath {
            let pathString = String(cString: path)
            print("RetroAchievements: Loading game from path: \(pathString)")
            
            // Use the new file-based loading method
            manager.loadGameFromFile(path: pathString) { success, error in
                if success {
                    print("RetroAchievements: Game session started successfully")
                } else {
                    print("RetroAchievements: Failed to start game session: \(error ?? "Unknown error")")
                    
                    // Fallback to hash-based loading
                    if let hashString = manager.getCurrentGameHash() {
                        print("RetroAchievements: Trying fallback with hash: \(hashString)")
                        
                        manager.loadGame(hash: hashString) { success, error in
                            if success {
                                print("RetroAchievements: Game data loaded successfully (fallback)")
                            } else {
                                print("RetroAchievements: Failed to load game data: \(error ?? "Unknown error")")
                            }
                        }
                    } else {
                        print("RetroAchievements: No game hash available for fallback")
                    }
                }
            }
        } else {
            print("RetroAchievements: No game path available")
        }
    }
    
    // MARK: - Deinitialization
    deinit {
        // Clean up RetroAchievements notification system
        RetroAchievementsNotificationView.cleanup()
        print("GameViewController deinitialized")
    }
}

// MARK: - Backup Write Callback

// 再帰的にGameMainViewControllerを探すヘルパー関数
func findGameMainViewController(from root: UIViewController?) -> GameMainViewController? {
    if let gameMainVC = root as? GameMainViewController {
        return gameMainVC
    }
    for child in root?.children ?? [] {
        if let found = findGameMainViewController(from: child) {
            return found
        }
    }
    if let presented = root?.presentedViewController {
        return findGameMainViewController(from: presented)
    }
    return nil
}

@_cdecl("YSOnBackupWrite")
public func YSOnBackupWrite(fname: UnsafePointer<CChar>, deviceId: Int32, before: UnsafeRawPointer, after: UnsafeRawPointer, size: Int32) {
    guard let rootVC = UIApplication.shared.windows.first?.rootViewController,
          let mainViewController = findGameMainViewController(from: rootVC),
          let gameViewController = mainViewController.children.first(where: { $0 is GameViewController }) as? GameViewController else {
        return
    }

    // 初回実行時にゲームを検出
    if gameViewController.currentGame == nil {
        gameViewController.detectGame()
    }

    // ファイル名をStringに変換
    let fileName = String(cString: fname)

    // バックアップデータをData型に変換
    let beforeData = Data(bytes: before, count: Int(size))
    let afterData = Data(bytes: after, count: Int(size))

    // ゲームのonBackUpUpdatedメソッドを呼び出す
    gameViewController.currentGame?.onBackUpUpdated(fname: fileName, before: beforeData, after: afterData)
}

