//
//  MapBitmap.swift
//  map_preprocessor
//
//  Created by Dan Crosby on 6/7/19.
//  Copyright © 2019 Dan Crosby. All rights reserved.
//

import Foundation
import Cocoa

func MapBitmap(bitmap: NSBitmapImageRep, provincesByColor: Dictionary<Int, Province>) -> [UInt8] {
    var idMap = [UInt8](repeating: 0, count: width * height)

    let width = Int(bitmap.size.width)
    let height = Int(bitmap.size.height)
    let totalSize = width * height

    let batchSize = 256
    let group = DispatchGroup()

    for baseY in stride(from: 0, to: height, by: batchSize) {
        DispatchQueue.global().async(group: group) {
            for y in baseY..<min((baseY + batchSize), height) {
                for x in 0..<width {
                    let colorInt = bitmap.colorAt(x: x, y: y)!.asInt
                    if let province = provincesByColor[colorInt] {
                        idMap[y * width + x] = province.id
                    } else if colorInt == 0 {
                        idMap[y * width + x] = 0
                    } else {
                        idMap[y * width + x] = 0xFF
                    }
                }
            }
        }
    }
    group.wait()

    return idMap;
}

func MapBitmap2(bitmap: NSBitmapImageRep, provincesByColor: Dictionary<Int, Province>) -> [UInt8] {
    var idMap = [UInt8](repeating: 0, count: width * height)

    let width = Int(bitmap.size.width)
    let height = Int(bitmap.size.height)
    let totalSize = width * height

    func processSet(_ idxs: [Int]) -> [Int] {
        var indices = idxs.shuffled()
        while !indices.isEmpty {
            let remainingCount = indices.count
            print("Remaining: \(remainingCount)")
            var newRemainingSet = [Int]()

            for index in indices {
                // rotate or randomize order
                if index % width != width-1 && idMap[index+1] != 0xFE && idMap[index+1] != 0xFF && idMap[index+1] != 0 {
                    idMap[index] = idMap[index+1]
                } else if index % width != 0 && idMap[index-1] != 0xFE && idMap[index-1] != 0xFF && idMap[index-1] != 0 {
                    idMap[index] = idMap[index-1]
                } else if index + width < totalSize && idMap[index + width] != 0xFE && idMap[index+width] != 0xFF && idMap[index+width] != 0 {
                    idMap[index] = idMap[index + width]
                } else if index > width && idMap[index - width] != 0xFE && idMap[index - width] != 0xFF && idMap[index-width] != 0 {
                    idMap[index] = idMap[index - width]
                } else {
                    newRemainingSet.append(index)
                }
            }
            indices = newRemainingSet
            if newRemainingSet.count == remainingCount {
                return newRemainingSet
            }
        }

        return indices
    }

    var remainingSet = [Int]()
    var borderSet = [Int]()
    let group = DispatchGroup()
    let batchSize = 256

    let syncQ = DispatchQueue(label: "sync")

    for baseY in stride(from: 0, to: height, by: batchSize) {
        DispatchQueue.global().async(group: group) {
            var localRemaining = [Int]()
            var localBorder = [Int]()
            for y in baseY..<min((baseY + batchSize), totalSize) {
                for x in 0..<width {
                    // do this flipping at the end
                    let index = y * width + x
                    let colorInt = bitmap.colorAt(x: x, y: y)!.asInt
                    if let province = provincesByColor[colorInt] {
                        idMap[index] = province.id
                    } else if colorInt == 0xFF {
                        localBorder.append(index)
                        /* } else if (colorInt >> 24) != 0xFF && (colorInt >> 24) != 0 {
                         idMap[index] = 0*/
                    } else if colorInt != 0 {
                        localRemaining.append(index)
                        idMap[index] = 0xFE
                    }
                }
            }

            syncQ.sync {
                remainingSet.append(contentsOf: localRemaining)
                borderSet.append(contentsOf: localBorder)
            }
        }
    }
    group.wait()

    let newRemaining = processSet(remainingSet)
    print("Finished remainingSet")

    borderSet.append(contentsOf: newRemaining)

    let unfinished = processSet(borderSet)
    print("Finished borders with \(unfinished.count) remaining")

    return idMap
}

func FlipArray<T>(array: [T], stride: Int) -> [T] {
    var newArray = [T]()
    let rows = (array.count - 1) / stride + 1
    for y in 0..<rows {
        for x in 0..<stride {
            newArray.append(array[(rows - y - 1) * stride + x])
        }
    }

    return newArray
}
