"""
Output class for creating beautifully aligned tables with flexible data input.
Supports headers, multiple data formats (list, tuple, dict), automatic column sizing,
and customizable empty value handling.
"""

from decimal import Decimal
from sunhpc.logs import Logs
from typing import List, Dict, Any, Optional, Union, Tuple

slog = Logs(name="Output", show_time=True)

class Output:
    """
    A flexible table class for aligned output with header support.
    
    Examples:
        table = Output()
        table.add_header(['name', 'age', 'city'])
        table.add_row(['Alice', 28, 'New York'])
        table.add_row({'name': 'Bob', 'age': 32, 'city': 'Los Angeles'})
        table.add_row(('Charlie', 25, 'Chicago'))
        table.print_table()
    """
    
    def __init__(
        self,
        headers: Optional[List[str]] = None,
        align: str = 'left',
        empty_fill: str = 'N/A',
        separator: str = '  ',
        border_style: str = 'simple',  # 'simple', 'grid', 'none'
        header_separator_char: str = '-',
        max_width: Optional[int] = None,
        truncate_suffix: str = '...',
        auto_align_numeric: bool = True
    ):
        """
        Initialize the Output.
        
        Args:
            headers: Initial list of column headers
            align: Default alignment for columns ('left', 'right', 'center')
            empty_fill: String to fill empty/missing values
            separator: Column separator string
            border_style: Style of borders ('simple', 'grid', 'none')
            header_separator_char: Character for header underline
            max_width: Maximum width for any column (truncates if exceeded)
            truncate_suffix: Suffix for truncated values
            auto_align_numeric: Automatically right-align numeric values
        """
        self.headers: List[str] = headers if headers else []
        self.data: List[List[Any]] = []
        self.alignments: Dict[str, str] = {}
        self.default_align = align
        self.empty_fill = empty_fill
        self.separator = separator
        self.border_style = border_style
        self.header_separator_char = header_separator_char
        self.max_width = max_width
        self.truncate_suffix = truncate_suffix
        self.auto_align_numeric = auto_align_numeric
        
        # Column order (if headers are provided)
        self.column_order = headers.copy() if headers else []
        
        # Initialize alignments for headers
        if headers:
            for header in headers:
                self.alignments[header] = align
    
    def add_header(self, headers: List[str]) -> 'Output':
        """
        Add or replace table headers.
        
        Args:
            headers: List of column header names
            
        Returns:
            Self for method chaining
        """
        self.headers = headers
        self.column_order = headers.copy()
        
        # Initialize alignments for new headers
        for header in headers:
            if header not in self.alignments:
                self.alignments[header] = self.default_align
        
        return self
    
    def set_alignment(self, column: str, align: str) -> 'Output':
        """
        Set alignment for a specific column.
        
        Args:
            column: Column header name
            align: Alignment ('left', 'right', 'center')
            
        Returns:
            Self for method chaining
        """
        if column in self.headers:
            self.alignments[column] = align
        return self

    def set_empty_fill(self, empty_fill: str) -> 'Output':
        """
        Set the string to fill empty/missing values.
        
        Args:
            empty_fill: String to use for filling empty/missing values
            
        Returns:
            Self for method chaining
        """
        self.empty_fill = empty_fill
        return self

    def set_separator(self, separator: str) -> 'Output':
        """
        Set the column separator string.
        
        Args:
            separator: String to use for separating columns
            
        Returns:
            Self for method chaining
        """
        self.separator = separator
        return self

    def set_border_style(self, border_style: str) -> 'Output':
        """
        Set the style of borders.
        
        Args:
            border_style: Style of borders ('simple', 'grid', 'none')
            
        Returns:
            Self for method chaining
        """
        self.border_style = border_style
        return self

    def set_header_separator_char(self, header_separator_char: str) -> 'Output':
        """
        Set the character for header underline.
        
        Args:
            header_separator_char: Character to use for header underline
            
        Returns:
            Self for method chaining
        """
        self.header_separator_char = header_separator_char
        return self

    def set_max_width(self, max_width: Optional[int] = None) -> 'Output':
        """
        Set the maximum width for any column.
        
        Args:
            max_width: Maximum width for any column (truncates if exceeded)
            
        Returns:
            Self for method chaining
        """
        self.max_width = max_width
        return self

    def set_truncate_suffix(self, truncate_suffix: str = '...') -> 'Output':
        """
        Set the suffix for truncated values.
        
        Args:
            truncate_suffix: Suffix to use for truncated values
            
        Returns:
            Self for method chaining
        """
        self.truncate_suffix = truncate_suffix
        return self

    def set_auto_align_numeric(self, auto_align_numeric: bool = True) -> 'Output':
        """
        Set whether to automatically align numeric values.
        
        Args:
            auto_align_numeric: Whether to align numeric values ('left' or 'right')
            
        Returns:
            Self for method chaining
        """
        self.auto_align_numeric = auto_align_numeric
        return self

    def add_row(
        self,
        row: Union[List[Any], Tuple[Any, ...], Dict[str, Any]],
        auto_fill: bool = True
    ) -> 'Output':
        """
        Add a row to the table.
        
        Args:
            row: Row data as list, tuple, or dictionary
            auto_fill: Automatically fill missing columns with empty_fill
            
        Returns:
            Self for method chaining
        """
        if not self.headers:
            raise ValueError("Headers must be set before adding rows. Use add_header() first.")
        
        processed_row = []
        
        if isinstance(row, dict):
            # Dictionary format: map values by header
            for header in self.headers:
                value = row.get(header)
                if value is None and header not in row:
                    if auto_fill:
                        value = self.empty_fill
                    else:
                        value = ''
                processed_row.append(value)
        elif isinstance(row, (list, tuple)):
            # List/tuple format: match by position
            row_list = list(row)
            for i, header in enumerate(self.headers):
                if i < len(row_list):
                    processed_row.append(row_list[i])
                elif auto_fill:
                    processed_row.append(self.empty_fill)
                else:
                    processed_row.append('')
        else:
            raise TypeError(f"Row must be list, tuple, or dict, got {type(row)}")
        
        self.data.append(processed_row)
        return self
    
    def add_rows(
        self,
        rows: List[Union[List[Any], Tuple[Any, ...], Dict[str, Any]]],
        auto_fill: bool = True
    ) -> 'Output':
        """
        Add multiple rows to the table.
        
        Args:
            rows: List of rows (each row as list, tuple, or dict)
            auto_fill: Automatically fill missing columns with empty_fill
            
        Returns:
            Self for method chaining
        """
        for row in rows:
            self.add_row(row, auto_fill)
        return self
    
    def insert_row(
        self,
        index: int,
        row: Union[List[Any], Tuple[Any, ...], Dict[str, Any]],
        auto_fill: bool = True
    ) -> 'Output':
        """
        Insert a row at a specific index.
        
        Args:
            index: Position to insert the row
            row: Row data as list, tuple, or dictionary
            auto_fill: Automatically fill missing columns with empty_fill
            
        Returns:
            Self for method chaining
        """
        if not self.headers:
            raise ValueError("Headers must be set before inserting rows. Use add_header() first.")
        
        processed_row = []
        
        if isinstance(row, dict):
            for header in self.headers:
                value = row.get(header)
                if value is None and header not in row:
                    if auto_fill:
                        value = self.empty_fill
                    else:
                        value = ''
                processed_row.append(value)
        elif isinstance(row, (list, tuple)):
            row_list = list(row)
            for i, header in enumerate(self.headers):
                if i < len(row_list):
                    processed_row.append(row_list[i])
                elif auto_fill:
                    processed_row.append(self.empty_fill)
                else:
                    processed_row.append('')
        else:
            raise TypeError(f"Row must be list, tuple, or dict, got {type(row)}")
        
        self.data.insert(index, processed_row)
        return self
    
    def remove_row(self, index: int) -> 'Output':
        """
        Remove a row at a specific index.
        
        Args:
            index: Position of row to remove
            
        Returns:
            Self for method chaining
        """
        if 0 <= index < len(self.data):
            self.data.pop(index)
        return self
    
    def clear(self) -> 'Output':
        """Clear all data rows but keep headers."""
        self.data = []
        return self
    
    def reset(self) -> 'Output':
        """Reset the entire table (headers and data)."""
        self.headers = []
        self.data = []
        self.column_order = []
        self.alignments = {}
        return self
    
    def _calculate_column_widths(self) -> Dict[str, int]:
        """Calculate optimal width for each column."""
        if not self.headers:
            return {}
        
        widths = {}
        
        # Initialize with header widths
        for i, header in enumerate(self.headers):
            widths[header] = len(str(header))
        
        # Check data widths
        for row in self.data:
            for i, header in enumerate(self.headers):
                if i < len(row):
                    value = row[i]
                    # Handle None values
                    if value is None:
                        value = ''
                    # Handle special types
                    if isinstance(value, (int, float, Decimal)):
                        # Format numbers for width calculation
                        if isinstance(value, float):
                            value_str = f"{value:.2f}" if value == int(value) else str(value)
                        else:
                            value_str = str(value)
                    else:
                        value_str = str(value)
                    
                    # Apply max width truncation for calculation
                    if self.max_width and len(value_str) > self.max_width:
                        value_str = value_str[:self.max_width - len(self.truncate_suffix)] + self.truncate_suffix
                    
                    widths[header] = max(widths[header], len(value_str))
        
        return widths
    
    def _format_value(self, value: Any, width: int, column: str) -> str:
        """Format a single value with proper alignment and truncation."""
        # Handle None values
        if value is None:
            value = self.empty_fill
        
        # Convert to string
        if isinstance(value, (int, float, Decimal)):
            # Auto-right align numbers if enabled
            if self.auto_align_numeric and self.alignments.get(column, self.default_align) == 'left':
                align = 'right'
            else:
                align = self.alignments.get(column, self.default_align)
            
            # Format numbers nicely
            if isinstance(value, float):
                # Show 2 decimal places for floats that are not integers
                if value == int(value):
                    value_str = str(int(value))
                else:
                    value_str = f"{value:.2f}"
            else:
                value_str = str(value)
        elif isinstance(value, bool):
            value_str = "True" if value else "False"
            align = self.alignments.get(column, self.default_align)
        else:
            value_str = str(value)
            align = self.alignments.get(column, self.default_align)
        
        # Truncate if exceeds max width
        if self.max_width and len(value_str) > self.max_width:
            value_str = value_str[:self.max_width - len(self.truncate_suffix)] + self.truncate_suffix
        
        # Apply alignment
        if align == 'left':
            return value_str.ljust(width)
        elif align == 'right':
            return value_str.rjust(width)
        elif align == 'center':
            return value_str.center(width)
        else:
            return value_str.ljust(width)
    
    def _format_row(self, row: List[Any], widths: Dict[str, int]) -> List[str]:
        """Format an entire row."""
        formatted_cells = []
        for i, header in enumerate(self.headers):
            if i < len(row):
                value = row[i]
            else:
                value = self.empty_fill
            formatted_cells.append(self._format_value(value, widths[header], header))
        return formatted_cells
    
    def _get_border_line(self, widths: Dict[str, int], style: str = 'simple') -> str:
        """Generate border line based on style."""
        if style == 'none':
            return ''
        
        parts = []
        for i, header in enumerate(self.headers):
            width = widths[header]
            if style == 'grid':
                parts.append('+' + '-' * (width + 2))
            else:  # simple
                parts.append('-' * (width + 2))
        
        if style == 'grid':
            return '+' + '+'.join(parts) + '+'
        else:
            return ' ' + '  '.join(parts)
    
    def _format_header(self, widths: Dict[str, int]) -> str:
        """Format the header row."""
        header_cells = []
        for i, header in enumerate(self.headers):
            align = self.alignments.get(header, self.default_align)
            width = widths[header]
            
            if align == 'left':
                formatted = header.ljust(width)
            elif align == 'right':
                formatted = header.rjust(width)
            else:
                formatted = header.center(width)
            
            header_cells.append(formatted)
        
        if self.border_style == 'grid':
            return '| ' + f' {self.separator} '.join(header_cells) + ' |'
        else:
            return self.separator.join(header_cells)
    
    def _format_separator(self, widths: Dict[str, int]) -> str:
        """Format the separator line under headers."""
        if self.border_style == 'none':
            return ''
        
        parts = []
        for i, header in enumerate(self.headers):
            width = widths[header]
            parts.append(self.header_separator_char * width)
        
        if self.border_style == 'grid':
            return '|-' + f'-{self.separator}-'.join(parts) + '-|'
        else:
            return self.separator.join(parts)
    
    def _format_data_row(self, row: List[str], is_last: bool = False) -> str:
        """Format a data row."""
        if self.border_style == 'grid':
            return '| ' + f' {self.separator} '.join(row) + ' |'
        else:
            return self.separator.join(row)
    
    def get_table_string(self) -> str:
        """
        Generate the complete table as a string.
        
        Returns:
            Formatted table string
        """
        if not self.headers:
            return "No headers defined"
        
        if not self.data:
            return f"Table '{self.separator.join(self.headers)}' has no data"
        
        # Calculate column widths
        widths = self._calculate_column_widths()
        
        # Build table lines
        lines = []
        
        # Header
        header_line = self._format_header(widths)
        lines.append(header_line)
        
        # Separator
        separator_line = self._format_separator(widths)
        if separator_line:
            lines.append(separator_line)
        
        # Data rows
        for i, row in enumerate(self.data):
            formatted_row = self._format_row(row, widths)
            lines.append(self._format_data_row(formatted_row, i == len(self.data) - 1))
        
        return '\n'.join(lines)
    
    def print_table(self) -> None:
        """Print the table to console."""
        print(self.get_table_string())
    
    def export_to_csv(self, filename: str, include_header: bool = True) -> None:
        """
        Export table to CSV file.
        
        Args:
            filename: Output file path
            include_header: Whether to include header row
        """
        import csv
        
        with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile)
            
            if include_header:
                writer.writerow(self.headers)
            
            for row in self.data:
                # Convert values to strings and handle None
                processed_row = [str(cell) if cell is not None else '' for cell in row]
                writer.writerow(processed_row)
    
    def get_data_frame(self) -> List[Dict[str, Any]]:
        """
        Convert table data to list of dictionaries.
        
        Returns:
            List of dictionaries representing the table data
        """
        result = []
        for row in self.data:
            row_dict = {}
            for i, header in enumerate(self.headers):
                if i < len(row):
                    value = row[i]
                    # Convert None to empty_fill for consistency
                    if value is None:
                        value = self.empty_fill
                    row_dict[header] = value
                else:
                    row_dict[header] = self.empty_fill
            result.append(row_dict)
        return result
    
    def sort_by(self, column: str, reverse: bool = False) -> 'Output':
        """
        Sort table rows by a specific column.
        
        Args:
            column: Column header to sort by
            reverse: Reverse sort order
            
        Returns:
            Self for method chaining
        """
        if column not in self.headers:
            raise ValueError(f"Column '{column}' not found in headers")
        
        column_index = self.headers.index(column)
        
        # Sort the data
        self.data.sort(
            key=lambda row: row[column_index] if column_index < len(row) and row[column_index] is not None else '',
            reverse=reverse
        )
        
        return self
    
    def filter_by(self, column: str, predicate) -> 'Output':
        """
        Filter rows based on a predicate function.
        
        Args:
            column: Column header to filter by
            predicate: Function that takes a value and returns bool
            
        Returns:
            Self for method chaining
        """
        if column not in self.headers:
            raise ValueError(f"Column '{column}' not found in headers")
        
        column_index = self.headers.index(column)
        
        # Filter the data
        self.data = [
            row for row in self.data
            if column_index < len(row) and predicate(row[column_index])
        ]
        
        return self
    
    def column_exists(self, column: str) -> bool:
        """Check if a column exists."""
        return column in self.headers
    
    def get_column_index(self, column: str) -> Optional[int]:
        """Get the index of a column."""
        if column in self.headers:
            return self.headers.index(column)
        return None
    
    def get_column_data(self, column: str) -> List[Any]:
        """
        Get all values from a specific column.
        
        Args:
            column: Column header name
            
        Returns:
            List of values from that column
        """
        if column not in self.headers:
            raise ValueError(f"Column '{column}' not found")
        
        column_index = self.headers.index(column)
        return [row[column_index] if column_index < len(row) else self.empty_fill for row in self.data]
    
    def update_column(self, column: str, values: List[Any]) -> 'Output':
        """
        Update values in a specific column.
        
        Args:
            column: Column header name
            values: New values for the column (must match row count)
            
        Returns:
            Self for method chaining
        """
        if column not in self.headers:
            raise ValueError(f"Column '{column}' not found")
        
        if len(values) != len(self.data):
            raise ValueError(f"Number of values ({len(values)}) does not match number of rows ({len(self.data)})")
        
        column_index = self.headers.index(column)
        
        for i, value in enumerate(values):
            if i < len(self.data):
                if column_index >= len(self.data[i]):
                    # Extend the row if needed
                    self.data[i].extend([self.empty_fill] * (column_index - len(self.data[i]) + 1))
                self.data[i][column_index] = value
        
        return self
    
    def get_statistics(self) -> Dict[str, Any]:
        """
        Calculate statistics for numeric columns.
        
        Returns:
            Dictionary with statistics for each numeric column
        """
        stats = {}
        
        for column in self.headers:
            column_data = self.get_column_data(column)
            numeric_data = []
            
            for value in column_data:
                try:
                    if isinstance(value, (int, float)):
                        numeric_data.append(float(value))
                    elif isinstance(value, str) and value.replace('.', '').replace('-', '').isdigit():
                        numeric_data.append(float(value))
                except (ValueError, TypeError):
                    pass
            
            if numeric_data:
                stats[column] = {
                    'count': len(numeric_data),
                    'min': min(numeric_data),
                    'max': max(numeric_data),
                    'sum': sum(numeric_data),
                    'avg': sum(numeric_data) / len(numeric_data),
                    'total_rows': len(column_data)
                }
        
        return stats

    def dict_output(self, data, key_width=None, value_width=None, separator=':', indent=0):
        '''
        Print a dictionary as a table.
        
        Args:
            data: Dictionary to print
            key_width: Width of the key column
            value_width: Width of the value column
            separator: Separator between key and value
            indent: Indentation level
        Returns:
            Self for method chaining
        '''
        if not isinstance(data, dict):
            try:
                if isinstance(data, (list, tuple)) and len(data) % 2 == 0:
                    data = dict(zip(data[::2], data[1::2]))
                else:
                    slog.error(f"Error: Cannot convert {type(data).__name__} to dict")
                    return self
            except:
                slog.error(f"Error: Cannot convert {type(data).__name__} to dict")
                return self

        if not data: return



        # 自动计算键和值的宽度最大宽度，确保对齐
        if key_width is None:
            key_width = max(len(key) for key in data.keys())

        if value_width is None:
            value_width = max(len(str(value)) for value in data.values())

        # 缩进
        indent_str = ' ' * indent

        # 输出每一行
        for key, value in data.items():
            print(f"{indent_str}{str(key):<{key_width}}{separator}{str(value):>{value_width}}")

        return self

    def __len__(self) -> int:
        """Return number of rows."""
        return len(self.data)
    
    def __repr__(self) -> str:
        """String representation."""
        if not self.headers:
            return "Output(no headers)"
        return f"Output(headers={self.headers}, rows={len(self.data)})"


