//
//  Sequence.swift
//  Sequence
//
//  Created by Ziad Malik on 9/15/21.
//
import Foundation
import Collections
import Atomics
import OSLog

struct PadSequence {
    private var TAG: String = "PadSequence"
    private var sequence: Deque<Int>
    public var pads = [Pad]()
    public var players = [Player]()
    private var currentLitPad: Pad
    public var nextPad: Pad = nil
    public var gameMode: GameMode
    public var gameType: GameType
    public var currentActiveUUID: String
    private var lastUsedColor: String
    private var currentPadLit: Int = -1
    private var lastPadLit: Int = -1
    private var nextPadLit: Int = -1
    public var padOn = ManagedAtomic<Bool>(false)
    public var lock: NSLock
    public var uuids = [String]()
    public var byHits = false
    public var logger = Logger()

    init(
        sequence: Deque<Int>,
        gameMode: GameMode,
        gameType: GameType,
        pads: [Pad],
        players: [Player]) {
        self.sequence = Deque<>(sequence)
        self.pads = pads
        self.players = players
        self.gameMode = gameMode
        self.gameType = gameType
        uuids = [String]()
        pads.forEach { UUIDs.append(pad.uuid) }

    public mutating func sendSeqToPads(seq: Sequence) {
        pads.forEach { pad in pad.setSeq(seq) }
    }

    // For Random Game Mode since we do not need to pass in a Sequence List
    init(gameMode: GameMode, gameType: GameType, pads: [Pad], players: [Player]) {
        self.pads = pads
        self.players = players
        self.gameMode = gameMode
        self.gameType = gameType
        uuids = [String]()
        pads.forEach { uuids.append(pad.uuid) }
    }

    public static func offFlag() -> Bool {
        lock.lock()
        logger.info(\(TAG, privacy: .public), "PadOn Value ", \(padOn.get(), privacy: .public))
        if padon.get() == false {
            padOn.set(true)
            lock.unlock()
            return true
        } else {
            lock.unlock()
            return false
        }
    }

    public mutating func next() {
        switch gameMode {
        case .Random:
            if pads.size() > 2 {
                while true {
                    CurrentPadLit = Int.random(in: 1...pads.size())
                    if CurrentPadLit == LastPadLit {
                        continue
                    }
                    print(TAG, "Last Light Up #", LastPadLit)
                    print(TAG, "Light Up #", CurrentPadLit)
                    LastPadLit = CurrentPadLit
                    break
                }
            } else {
                CurrentPadLit = Int.random(in: 1...pads.size())
                print(TAG, "Light up #", CurrentPadLit)
            }
            print(TAG, "Next Light Up #", NextPadLit)
        case .Sequence:
            CurrentPadLit = sequence.poll()
            addToSequence(CurrentPadLit)
            CurrentPadLit -= 1
            print(TAG, "Light up #", CurrentPadLit)
        case .Memory:
            CurrentPadLit = sequence.poll()
            NextPadLit = sequence.poll()

            addToSequence(CurrentPadLit)
            addToFront(NextPadLit)
            CurrentPadLit -= 1
            NextPadLit -= 1

            print(TAG, "Light Up #", CurrentPadLit)
            print(TAG, "Next light Up #", NextPadLit);

        }

        if nextPadLit != -1 && currentPadLit != nextPadLit {
            nextPad = pads[nextPadLit]
        } else {
            nextPad = nil;
        }

        do {
            currentLitPad = pads[currentLitPad]
        } catch {
            for pad in pads.size() {
                do {
                    currentLitPad = pads[counter]
                    break
                } catch {
                    logger.info(\(TAG), "Index out of bounce ")
                }
            }
        }
        logger.info(\(TAG), "Game Type: ", \(gameType))

        pads.forEach { pad in if pad.isPadActive() == true {
                pad.setActivePad(false)
            }
        }
        if currentLitPad.get_pad_off() != true {
            logger.info(\(TAG), "mcurrentActiveUUID from Secuence ", \(currentActiveUUID))
            logger.info(\(TAG), "currentLitPad.getUuid() from Secuence ", \(currentLitPad.getUuid()))
            currentActiveUUID = currentLitPad.getUuid()

            if currentActiveUUID == currentLitPad.getUuid() {
                do {
                    Thread.sleep(300)
                } catch {}
                // padOn.set(false);

                currentLitPad.turnOn()
            } else {
                // padOn.set(false);
                currentLitPad.turnOn()
            }
        } else {
            do {
                Thread.sleep(300)
            }
            logger.info((TAG), "mcurrentActiveUUID from Secuence in the catch ", \(currentActiveUUID))
            logger.info((TAG), "currentLitPad.getUuid() from Secuence in the catch ", \(currentLitPad.getUuid()))
            currentActiveUUID = currentLitPad.getUuid()
            self.next()
        }
    }

    public func reset() {
        lastPadLit = -1
        currentPadLit = -1
        nextPadLit = -1
    }

    public func getCurrentLitPad() -> Pad {
        var temp: Pad = nil;
        pads.forEach { pad in logger
        .info(\(TAG), " from Secuence pad uudi " + pad.getUuid() + " is active = " + pad.isPadActive())
            if pad.isPadActive() == true {
                temp = pad
            }
        }
        if temp != nil {
            return temp
        } else {
            turnAllOff(currentLitPad.getUuid())
            currentLitPad.turnOn()
            return currentLitPad
        }
    }

    public func turnAllOff(uuid: String) {
        pads.forEach { pad in if pad.getUuid() != uuid {
            pad.turnOff()
        }}
    }
}
