<commit_before>//
//  GameViewController.swift
//  SwiftChip-8
//
//  Created by <NAME> on 8/12/17.
//  Copyright © 2017 <NAME>. All rights reserved.
//

import UIKit
import QuartzCore
import GLKit

class GameViewController: UIViewController {
    
    var game: URL!
    
    fileprivate var interpreter: Chip8?
    
    fileprivate var buzzer: Buzzer?
    
    private var displayLink: CADisplayLink?
    
    fileprivate var vertexBuffer = GLuint()
    
    fileprivate var colorEffect = GLKBaseEffect()
    
    @IBOutlet weak var gameView: GLKView!

    @IBOutlet var buttons: [UIButton]!
    
    deinit {
        glDeleteBuffers(1, &vertexBuffer)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = game.lastPathComponent
        setupKeyboard()
        setupChip8()
        setupBuzzer()
        setupSpeedSlider()
        setupGL()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if displayLink == nil {
            displayLink = CADisplayLink(target: self, selector: #selector(redraw(displayLink:)))
            displayLink?.add(to: .main, forMode: .defaultRunLoopMode)
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        displayLink?.remove(from: .main, forMode: .defaultRunLoopMode)
    }
    
    @IBAction func didPressButton(_ sender: UIButton) {
        interpreter?.press(button: sender.tag)
    }
    
    @IBAction func didReleaseButton(_ sender: UIButton) {
        interpreter?.release(button: sender.tag)
    }
    
    @objc private func redraw(displayLink: CADisplayLink) {
        do {
            try interpreter?.run()
        } catch {
            alert(error: error)
        }
        gameView.display()
    }
    
    @objc private func adjustSpeed(sender: UISlider) {
        interpreter?.speed = Int(sender.value)
    }
    
    private func setupKeyboard() {
        let keymap = [
            0: 1,
            1: 2,
            2: 3,
            3: 0xC,
            4: 4,
            5: 5,
            6: 6,
            7: 0xD,
            8: 7,
            9: 8,
            10: 9,
            11: 0xE,
            12: 0xA,
            13: 0,
            14: 0xB,
            15: 0xF
        ]
        
        for (index, button) in buttons.enumerated() {
            let image = UIImage(named: String(format: "%X", keymap[index]!))?.withRenderingMode(.alwaysTemplate)
            button.setImage(image, for: .normal)
            button.tintColor = SettingsManager.instance.pixelColor
            button.backgroundColor = SettingsManager.instance.backgroundColor
        }
    }
    
    private func setupChip8() {
        do {
            interpreter = try Chip8(program: game)
        } catch {
            alert(error: error)
        }
        interpreter?.delegate = self
    }
    
    private func setupBuzzer() {
        do {
            buzzer = try Buzzer(frequency: SettingsManager.instance.buzzerNote)
        } catch {
            alert(error: error)
        }
        buzzer?.volume = SettingsManager.instance.buzzerVolume
    }
    
    private func setupSpeedSlider() {
        let speedSlider = UISlider()
        speedSlider.translatesAutoresizingMaskIntoConstraints = false
        speedSlider.minimumValue = 1
        speedSlider.maximumValue = 30
        speedSlider.value = Float(Chip8.DefaultSpeed)
        speedSlider.addTarget(self, action: #selector(adjustSpeed(sender:)), for: .valueChanged)
        let speedItem = UIBarButtonItem(customView: speedSlider)
        toolbarItems?.append(speedItem)
    }
    
    private func setupGL() {
        let context = EAGLContext(api: .openGLES2)!
        EAGLContext.setCurrent(context)
        
        colorEffect.transform.projectionMatrix = GLKMatrix4MakeOrtho(0.0, Float(Chip8.DisplayWidth), 0.0, Float(Chip8.DisplayHeight), -1.0, 1.0)
        colorEffect.useConstantColor = GLboolean(GL_TRUE)
        colorEffect.constantColor = SettingsManager.instance.pixelColor.glkVector4
        
        let vertices: [Float32] = [
            -0.5, -0.5,
            0.5, 0.5,
            -0.5, 0.5,
            -0.5, -0.5,
            0.5, -0.5,
            0.5, 0.5,
        ]
        
        glGenBuffers(1, &vertexBuffer)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
        glBufferData(GLenum(GL_ARRAY_BUFFER), vertices.count * MemoryLayout<Float32>.size, vertices, GLenum(GL_STATIC_DRAW))
        
        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue))
        glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, nil)
        
        gameView.context = context
    }
    
