#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributors.

import re

from fenrirscreenreader.core import debug


class TableManager:
    def __init__(self):
        self.headLine = ""
        self.defaultSeparators = ["+", ";", "|", " "]
        self.noOfHeadLineColumns = 0
        self.headColumnSep = ""
        self.rowColumnSep = ""
        self.headerRow = []
        self.tableMode = False
        self.currentColumn = 0
        self.currentRow = 0

    def initialize(self, environment):
        self.env = environment

    def shutdown(self):
        pass
    
    def is_table_mode(self):
        return self.tableMode
    
    def set_table_mode(self, active):
        self.tableMode = active
        if not active:
            self.clear_header_row()
        else:
            # Initialize current position when entering table mode
            self.sync_table_position_from_cursor()
    
    def set_header_row_from_cursor(self):
        """Set header row from current cursor position"""
        if not self.env["runtime"]["CursorManager"].is_review_mode():
            return False
        
        cursor_pos = self.env["runtime"]["CursorManager"].get_review_or_text_cursor()
        if not cursor_pos:
            return False
        
        # Get the line text at cursor position
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"])
        if not line_text:
            return False
        
        # Parse the line into columns
        self.headerRow = self.parse_line_into_columns(line_text)
        return len(self.headerRow) > 0
    
    def clear_header_row(self):
        """Clear the stored header row"""
        self.headerRow = []
    
    def has_header_row(self):
        """Check if header row is set"""
        return len(self.headerRow) > 0
    
    def get_column_header(self, column_index):
        """Get header for a specific column (0-based)"""
        if column_index < 0:
            return f"Column {column_index + 1}"
        
        if len(self.headerRow) == 0:
            return f"Column {column_index + 1}"
        
        if column_index >= len(self.headerRow):
            return f"Column {column_index + 1}"
        
        header = self.headerRow[column_index].strip()
        if not header:  # If header is empty
            return f"Column {column_index + 1}"
        
        return header
    
    def get_column_count(self):
        """Get number of columns based on header row"""
        return len(self.headerRow)
    
    def parse_line_into_columns(self, line_text):
        """Parse a line into columns using various separators"""
        if not line_text:
            return []
        
        # Try different separators in order of preference
        separators = [',', '|', ';', '\t']
        
        for sep in separators:
            if sep in line_text:
                columns = line_text.split(sep)
                if len(columns) > 1:
                    return columns
        
        # If no clear separator, try to detect aligned columns
        return self.detect_aligned_columns(line_text)
    
    def detect_aligned_columns(self, line_text):
        """Detect columns in space-aligned text"""
        if not line_text:
            return []
        
        # Split on multiple spaces (2 or more)
        parts = re.split(r'  +', line_text)
        if len(parts) > 1:
            return parts
        
        # Fallback: treat as single column
        return [line_text]
    
    def get_cell_content(self, line_text, column_index):
        """Get content of a specific cell in a line"""
        columns = self.parse_line_into_columns(line_text)
        if column_index < 0 or column_index >= len(columns):
            return ""
        return columns[column_index].strip()
    
    def get_table_cell_info(self, cursor_pos):
        """Get table cell information for speech output"""
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"])
        if not line_text:
            return None
        
        columns = self.parse_line_into_columns(line_text)
        if not columns:
            return None
        
        # Find which column we're in based on cursor x position
        column_index = self.get_column_index_from_x_position(line_text, cursor_pos["x"])
        
        if column_index < 0 or column_index >= len(columns):
            return None
        
        # Use header if available, otherwise generic column name
        if self.has_header_row():
            column_header = self.get_column_header(column_index)
        else:
            column_header = f"Column {column_index + 1}"
        
        return {
            'column_index': column_index,
            'column_header': column_header,
            'cell_content': columns[column_index].strip(),
            'total_columns': len(columns),
            'row_number': cursor_pos["y"] + 1
        }
    
    def get_column_index_from_x_position(self, line_text, x_pos):
        """Determine which column an x position falls into"""
        columns = self.parse_line_into_columns(line_text)
        if not columns:
            return 0
        
        # Handle CSV/delimited text
        if ',' in line_text or '|' in line_text or ';' in line_text or '\t' in line_text:
            current_pos = 0
            for i, column in enumerate(columns):
                column_end = current_pos + len(column)
                if current_pos <= x_pos <= column_end:
                    return i
                current_pos = column_end + 1  # +1 for separator
        else:
            # Handle space-aligned text - find the column by position ranges
            current_pos = 0
            for i, column in enumerate(columns):
                # Find where this column starts in the original text
                if i == 0:
                    column_start = 0
                else:
                    # Find the column by searching from current position
                    column_start = line_text.find(column.strip(), current_pos)
                    if column_start == -1:
                        column_start = current_pos
                
                column_end = column_start + len(column.strip())
                if column_start <= x_pos <= column_end:
                    return i
                current_pos = column_end
        
        # If past the end, return last column
        return len(columns) - 1
    
    def sync_table_position_from_cursor(self):
        """Sync internal table position from current cursor position"""
        if not self.env["runtime"]["CursorManager"].is_review_mode():
            return
        
        cursor_pos = self.env["runtime"]["CursorManager"].get_review_or_text_cursor()
        if cursor_pos:
            old_column = self.currentColumn
            self.currentRow = cursor_pos["y"]
            line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"])
            if line_text:
                self.currentColumn = self.get_column_index_from_x_position(line_text, cursor_pos["x"])
            else:
                self.currentColumn = 0
            
            self.env["runtime"]["DebugManager"].write_debug_out(
                f"TableManager sync: old_column={old_column}, new_column={self.currentColumn}, cursor_x={cursor_pos['x']}, cursor_y={cursor_pos['y']}",
                debug.DebugLevel.INFO
            )
    
    def move_to_next_column(self):
        """Move to next column in table"""
        self.env["runtime"]["DebugManager"].write_debug_out(
            f"TableManager move_to_next_column: currentColumn={self.currentColumn}",
            debug.DebugLevel.INFO
        )
        
        if not self.env["runtime"]["CursorManager"].is_review_mode():
            return None
        
        cursor_pos = self.env["runtime"]["CursorManager"].get_review_or_text_cursor()
        if not cursor_pos:
            return None
        
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"])
        if not line_text:
            return None
        
        columns = self.parse_line_into_columns(line_text)
        if not columns:
            return None
        
        self.env["runtime"]["DebugManager"].write_debug_out(
            f"TableManager: line_text='{line_text}', columns={columns}, currentColumn={self.currentColumn}",
            debug.DebugLevel.INFO
        )
        
        # Don't sync from cursor position - maintain our own tracking
        # Check if we're already at the last column
        if self.currentColumn >= len(columns) - 1:
            # At end of line - return special indicator but keep position
            return {"at_end": True, "current_info": self.get_table_cell_info_by_indices(cursor_pos["y"], self.currentColumn)}
        
        # Move to next column
        self.currentColumn += 1
        
        self.env["runtime"]["DebugManager"].write_debug_out(
            f"TableManager: moved to column {self.currentColumn}",
            debug.DebugLevel.INFO
        )
        
        # Return info for the new column without moving cursor position
        return self.get_table_cell_info_by_indices(cursor_pos["y"], self.currentColumn)
    
    def move_to_prev_column(self):
        """Move to previous column in table"""
        if not self.env["runtime"]["CursorManager"].is_review_mode():
            return None
        
        cursor_pos = self.env["runtime"]["CursorManager"].get_review_or_text_cursor()
        if not cursor_pos:
            return None
        
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"])
        if not line_text:
            return None
        
        columns = self.parse_line_into_columns(line_text)
        if not columns:
            return None
        
        # Don't sync from cursor position - maintain our own tracking
        # Check if we're already at the first column
        if self.currentColumn <= 0:
            # At beginning of line - return special indicator but keep position
            return {"at_start": True, "current_info": self.get_table_cell_info_by_indices(cursor_pos["y"], self.currentColumn)}
        
        # Move to previous column
        self.currentColumn -= 1
        
        # Return info for the new column without moving cursor position
        return self.get_table_cell_info_by_indices(cursor_pos["y"], self.currentColumn)
    
    def get_current_table_cell_info(self):
        """Get current table cell info using internal position tracking"""
        if not self.env["runtime"]["CursorManager"].is_review_mode():
            return None
        
        cursor_pos = self.env["runtime"]["CursorManager"].get_review_or_text_cursor()
        if not cursor_pos:
            return None
        
        return self.get_table_cell_info_by_indices(cursor_pos["y"], self.currentColumn)
    
    def get_table_cell_info_by_indices(self, row, column_index):
        """Get table cell info for specific row and column indices"""
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(row)
        if not line_text:
            return None
        
        columns = self.parse_line_into_columns(line_text)
        if not columns or column_index < 0 or column_index >= len(columns):
            return None
        
        # Always get column header (fallback to generic if needed)
        column_header = self.get_column_header(column_index)
        
        cell_content = columns[column_index].strip()
        if not cell_content:  # Handle empty cells
            cell_content = "blank"
        
        return {
            'column_index': column_index,
            'column_header': column_header,
            'cell_content': cell_content,
            'total_columns': len(columns),
            'row_number': row + 1
        }
    
    def get_column_start_position(self, line_text, column_index):
        """Get the starting x position of a specific column"""
        columns = self.parse_line_into_columns(line_text)
        if not columns or column_index < 0 or column_index >= len(columns):
            return 0
        
        # For CSV/delimited text - find the actual position in the original line
        if ',' in line_text or '|' in line_text or ';' in line_text or '\t' in line_text:
            # Determine the separator being used
            separator = ','
            if '|' in line_text and line_text.count('|') > line_text.count(','):
                separator = '|'
            elif ';' in line_text and line_text.count(';') > line_text.count(','):
                separator = ';'
            elif '\t' in line_text:
                separator = '\t'
            
            # Find the position by splitting and calculating
            if column_index == 0:
                return 0
            
            # Count characters up to the target column
            parts = line_text.split(separator)
            position = 0
            for i in range(column_index):
                if i < len(parts):
                    position += len(parts[i]) + 1  # +1 for separator
            return position
        else:
            # For space-aligned text, find the actual position of the column
            if column_index == 0:
                return 0
            
            # Find the column text in the line
            target_text = columns[column_index].strip()
            search_start = 0
            for i in range(column_index):
                prev_text = columns[i].strip()
                found_pos = line_text.find(prev_text, search_start)
                if found_pos != -1:
                    search_start = found_pos + len(prev_text)
            
            column_pos = line_text.find(target_text, search_start)
            return column_pos if column_pos != -1 else search_start

    def is_cursor_within_current_cell(self, cursor_x, cursor_y):
        """Check if the given cursor position is within the current table cell"""
        if not self.is_table_mode():
            return False
        
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_y)
        if not line_text:
            return False
        
        columns = self.parse_line_into_columns(line_text)
        if not columns or self.currentColumn < 0 or self.currentColumn >= len(columns):
            return False
        
        # Get the bounds of the current column
        column_start = self.get_column_start_position(line_text, self.currentColumn)
        column_text = columns[self.currentColumn]
        column_end = column_start + len(column_text)
        
        # Check if cursor is within the column bounds
        return column_start <= cursor_x < column_end

    def move_to_first_cell(self):
        """Move to first cell in current row"""
        if not self.env["runtime"]["CursorManager"].is_review_mode():
            return None
        
        cursor_pos = self.env["runtime"]["CursorManager"].get_review_or_text_cursor()
        if not cursor_pos:
            return None
        
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"])
        if not line_text:
            return None
        
        columns = self.parse_line_into_columns(line_text)
        if not columns:
            return None
        
        # Set current column to first column
        self.currentColumn = 0
        
        # Return info for the first column
        return self.get_table_cell_info_by_indices(cursor_pos["y"], 0)
    
    def move_to_last_cell(self):
        """Move to last cell in current row"""
        if not self.env["runtime"]["CursorManager"].is_review_mode():
            return None
        
        cursor_pos = self.env["runtime"]["CursorManager"].get_review_or_text_cursor()
        if not cursor_pos:
            return None
        
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"])
        if not line_text:
            return None
        
        columns = self.parse_line_into_columns(line_text)
        if not columns:
            return None
        
        # Set current column to last column
        self.currentColumn = len(columns) - 1
        
        # Return info for the last column
        return self.get_table_cell_info_by_indices(cursor_pos["y"], self.currentColumn)
    
    def move_to_first_char_in_cell(self):
        """Move to first character in current cell"""
        if not self.env["runtime"]["CursorManager"].is_review_mode():
            return None
        
        cursor_pos = self.env["runtime"]["CursorManager"].get_review_or_text_cursor()
        if not cursor_pos:
            return None
        
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"])
        if not line_text:
            return None
        
        columns = self.parse_line_into_columns(line_text)
        if not columns or self.currentColumn < 0 or self.currentColumn >= len(columns):
            return None
        
        # Get column start position
        column_start = self.get_column_start_position(line_text, self.currentColumn)
        
        # Find first non-space character in the column
        column_text = columns[self.currentColumn]
        first_char_offset = len(column_text) - len(column_text.lstrip())
        
        # Set cursor position to first character in cell
        new_x = column_start + first_char_offset
        self.env["runtime"]["CursorManager"].set_review_cursor_position(new_x, cursor_pos["y"])
        
        # Get the character at the new position
        from fenrirscreenreader.utils import char_utils
        (
            self.env["screen"]["newCursorReview"]["x"],
            self.env["screen"]["newCursorReview"]["y"],
            curr_char,
        ) = char_utils.get_current_char(
            new_x,
            cursor_pos["y"],
            self.env["screen"]["new_content_text"],
        )
        
        return {
            'cell_content': column_text.strip(),
            'column_header': self.get_column_header(self.currentColumn),
            'character': curr_char,
            'position': 'first'
        }
    
    def move_to_last_char_in_cell(self):
        """Move to last character in current cell"""
        if not self.env["runtime"]["CursorManager"].is_review_mode():
            return None
        
        cursor_pos = self.env["runtime"]["CursorManager"].get_review_or_text_cursor()
        if not cursor_pos:
            return None
        
        line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"])
        if not line_text:
            return None
        
        columns = self.parse_line_into_columns(line_text)
        if not columns or self.currentColumn < 0 or self.currentColumn >= len(columns):
            return None
        
        # Get column start position
        column_start = self.get_column_start_position(line_text, self.currentColumn)
        column_text = columns[self.currentColumn]
        
        # Find last non-space character in the column
        trimmed_text = column_text.rstrip()
        if not trimmed_text:
            # If empty cell, go to column start
            new_x = column_start
        else:
            # Find the position of the last character
            new_x = column_start + len(trimmed_text) - 1
        
        # Set cursor position to last character in cell
        self.env["runtime"]["CursorManager"].set_review_cursor_position(new_x, cursor_pos["y"])
        
        # Get the character at the new position
        from fenrirscreenreader.utils import char_utils
        (
            self.env["screen"]["newCursorReview"]["x"],
            self.env["screen"]["newCursorReview"]["y"],
            curr_char,
        ) = char_utils.get_current_char(
            new_x,
            cursor_pos["y"],
            self.env["screen"]["new_content_text"],
        )
        
        return {
            'cell_content': column_text.strip(),
            'column_header': self.get_column_header(self.currentColumn),
            'character': curr_char,
            'position': 'last'
        }

    def reset_table_mode(self):
        self.set_head_line()

    def set_head_column_sep(self, columnSep=""):
        self.headColumnSep = columnSep
        if columnSep == "":
            self.noOfHeadLineColumns = 0
        else:
            self.coun_no_of_head_columns()

    def coun_no_of_head_columns(self):
        if self.headLine and self.headColumnSep:
            self.noOfHeadLineColumns = len(self.headLine.split(self.headColumnSep))

    def search_for_head_column_sep(self, headLine):
        """Find the most likely column separator in a header line"""
        separators = [',', '|', ';', '\t']
        
        for sep in separators:
            if sep in headLine:
                return sep
        
        # Check for multiple spaces (aligned columns)
        if '  ' in headLine:
            return '  '
        
        return ""

    def set_row_column_sep(self, columnSep=""):
        self.rowColumnSep = columnSep

    def set_head_line(self, headLine=""):
        self.set_head_column_sep()
        self.set_row_column_sep()
        if headLine != "":
            sep = self.search_for_head_column_sep(headLine)
            if sep != "":
                self.headLine = headLine
                self.set_head_column_sep(sep)
