//
//  CPTimelineStatusMessageTests.swift
//  CorePacerTests
//
//  Created by Sam Smallman on 22/04/2021.
//  Copyright (c) 2021 Sam Smallman
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in all
//  copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//  SOFTWARE.

import XCTest
@testable import CorePacer

final class CPTimelineStatusMessageTests: XCTestCase {
    
    func testInitWithValidData() throws {
        let data = validData()
        let timelineStatusMessage = try CPTimelineStatusMessage(data: data)
        
        XCTAssertEqual(timelineStatusMessage.timelineID, 1337)
        XCTAssertEqual(timelineStatusMessage.cpTimelineStatus, CPTimelineStatus.standby)
        XCTAssertEqual(timelineStatusMessage.cpTimelineTimecodeHint, CPTimelineTimecodeHint.none)
        XCTAssertEqual(timelineStatusMessage.referenceClock, CPTimestamp.example)
        XCTAssertEqual(timelineStatusMessage.offset, CPTimestamp.example)
        XCTAssertEqual(timelineStatusMessage.timestamp, CPTimestamp.example)

    }
    
    func testInvalidDataSize() throws {
        let seconds: UInt64 = 34860
        let data = Data(seconds.data)
        
        assert(try CPTimelineStatusMessage(data: data), throws: CPTimelineStatusMessage.Error.invalidDataSize)
    }
    
    func testInvalidTimelineStatus() throws {
        assert(try CPTimelineStatusMessage(data: validData(invalidTimelineStatus: true)), throws: CPTimelineStatusMessage.Error.invalidTimelineStatus)
    }

    func testInvalidTimelineTimecodeHint() throws {
        assert(try CPTimelineStatusMessage(data: validData(invalidTimelineTimecodeHint: true)), throws: CPTimelineStatusMessage.Error.invalidTimelineTimecodeHint)
    }
    
    func testRawDescription() {
        let timelineStatusMessage = CPTimelineStatusMessage.example

        XCTAssertEqual(timelineStatusMessage.rawDescription, "Timeline ID: 1337, Status: 1, Hint: 0, Reference Clock: 34860+0/1000, Offset: 34860+0/1000, Timestamp: 34860+0/1000")
    }
    
    func testDescription() {
        let timelineStatusMessage = CPTimelineStatusMessage.example

        XCTAssertEqual(timelineStatusMessage.description, "Timeline ID: 1337, Status: Standby, Hint: No Hint, Reference Clock: 0009:41:00+0000/1000, Offset: 0009:41:00+0000/1000, Timestamp: 0009:41:00+0000/1000")
    }
    
    func testComputedProperties() throws {
        let data = validData()
        let timelineStatusMessage = try CPTimelineStatusMessage(data: data)

        XCTAssertEqual(timelineStatusMessage.timelineStatus, CPTimelineStatus.standby.rawValue)
        XCTAssertEqual(timelineStatusMessage.timelineStatusString, "Standby")
        XCTAssertEqual(timelineStatusMessage.timelineTimecodeHint, CPTimelineTimecodeHint.none.rawValue)
        XCTAssertEqual(timelineStatusMessage.timelineTimecodeHintString, "No Hint")
        XCTAssertEqual(timelineStatusMessage.data, data)
    }
    
    /// Creates a `CPTimelineStatusMessage` data packet.
    ///
    /// A valid `CPTimelineStatusMessage` packet contains the following:
    ///   - `UInt32`: - `<TimelineNumericID>`
    ///   - `UInt8`: - `<TimelineStatus>`
    ///   - `UInt8`: - `<TimecodeHintFlag>`
    ///   - `UInt16`: - `<Reserved>`
    ///   - `UInt64`: - `<ReferenceClockSeconds>`
    ///   - `UInt32`: - `<Reserved>`
    ///   - `UInt32`: - `<ReferenceClockFrational>`
    ///   - `UInt32`: - `<ReferenceClockTimescale>`
    ///   - `UInt64`: - `<ReferenceOffsetSeconds>`
    ///   - `UInt32`: - `<Reserved>`
    ///   - `UInt32`: - `<ReferenceOffsetFrational>`
    ///   - `UInt32`: - `<ReferenceOffsetTimescale>`
    ///   - `UInt64`: - `<TimestampSeconds>`
    ///   - `UInt32`: - `<Reserved>`
    ///   - `UInt32`: - `<TimestampFrational>`
    ///   - `UInt32`: - `<TimestampTimescale>`
    /// - Returns: A byte buffer in memory representing a `CPTimelineStatusMessage`.
    private func validData(invalidTimelineStatus: Bool = false, invalidTimelineTimecodeHint: Bool = false) -> Data {
        let timelineId: UInt32 = 1337
        let timelineStatus = invalidTimelineStatus ? 255 : CPTimelineStatus.standby.rawValue
        let timelineTimecodHint = invalidTimelineTimecodeHint ? 255 : CPTimelineTimecodeHint.none.rawValue
        let referenceClock = CPTimestamp.example
        let referenceOffset = CPTimestamp.example
        let timestamp = CPTimestamp.example
        var data = Data()
        data.append(timelineId.bigEndian.data)
        data.append(timelineStatus)
        data.append(timelineTimecodHint)
        data.append(Data(repeating: 0, count: 2))
        data.append(referenceClock.data)
        data.append(referenceOffset.data)
        data.append(timestamp.data)
        return data
    }
    
    static var allTests = [
        ("testInitWithValidData", testInitWithValidData),
        ("testInvalidDataSize", testInvalidDataSize),
        ("testInvalidTimelineStatus", testInvalidTimelineStatus),
        ("testInvalidTimelineTimecodeHint", testInvalidTimelineTimecodeHint),
        ("testRawDescription", testRawDescription),
        ("testDescription", testDescription),
        ("testComputedProperties", testComputedProperties),
    ]
}
