import SwiftUI
import Foundation
import OSLog
import Atomics

public class Game: Thread {
    public var tag: String = "Game"
    public var logger = Logger()
    public var isHit: Bool = false
    public var gameTime: Double
    public var isSleeping: Bool = false
    public var byHits: Bool = false
    public var lightUpTime: Double
    public var count: Int = 0
    public var startGame: Double
    public let lock = NSLock()
    public var padLightUpTime: Int
    public var padLightUpTimeDelay: Int
    public var sequence: Sequence

    init(sequence: Sequence, gameTime: Double, padLightUpTime: Int, padLightUpTimeDelay: Int) {
        self.gameTime = gameTime
        self.padLightUpTime = padLightUpTime
        self.padLightUpTimeDelay = padLightUpTimeDelay
        self.sequence = sequence
        self.startGame = CACurrentMediaTime()
        lightUpTime = 0.0
    }

    func returnlightUpTime() -> Double {
        if isSleeping {
            logger.debug("\(self.tag), we are getting time while sleeping")
        }
        return lightUpTime
    }

    override public func cancel() {
        var gameOver = ManagedAtomic<Bool>(true)
        super.cancel()
        for pad in sequence.getPads() {
            pad.turn0ff()
            pad.endGame()
            logger.debug("\(self.tag), Sending game over")
        }
    }

    override public func main() {
        var gameOver = ManagedAtomic<Bool>(false)
        for pad in sequence.getPads() {
            pad.turn0ff()
            pad.startGame() //send a game is starting so start sensor readding
        }
        let startGame: Double = CACurrentMediaTime()
        if byHits {
            while (!Thread.current.isExecuting) {
                playGame(startTime: startGame)
            }
            logger.debug("\(self.tag), Sending run interrupt/cancel")
            Thread.current.cancel() //Interrupt/cancel after the  of byHits game mode
        }
        else {
            while (gameTime > CACurrentMediaTime() - startGame && !Thread.current.isExecuting) {
                let startT: Double = CACurrentMediaTime()
                playGame(startTime: startGame)
            }
        }
    }

    public func playGame(startTime: Double) {
        lock.lock()
        count += 1
        isHit = false
        isSleeping = true
        sequence.next()
        lightUpTime = CACurrentMediaTime()
        isSleeping = false
        if (padLightUpTime != 0) {
            while(Double(padLightUpTime) > (CACurrentMediaTime() - lightUpTime) && !isHit
                    && !Thread.current.isExecuting){
                if (!(gameTime > CACurrentMediaTime() - startTime) && !Thread.current.isExecuting
                        && !byHits){
                    Thread.current.cancel()
                }
            }
            isSleeping = true
            if(!isHit){
                logger.debug("\(self.tag), Failed to get a hit2")
                for player in sequence.getPlayers() {
                    player.addMiss(CACurrentMediaTime()-startGame,sequence.getCurrentLitPad().getUuid())
                    player.removePoints()
                }
                sequence.getCurrentLitPad().turnOff()
            }
        }
        else {
            while(!isHit) {
                if(!(gameTime > CACurrentMediaTime()) && !Thread.current.isExecuting && !byHits){
                    Thread.current.cancel()
                }
            }
        }
        if (sequence.getNextLitPad() != nil) {
            sequence.getNextLitPad().turnOff()
        }
        if (padLightUpTimeDelay != 0) {
            let startLightUpDelayTime: Double = CACurrentMediaTime()
            while(Double(padLightUpTimeDelay) > (CACurrentMediaTime() - startLightUpDelayTime)){
                if(!(gameTime > CACurrentMediaTime() - startTime) && !Thread.current.isExecuting && !byHits){
                    Thread.current.cancel()
                }
            }
        }
        isSleeping = false
        lock.unlock()
    }
}

