//
//  QueueBufferTests.swift
//  SwiftQueueTests
//
//  Created by Zehua Chen on 8/4/19.
//

import XCTest
@testable import SwiftQueue

final class QueueBufferTests: XCTestCase {

    // MARK: Allocation test

    func testHeadTailAllocation() {
        /// Local object used to test the number of deinitialization
        class Object {
            static var deinitCount = 0
            deinit { Object.deinitCount += 1 }
        }

        let dequeue = 2
        let enqueue = 7
        let count = 5
        let minCapacity = 10

        var buffer: _QueueBuffer<Object>? = _QueueBuffer<Object>.create(minimumCapacity: minCapacity)
        buffer!.dequeueIndex = dequeue
        buffer!.enqueueIndex = enqueue
        buffer!.count = count

        buffer!.withUnsafeMutablePointerToElements { (pointer) in
            for i in dequeue...enqueue {
                pointer.advanced(by: i).initialize(to: Object())
            }
        }

        buffer = nil

        XCTAssertEqual(Object.deinitCount, count)
    }

    func testTailHeadAllocation() {
        /// Local object used to test the number of deinitialization
        class Object {
            static var deinitCount = 0
            deinit { Object.deinitCount += 1 }
        }

        let headSize = 2
        let enqueueIndex = 3
        let count = 5
        let minCapacity = 10

        var buffer: _QueueBuffer<Object>? = _QueueBuffer<Object>.create(minimumCapacity: minCapacity)
        buffer!.dequeueIndex = buffer!.capacity - headSize
        buffer!.enqueueIndex = enqueueIndex
        buffer!.count = count

        buffer!.withUnsafeMutablePointerToElements { (pointer) in
            for i in (buffer!.capacity - headSize)...(buffer!.capacity - 1) {
                pointer.advanced(by: i).initialize(to: Object())
            }

            for i in 0..<enqueueIndex {
                pointer.advanced(by: i).initialize(to: Object())
            }
        }

        buffer = nil

        XCTAssertEqual(Object.deinitCount, count)
    }

    func testFullCircleAllocation() {
        /// Local object used to test the number of deinitialization
        class Object {
            static var deinitCount = 0
            deinit { Object.deinitCount += 1 }
        }

        let dequeue = 0
        let enqueue = 0
        let minCapacity = 10

        var buffer: _QueueBuffer<Object>? = _QueueBuffer<Object>.create(minimumCapacity: minCapacity)
        buffer!.dequeueIndex = dequeue
        buffer!.enqueueIndex = enqueue
        buffer!.count = buffer!.capacity

        buffer!.withUnsafeMutablePointerToElements { (pointer) in
            for i in 0..<buffer!.count {
                pointer.advanced(by: i).initialize(to: Object())
            }
        }

        let count = buffer!.count
        buffer = nil

        XCTAssertEqual(Object.deinitCount, count)
    }

    // MARK: Copy Test

    func testHeadTailCopy() {
        let buffer = _QueueBuffer<Int>.create(minimumCapacity: 5)
        buffer.dequeueIndex = 0
        buffer.enqueueIndex = 4
        buffer.count = 4

        buffer.withUnsafeMutablePointerToElements { (ptr) in
            for i in 0...3 {
                ptr.advanced(by: i).initialize(to: i)
            }
        }

        let cloned = buffer.copy()
        XCTAssertEqual(cloned.dequeueIndex, buffer.dequeueIndex)
        XCTAssertEqual(cloned.enqueueIndex, buffer.enqueueIndex)
        XCTAssertEqual(cloned.count, buffer.count)

        var pattern = [Int]()

        buffer.withUnsafeMutablePointerToElements { (ptr) in
            for i in 0...3 {
                pattern.append(ptr.advanced(by: i).pointee)
            }
        }

        XCTAssertEqual(pattern, [0, 1, 2, 3])
    }


    func testTailHeadCopy() {
        let headSize = 2

        let buffer = _QueueBuffer<Int>.create(minimumCapacity: 5)
        buffer.dequeueIndex = buffer.capacity - headSize
        buffer.enqueueIndex = 2
        buffer.count = 4

        buffer.withUnsafeMutablePointerToElements { (ptr) in
            ptr.advanced(by: buffer.headIndex + 0).initialize(to: 1)
            ptr.advanced(by: buffer.headIndex + 1).initialize(to: 2)

            ptr.advanced(by: buffer.tailIndex - 1).initialize(to: 3)
            ptr.advanced(by: buffer.tailIndex - 0).initialize(to: 4)
        }

        let cloned = buffer.copy()
        XCTAssertEqual(cloned.headIndex, buffer.headIndex)
        XCTAssertEqual(cloned.tailIndex, buffer.tailIndex)
        XCTAssertEqual(cloned.count, buffer.count)

        var pattern = [Int]()

        buffer.withUnsafeMutablePointerToElements { (ptr) in
            pattern.append(ptr.advanced(by: buffer.headIndex + 0).pointee)
            pattern.append(ptr.advanced(by: buffer.headIndex + 1).pointee)

            pattern.append(ptr.advanced(by: buffer.tailIndex - 1).pointee)
            pattern.append(ptr.advanced(by: buffer.tailIndex - 0).pointee)
        }

        XCTAssertEqual(pattern, [1, 2, 3, 4])
    }