# ------------------------------------------------------------------
# Example usage and test
# ------------------------------------------------------------------
if __name__ == '__main__':
    print("=" * 80)
    print("ALIGNED TABLE DEMONSTRATION")
    print("=" * 80)
    
    # Example 1: Basic usage with list rows
    print("\n1. Basic table with list rows:")
    table1 = Output()
    table1.add_header(['Name', 'Age', 'City', 'Country'])
    table1.add_row(['Alice', 28, 'New York', 'USA'])
    table1.add_row(['Bob', 32, 'Los Angeles', 'USA'])
    table1.add_row(['Charlie', 25, 'Chicago', 'USA'])
    table1.print_table()
    
    # Example 2: Dictionary rows with missing values
    print("\n2. Dictionary rows with missing values (auto-filled with '--'):")
    table2 = Output(headers=['Name', 'Age', 'Email', 'Phone'])
    table2.add_row({'Name': 'Alice', 'Age': 28, 'Email': 'alice@example.com'})
    table2.add_row({'Name': 'Bob', 'Age': 32, 'Phone': '123-456-7890'})
    table2.add_row({'Name': 'Charlie', 'Email': 'charlie@example.com', 'Phone': '098-765-4321'})
    table2.print_table()
    
    # Example 3: Mixed data types and custom alignment
    print("\n3. Mixed data types with custom alignment:")
    table3 = Output(headers=['Product', 'Price', 'Quantity', 'In Stock'])
    table3.set_alignment('Price', 'right')
    table3.set_alignment('Quantity', 'right')
    table3.add_rows([
        ['Laptop', 999.99, 5, True],
        ['Mouse', 19.99, 25, True],
        ['Keyboard', 49.99, 0, False],
        ['Monitor', 299.99, 3, True]
    ])
    table3.print_table()
    
    # Example 4: Different border styles
    print("\n4. Grid border style:")
    table4 = Output(headers=['ID', 'Name', 'Score'], border_style='grid')
    table4.add_rows([
        [1, 'John', 85.5],
        [2, 'Jane', 92.0],
        [3, 'Bob', 78.5]
    ])
    table4.print_table()
    
    print("\n5. No border style:")
    table4.border_style = 'none'
    table4.print_table()
    
    # Example 6: Tuple rows
    print("\n6. Tuple rows:")
    table6 = Output(headers=['X', 'Y', 'Z'])
    table6.add_rows([
        (10, 20, 30),
        (40, 50, 60),
        (70, 80, 90)
    ])
    table6.print_table()
    
    # Example 7: Sorting and filtering
    print("\n7. Sorting by age (descending):")
    table7 = Output(headers=['Name', 'Age', 'Salary'])
    table7.add_rows([
        {'Name': 'Alice', 'Age': 28, 'Salary': 50000},
        {'Name': 'Bob', 'Age': 35, 'Salary': 60000},
        {'Name': 'Charlie', 'Age': 25, 'Salary': 45000},
        {'Name': 'Diana', 'Age': 30, 'Salary': 55000}
    ])
    table7.sort_by('Age', reverse=True)
    table7.print_table()
    
    print("\n8. Filtered (salary > 50000):")
    table8 = Output(headers=['Name', 'Age', 'Salary'])
    table8.add_rows([
        {'Name': 'Alice', 'Age': 28, 'Salary': 50000},
        {'Name': 'Bob', 'Age': 35, 'Salary': 60000},
        {'Name': 'Charlie', 'Age': 25, 'Salary': 45000},
        {'Name': 'Diana', 'Age': 30, 'Salary': 55000}
    ])
    table8.filter_by('Salary', lambda x: x > 50000 if x is not None else False)
    table8.print_table()
    
    # Example 9: Column operations
    print("\n9. Column operations:")
    table9 = Output(headers=['Product', 'Price', 'Quantity'])
    table9.add_rows([
        ['Apple', 1.2, 100],
        ['Banana', 0.8, 150],
        ['Orange', 1.5, 80]
    ])
    
    print("Column 'Price' values:", table9.get_column_data('Price'))
    
    # Calculate total value
    prices = table9.get_column_data('Price')
    quantities = table9.get_column_data('Quantity')
    totals = [p * q for p, q in zip(prices, quantities)]
    
    table9.add_header(['Product', 'Price', 'Quantity', 'Total'])
    table9.update_column('Total', totals)
    table9.print_table()
    
    # Example 10: Statistics
    print("\n10. Statistics for numeric columns:")
    table10 = Output(headers=['Name', 'Math', 'Science', 'English'])
    table10.add_rows([
        ['Alice', 85, 90, 88],
        ['Bob', 78, 85, 82],
        ['Charlie', 92, 88, 91],
        ['Diana', 88, 92, 87]
    ])
    
    stats = table10.get_statistics()
    for column, stat in stats.items():
        if column != 'Name':
            print(f"  {column}: avg={stat['avg']:.1f}, min={stat['min']}, max={stat['max']}")
    
    # Example 11: Export to CSV (uncomment to test)
    # table10.export_to_csv('grades.csv')
    
    # Example 12: Complex real-world example
    print("\n11. Real-world example - Employee Directory:")
    employees = Output(
        headers=['Employee ID', 'Name', 'Department', 'Position', 'Salary', 'Status'],
        align='left',
        empty_fill='N/A',
        separator=' │ ',
        border_style='grid'
    )
    
    employees.set_alignment('Salary', 'right')
    employees.set_alignment('Employee ID', 'center')
    
    employees.add_rows([
        {'Employee ID': 1001, 'Name': 'John Smith', 'Department': 'Engineering', 'Position': 'Senior Developer', 'Salary': 95000, 'Status': 'Active'},
        {'Employee ID': 1002, 'Name': 'Jane Doe', 'Department': 'Marketing', 'Position': 'Manager', 'Salary': 85000, 'Status': 'Active'},
        {'Employee ID': 1003, 'Name': 'Bob Johnson', 'Department': 'Engineering', 'Position': 'Developer', 'Salary': 75000, 'Status': 'On Leave'},
        {'Employee ID': 1004, 'Name': 'Alice Brown', 'Department': 'Sales', 'Position': 'Representative', 'Salary': 65000, 'Status': 'Active'},
        {'Employee ID': 1005, 'Name': 'Charlie Wilson', 'Department': 'Engineering', 'Position': 'Lead', 'Salary': 110000, 'Status': 'Active'},
        {'Employee ID': 1006, 'Name': 'Diana Miller', 'Department': 'HR', 'Position': 'Specialist', 'Salary': 60000, 'Status': 'Inactive'},
    ])
    
    employees.print_table()
    
    # Example 13: Get data as dictionary list
    print("\n12. Export to dictionary list:")
    data_dict = table10.get_data_frame()
    for row in data_dict[:2]:  # Show first 2 rows
        print(f"  {row}")
    
    # Example 14: Chaining methods
    print("\n13. Method chaining example:")
    (Output(headers=['Item', 'Count'])
     .add_row(['Apples', 10])
     .add_row(['Bananas', 15])
     .add_row(['Oranges', 8])
     .sort_by('Count', reverse=True)
     .print_table())