    fileprivate func alert(error: Error) {
        var message = ""
        switch error {
        case Chip8Error.invalidOpCode(let opCode):
            message = "Invalid op code: \(opCode)"
        case Chip8Error.invalidROMFile(let url):
            message = "Invalid ROM file: \(url)"
        case BuzzerError.error(let status):
            message = "Buzzer error: \(status)"
        default:
            message = "Other error occured: \(error.localizedDescription)"
        }
        let alertController = UIAlertController(title: "Error", message: message, preferredStyle: .alert)<commit_msg>Added airplay support, still buggy<commit_after>//
//  GameViewController.swift
//  SwiftChip-8
//
//  Created by <NAME> on 8/12/17.
//  Copyright © 2017 <NAME>. All rights reserved.
//

import UIKit
import QuartzCore
import GLKit

class GameViewController: UIViewController {
    
    var game: URL!
    
    fileprivate var interpreter: Chip8?
    
    fileprivate var buzzer: Buzzer?
    
    private var displayLink: CADisplayLink?
    
    fileprivate var vertexBuffer = GLuint()
    
    fileprivate var colorEffect = GLKBaseEffect()
    
    private var secondWindow: UIWindow?
    
    private var secondGameView: GLKView?
    
    @IBOutlet weak var gameView: GLKView!

    @IBOutlet var buttons: [UIButton]!
    
    deinit {
        glDeleteBuffers(1, &vertexBuffer)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = game.lastPathComponent
        setupKeyboard()
        setupChip8()
        setupBuzzer()
        setupSpeedSlider()
        setupGL()
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if UIScreen.screens.count > 1 {
            setupSecondScreen()
        }
        
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIScreenDidConnect, object: nil, queue: OperationQueue.main) { _ in
            self.setupSecondScreen()
        }
        
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIScreenDidDisconnect, object: nil, queue: OperationQueue.main) { _ in
            self.removeSecondScreen()
        }
        
        if displayLink == nil {
            displayLink = CADisplayLink(target: self, selector: #selector(redraw(displayLink:)))
            displayLink?.add(to: .main, forMode: .defaultRunLoopMode)
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        displayLink?.remove(from: .main, forMode: .defaultRunLoopMode)
        
        if UIScreen.screens.count > 1 {
            removeSecondScreen()
        }
        
        NotificationCenter.default.removeObserver(self)
    }
    
    @IBAction func didPressButton(_ sender: UIButton) {
        interpreter?.press(button: sender.tag)
    }
    
    @IBAction func didReleaseButton(_ sender: UIButton) {
        interpreter?.release(button: sender.tag)
    }
    
    @objc private func redraw(displayLink: CADisplayLink) {
        do {
            try interpreter?.run()
        } catch {
            alert(error: error)
        }
        
        gameView.display()
        
        if let secondGameView = secondGameView {
            secondGameView.display()
        }
    }
    
    @objc private func adjustSpeed(sender: UISlider) {
        interpreter?.speed = Int(sender.value)
    }
    
    private func setupKeyboard() {
        let keymap = [
            0: 1,
            1: 2,
            2: 3,
            3: 0xC,
            4: 4,
            5: 5,
            6: 6,
            7: 0xD,
            8: 7,
            9: 8,
            10: 9,
            11: 0xE,
            12: 0xA,
            13: 0,
            14: 0xB,
            15: 0xF
        ]
        
        for (index, button) in buttons.enumerated() {
            let image = UIImage(named: String(format: "%X", keymap[index]!))?.withRenderingMode(.alwaysTemplate)
            button.setImage(image, for: .normal)
            button.tintColor = SettingsManager.instance.pixelColor
            button.backgroundColor = SettingsManager.instance.backgroundColor
        }
    }
    
    private func setupChip8() {
        do {
            interpreter = try Chip8(program: game)
        } catch {
            alert(error: error)
        }
        interpreter?.delegate = self
    }
    
    private func setupBuzzer() {
        do {
            buzzer = try Buzzer(frequency: SettingsManager.instance.buzzerNote)
        } catch {
            alert(error: error)
        }
        buzzer?.volume = SettingsManager.instance.buzzerVolume
    }
    
    private func setupSpeedSlider() {
        let speedSlider = UISlider()
        speedSlider.translatesAutoresizingMaskIntoConstraints = false
        speedSlider.minimumValue = 1
        speedSlider.maximumValue = 30
        speedSlider.value = Float(Chip8.DefaultSpeed)
        speedSlider.addTarget(self, action: #selector(adjustSpeed(sender:)), for: .valueChanged)
        let speedItem = UIBarButtonItem(customView: speedSlider)
        toolbarItems?.append(speedItem)
    }
    
    private func setupGL() {
        let context = EAGLContext(api: .openGLES2)!
        EAGLContext.setCurrent(context)
        
        colorEffect.transform.projectionMatrix = GLKMatrix4MakeOrtho(0.0, Float(Chip8.DisplayWidth), 0.0, Float(Chip8.DisplayHeight), -1.0, 1.0)
        colorEffect.useConstantColor = GLboolean(GL_TRUE)
        colorEffect.constantColor = SettingsManager.instance.pixelColor.glkVector4
        
        let vertices: [Float32] = [
            -0.5, -0.5,
            0.5, 0.5,
            -0.5, 0.5,
            -0.5, -0.5,
            0.5, -0.5,
            0.5, 0.5,
        ]
        
        glGenBuffers(1, &vertexBuffer)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
        glBufferData(GLenum(GL_ARRAY_BUFFER), vertices.count * MemoryLayout<Float32>.size, vertices, GLenum(GL_STATIC_DRAW))
        
        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue))
        glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, nil)
        
        gameView.context = context
    }
    