    func testFullCircleCopy() {
        let buffer = _QueueBuffer<Int>.create(minimumCapacity: 5)
        buffer.dequeueIndex = 0
        buffer.enqueueIndex = 0
        buffer.count = buffer.capacity

        var expectedPattern = [Int]()

        buffer.withUnsafeMutablePointerToElements { (ptr) in
            for i in 0..<buffer.count {
                ptr.advanced(by: i).initialize(to: i)
                expectedPattern.append(i)
            }
        }

        let cloned = buffer.copy()

        XCTAssertEqual(cloned.enqueueIndex, buffer.enqueueIndex)
        XCTAssertEqual(cloned.dequeueIndex, buffer.dequeueIndex)
        XCTAssertEqual(cloned.count, buffer.count)

        var pattern = [Int]()

        cloned.withUnsafeMutablePointerToElements { (ptr) in
            for i in 0..<buffer.count {
                pattern.append(ptr.advanced(by: i).pointee)
            }
        }

        XCTAssertEqual(pattern, expectedPattern)
    }


    func testHeadTailResize() {
        let buffer = _QueueBuffer<Int>.create(minimumCapacity: 5)
        buffer.dequeueIndex = 0
        buffer.enqueueIndex = 4
        buffer.count = 4

        var expectedPattern = [Int]()

        buffer.withUnsafeMutablePointerToElements { (ptr) in
            for i in 0..<4 {
                ptr.advanced(by: i).initialize(to: i)
                expectedPattern.append(i)
            }
        }

        let resized = buffer.resize(to: 10)

        XCTAssertGreaterThanOrEqual(resized.capacity, 10)
        XCTAssertEqual(resized.dequeueIndex, 0)
        XCTAssertEqual(resized.enqueueIndex, 4)
        XCTAssertEqual(resized.count, 4)

        var pattern = [Int]()

        buffer.withUnsafeMutablePointerToElements { (ptr) in
            for i in 0..<4 {
                pattern.append(ptr.advanced(by: i).pointee)
            }
        }

        XCTAssertEqual(pattern, expectedPattern)
    }

    func testTailHeadResize() {
        let headSize = 2

        let buffer = _QueueBuffer<Int>.create(minimumCapacity: 5)
        buffer.dequeueIndex = buffer.capacity - headSize
        buffer.enqueueIndex = 2
        buffer.count = 4

        buffer.withUnsafeMutablePointerToElements { (ptr) in
            ptr.advanced(by: buffer.headIndex + 0).initialize(to: 1)
            ptr.advanced(by: buffer.headIndex + 1).initialize(to: 2)

            ptr.advanced(by: buffer.tailIndex - 1).initialize(to: 3)
            ptr.advanced(by: buffer.tailIndex - 0).initialize(to: 4)
        }

        let moved = buffer.resize(to: 10)

        XCTAssertGreaterThanOrEqual(moved.capacity, 10)
        XCTAssertEqual(moved.headIndex, 0)
        XCTAssertEqual(moved.tailIndex, 3)
        XCTAssertEqual(moved.count, 4)

        var pattern = [Int]()

        moved.withUnsafeMutablePointerToElements { (ptr) in
            for i in 0..<4 {
                pattern.append(ptr.advanced(by: i).pointee)
            }
        }

        XCTAssertEqual(pattern, [1, 2, 3, 4])
    }

    func testIndexWrapping() {
        let buffer = _QueueBuffer<Int>.create(minimumCapacity: 5)

        XCTAssertEqual(buffer.advance(index: 4, by: 1), 0)
        XCTAssertEqual(buffer.advance(index: 3, by: 1), 4)
    }

    func testHasSpace() {
        let buffer = _QueueBuffer<Int>.create(minimumCapacity: 5)
        buffer.count = 2
        buffer.dequeueIndex = 0
        buffer.enqueueIndex = 2

        XCTAssertTrue(buffer.hasSpaceFor(count: 1))

        buffer.count = buffer.capacity
        buffer.dequeueIndex = 3
        buffer.enqueueIndex = 3

        XCTAssertFalse(buffer.hasSpaceFor(count: 1))
    }
}
