// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 MergeKSortedLists Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "MergeKSortedLists.h"
#include <iostream>
#include <climits>

// LIFECYCLE

MergeKSortedLists::MergeKSortedLists() {
    // Default constructor - no initialization needed
}

MergeKSortedLists::~MergeKSortedLists() {
    // Virtual destructor - no cleanup needed
}

// OPERATIONS

ListNode* MergeKSortedLists::MergeKListsDivideConquer(std::vector<ListNode*>& lists) {
    if (lists.empty()) {
        return nullptr;
    }
    
    return MergeRange(lists, 0, lists.size() - 1);
}

ListNode* MergeKSortedLists::MergeKListsPriorityQueue(std::vector<ListNode*>& lists) {
    if (lists.empty()) {
        return nullptr;
    }
    
    // Create min-heap priority queue
    std::priority_queue<ListNode*, std::vector<ListNode*>, ListNodeComparator> min_heap;
    
    // Add all non-null list heads to the priority queue
    for (ListNode* head : lists) {
        if (head != nullptr) {
            min_heap.push(head);
        }
    }
    
    if (min_heap.empty()) {
        return nullptr;
    }
    
    // Create dummy head for easier list construction
    ListNode dummy_head(0);
    ListNode* current = &dummy_head;
    
    // Process nodes from priority queue
    while (!min_heap.empty()) {
        // Get the node with minimum value
        ListNode* min_node = min_heap.top();
        min_heap.pop();
        
        // Add it to result list
        current->mpNext = min_node;
        current = current->mpNext;
        
        // If this node has a next node, add it to priority queue
        if (min_node->mpNext != nullptr) {
            min_heap.push(min_node->mpNext);
        }
    }
    
    return dummy_head.mpNext;
}

ListNode* MergeKSortedLists::MergeKListsOneByOne(std::vector<ListNode*>& lists) {
    if (lists.empty()) {
        return nullptr;
    }
    
    ListNode* result = lists[0];
    
    // Merge each list one by one with the result
    for (size_t i = 1; i < lists.size(); ++i) {
        result = MergeTwoLists(result, lists[i]);
    }
    
    return result;
}

// PRIVATE OPERATIONS

ListNode* MergeKSortedLists::MergeTwoLists(ListNode* list1, ListNode* list2) {
    // Create dummy head for easier list construction
    ListNode dummy_head(0);
    ListNode* current = &dummy_head;
    
    // Merge the two lists
    while (list1 != nullptr && list2 != nullptr) {
        if (list1->mVal <= list2->mVal) {
            current->mpNext = list1;
            list1 = list1->mpNext;
        } else {
            current->mpNext = list2;
            list2 = list2->mpNext;
        }
        current = current->mpNext;
    }
    
    // Append remaining nodes
    if (list1 != nullptr) {
        current->mpNext = list1;
    } else {
        current->mpNext = list2;
    }
    
    return dummy_head.mpNext;
}

ListNode* MergeKSortedLists::MergeRange(std::vector<ListNode*>& lists, int start, int end) {
    if (start == end) {
        return lists[start];
    }
    
    if (start > end) {
        return nullptr;
    }
    
    // Divide the range into two halves
    int mid = start + (end - start) / 2;
    
    // Recursively merge left and right halves
    ListNode* left = MergeRange(lists, start, mid);
    ListNode* right = MergeRange(lists, mid + 1, end);
    
    // Merge the two halves
    return MergeTwoLists(left, right);
}

// UTILITY FUNCTIONS

ListNode* CreateList(const std::vector<int>& values) {
    if (values.empty()) {
        return nullptr;
    }
    
    ListNode* head = new ListNode(values[0]);
    ListNode* current = head;
    
    for (size_t i = 1; i < values.size(); ++i) {
        current->mpNext = new ListNode(values[i]);
        current = current->mpNext;
    }
    
    return head;
}

void PrintList(ListNode* head) {
    ListNode* current = head;
    
    std::cout << "[";
    while (current != nullptr) {
        std::cout << current->mVal;
        if (current->mpNext != nullptr) {
            std::cout << ", ";
        }
        current = current->mpNext;
    }
    std::cout << "]" << std::endl;
}

void DeleteList(ListNode* head) {
    while (head != nullptr) {
        ListNode* temp = head;
        head = head->mpNext;
        delete temp;
    }
}