#include "lumacs/rectangle_manager.hpp"
#include "lumacs/editor_core.hpp" // For EditorCore access
#include <algorithm>

namespace lumacs {

RectangleManager::RectangleManager(EditorCore& core) : core_(core) {
}

void RectangleManager::kill_rectangle() {
    auto& buf = core_.buffer();
    Position cursor = core_.active_window()->cursor();
    
    auto region = buf.get_region(cursor);
    if (!region) {
        core_.set_message("No active region");
        return;
    }
    
    Position start_pos = region->start;
    Position end_pos = region->end;
    
    if (start_pos.line > end_pos.line || (start_pos.line == end_pos.line && start_pos.column > end_pos.column)) {
        std::swap(start_pos, end_pos);
    }
    
    // Determine the rectangular column range (consistent across all lines)
    size_t min_col = std::min(start_pos.column, end_pos.column);
    size_t max_col = std::max(start_pos.column, end_pos.column);
    size_t rect_width = max_col - min_col;
    
    rectangle_kill_ring_.clear();
    
    // Extract rectangle line by line
    for (size_t line_idx = start_pos.line; line_idx <= end_pos.line; ++line_idx) {
        if (line_idx >= buf.line_count()) {
            rectangle_kill_ring_.push_back(std::string(rect_width, ' ')); // Extract spaces for lines out of buffer bounds
            continue;
        }
        
        std::string line_text = buf.line(line_idx);
        
        // Build the substring to extract, padding with spaces if necessary
        std::string extracted_text;
        if (min_col >= line_text.size()) {
            // Entirely outside the line, fill with spaces
            extracted_text = std::string(rect_width, ' ');
        } else {
            // Part of or entirely within the line
            size_t actual_extract_len = std::min(rect_width, line_text.size() - min_col);
            extracted_text = line_text.substr(min_col, actual_extract_len);
            if (extracted_text.length() < rect_width) {
                // Pad with spaces if the extracted part is shorter than rect_width
                extracted_text += std::string(rect_width - extracted_text.length(), ' ');
            }
        }
        rectangle_kill_ring_.push_back(extracted_text);
    }
    
    // Now delete the rectangle content (from bottom to top to preserve line indices)
    for (int line_idx = static_cast<int>(end_pos.line); line_idx >= static_cast<int>(start_pos.line); --line_idx) {
        if (line_idx >= static_cast<int>(buf.line_count())) continue; // Line might have been deleted/shortened
        
        std::string current_line_text = buf.line(line_idx);
        
        if (min_col < current_line_text.size()) { // Only delete if min_col is within current line bounds
            size_t actual_max_col = std::min(max_col, current_line_text.size());
            if (min_col < actual_max_col) {
                buf.erase(Range{Position(line_idx, min_col), Position(line_idx, actual_max_col)});
            }
        }
    }

    // Push the killed rectangle content to the global kill ring as a single string
    std::string killed_text_combined;
    for (size_t i = 0; i < rectangle_kill_ring_.size(); ++i) {
        killed_text_combined += rectangle_kill_ring_[i];
        if (i < rectangle_kill_ring_.size() - 1) {
            killed_text_combined += '\n';
        }
    }
    if (!killed_text_combined.empty()) {
        core_.kill_ring_manager().push(killed_text_combined);
    }
    
    buf.deactivate_mark();
    core_.emit_event(EditorEvent::BufferModified);
    
    core_.set_message(std::string("Rectangle killed (") + std::to_string(rectangle_kill_ring_.size()) + " lines)");
}

void RectangleManager::yank_rectangle() {
    if (rectangle_kill_ring_.empty()) {
        core_.set_message("No rectangle in kill ring");
        return;
    }
    
    auto& buf = core_.buffer();
    Position cursor = core_.active_window()->cursor();
    
    // Insert rectangle starting at cursor position
    for (size_t i = 0; i < rectangle_kill_ring_.size(); ++i) {
        Position insert_pos{cursor.line + i, cursor.column};
        
        // Ensure we have enough lines in the buffer
        while (buf.line_count() <= insert_pos.line) {
            // Insert new empty lines at the end of the buffer until insert_pos.line is valid
            buf.insert_newline(Position{buf.line_count() - 1, buf.line(buf.line_count() - 1).size()});
        }
        
        // Get the current line content where we want to insert
        std::string current_line_text = buf.line(insert_pos.line);
        
        // Pad line with spaces if the insertion point is beyond current line length
        if (current_line_text.size() < insert_pos.column) {
            std::string padding(insert_pos.column - current_line_text.size(), ' ');
            buf.insert(Position{insert_pos.line, current_line_text.size()}, padding);
        }
        
        // Insert the killed rectangle content for the current line
        buf.insert(insert_pos, rectangle_kill_ring_[i]);
    }
    
    core_.emit_event(EditorEvent::BufferModified);
    core_.set_message(std::string("Rectangle yanked (") + std::to_string(rectangle_kill_ring_.size()) + " lines)");
}

void RectangleManager::string_rectangle(const std::string& text) {
    auto& buf = core_.buffer(); // Delegated access
    Position cursor = core_.active_window()->cursor(); // Delegated access
    
    auto region = buf.get_region(cursor);
    if (!region) {
        core_.set_message("No active region");
        return;
    }
    
    Position start_pos = region->start;
    Position end_pos = region->end;
    
    if (start_pos.line > end_pos.line || (start_pos.line == end_pos.line && start_pos.column > end_pos.column)) {
        std::swap(start_pos, end_pos);
    }
    
    // Determine the rectangular column range (consistent across all lines)
    size_t min_col = std::min(start_pos.column, end_pos.column);
    size_t max_col = std::max(start_pos.column, end_pos.column);
    size_t rect_width = max_col - min_col;

    if (rect_width == 0) { // No width to fill
        core_.set_message("Rectangle has zero width");
        return;
    }

    // Prepare fill text
    std::string fill_text_pattern = text.empty() ? " " : text; // Default to space if empty
    std::string repeated_fill_text;
    while (repeated_fill_text.length() < rect_width) {
        repeated_fill_text += fill_text_pattern;
    }
    repeated_fill_text = repeated_fill_text.substr(0, rect_width);

    // Fill rectangle with the given text
    for (size_t line_idx = start_pos.line; line_idx <= end_pos.line; ++line_idx) {
        if (line_idx >= buf.line_count()) {
            // Lines out of buffer bounds are implicitly "empty" and don't get filled unless
            // the buffer is extended. For simplicity, we only operate on existing lines.
            // A more complete implementation would insert new lines.
            continue; 
        }
        
        std::string current_line_text = buf.line(line_idx);
        
        // Pad line with spaces if min_col extends beyond current line length
        if (current_line_text.size() < min_col) {
            std::string padding(min_col - current_line_text.size(), ' ');
            buf.insert(Position{line_idx, current_line_text.size()}, padding);
            current_line_text = buf.line(line_idx); // Refresh line after padding
        }
        
        // Determine the actual range to replace within the line
        size_t effective_replace_end_col = std::min(max_col, current_line_text.size());
        
        // Perform replacement
        if (min_col < effective_replace_end_col) {
            buf.replace(Range{Position(line_idx, min_col), Position(line_idx, effective_replace_end_col)}, 
                        repeated_fill_text.substr(0, effective_replace_end_col - min_col));
        } else if (min_col >= effective_replace_end_col) {
            // If the rectangle starts at or beyond the current line's length,
            // we insert the fill text (with padding if necessary)
            buf.insert(Position(line_idx, min_col), repeated_fill_text);
        }
    }
    
    buf.deactivate_mark();
    core_.emit_event(EditorEvent::BufferModified);
    core_.set_message("Rectangle filled");
}

} // namespace lumacs