    private func setupSecondScreen() {
        guard secondWindow == nil && secondGameView == nil else {
            return
        }
        
        let secondScreen = UIScreen.screens[1]
        
        secondWindow = UIWindow(frame: secondScreen.bounds)
        guard let secondWindow = secondWindow else {
            return
        }
        
        secondWindow.screen = secondScreen
        
        secondGameView = GLKView(frame: secondWindow.bounds, context: gameView.context)
        guard let secondGameView = secondGameView else {
            return
        }
        
        secondGameView.delegate = self
        secondWindow.addSubview(secondGameView)
        
        secondWindow.isHidden = false
    }
    
    private func removeSecondScreen() {
        secondGameView?.removeFromSuperview()
        secondGameView = nil
        secondWindow?.isHidden = true
        secondWindow = nil
    }
    
    fileprivate func alert(error: Error) {
        var message = ""
        switch error {
        case Chip8Error.invalidOpCode(let opCode):
            message = "Invalid op code: \(opCode)"
        case Chip8Error.invalidROMFile(let url):
            message = "Invalid ROM file: \(url)"
        case BuzzerError.error(let status):
            message = "Buzzer error: \(status)"
        default:
            message = "Other error occured: \(error.localizedDescription)"
        }
        let alertController = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
{'fraction_non_alphanumeric': 0.0939873417721519, 'fraction_numerical': 0.01653481012658228, 'mean_word_length': 1.9535046728971963, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6522546', 'n_tokens_mistral': 3779, 'n_tokens_neox': 3663, 'n_words': 763}