Below Is my ncdu like python based tool. I would like you to upgrade and add to its features and abilities and make it a truly powerful tool which can do everything ncdu can and much more. do not worry about the length, simply only respond with the updated script and if you run out of space I will ask you to continue where you left off. Make the tool as powerful, feature rich, option rich, and useful as you can. It should blow ncdu out of the water. Do nothing else


#!/usr/bin/env python3
"""
Python-based ncdu-like Disk Usage Analyzer
"""

import os
import sys
import asyncio
import argparse
import threading
import shutil
import datetime
import curses
import json
import csv
from pathlib import Path
from typing import List, Dict, Optional
from dataclasses import dataclass, field


@dataclass
class FileEntry:
    name: str
    path: str
    is_dir: bool
    size: int = 0
    percentage: float = 0.0
    children: List['FileEntry'] = field(default_factory=list)
    permission_denied: bool = False
    last_modified: Optional[datetime.datetime] = None
    bookmarks: bool = False


class DiskUsageAnalyzer:
    def __init__(self, path: str, show_hidden: bool = False):
        self.path = os.path.abspath(path)
        self.show_hidden = show_hidden
        self.root_entry = None
        self.sort_by = 'size'
        self.reverse_sort = True  # From largest to smallest
        self.bookmarks = set()
        self.scan_lock = threading.Lock()

    def scan(self):
        self.root_entry = asyncio.run(self.scan_directory(self.path))

    async def scan_directory(self, path: str) -> FileEntry:
        entry = FileEntry(
            name=os.path.basename(path) or path,
            path=path,
            is_dir=True,
            last_modified=self.get_last_modified(path)
        )
        try:
            with os.scandir(path) as it:
                tasks = []
                for item in it:
                    if not self.show_hidden and item.name.startswith('.'):
                        continue
                    if item.is_symlink():
                        continue
                    full_path = item.path
                    last_modified = self.get_last_modified(full_path)
                    if item.is_dir(follow_symlinks=False):
                        task = self.scan_directory(full_path)
                        task = asyncio.create_task(task)
                        tasks.append((task, item.name, full_path, last_modified))
                    else:
                        size = item.stat(follow_symlinks=False).st_size
                        entry.children.append(FileEntry(
                            name=item.name,
                            path=full_path,
                            is_dir=False,
                            size=size,
                            last_modified=last_modified
                        ))
                        entry.size += size
                if tasks:
                    for task, name, full_path, last_modified in tasks:
                        child_entry = await task
                        entry.children.append(child_entry)
                        entry.size += child_entry.size
        except PermissionError:
            entry.permission_denied = True
        return entry

    @staticmethod
    def get_last_modified(path: str) -> Optional[datetime.datetime]:
        try:
            timestamp = os.path.getmtime(path)
            return datetime.datetime.fromtimestamp(timestamp)
        except Exception:
            return None

    def sort_entries(self, entries: List[FileEntry]) -> List[FileEntry]:
        if self.sort_by == 'size':
            key_func = lambda e: e.size
        elif self.sort_by == 'name':
            key_func = lambda e: e.name.lower()
        elif self.sort_by == 'date':
            key_func = lambda e: e.last_modified or datetime.datetime.min
        else:
            key_func = lambda e: e.size

        return sorted(entries, key=key_func, reverse=self.reverse_sort)

    def calculate_percentages(self, entry: FileEntry, total_size: int):
        if total_size > 0:
            entry.percentage = (entry.size / total_size) * 100
        else:
            entry.percentage = 0
        for child in entry.children:
            self.calculate_percentages(child, total_size)

    def export_report(self, format: str = 'json', filename: str = 'disk_usage_report'):
        if format.lower() == 'json':
            report = self.create_report(self.root_entry)
            with open(f'{filename}.json', 'w') as f:
                json.dump(report, f, indent=2)
        elif format.lower() == 'csv':
            with open(f'{filename}.csv', 'w', newline='') as f:
                writer = csv.writer(f)
                writer.writerow(['Name', 'Path', 'Size', 'Percentage'])
                self.write_csv_entry(writer, self.root_entry)

    def create_report(self, entry: FileEntry) -> Dict:
        data = {
            'name': entry.name,
            'path': entry.path,
            'size': entry.size,
            'percentage': entry.percentage,
            'is_dir': entry.is_dir,
            'children': [self.create_report(child) for child in entry.children]
        }
        return data

    def write_csv_entry(self, writer, entry: FileEntry):
        writer.writerow([entry.name, entry.path, entry.size, entry.percentage])
        for child in entry.children:
            self.write_csv_entry(writer, child)

    def toggle_bookmark(self, path: str):
        if path in self.bookmarks:
            self.bookmarks.remove(path)
        else:
            self.bookmarks.add(path)

    def get_bookmarks(self) -> List[str]:
        return list(self.bookmarks)


class NcduTUI:
    def __init__(self, analyzer: DiskUsageAnalyzer):
        self.analyzer = analyzer
        self.current_entry = analyzer.root_entry
        self.selected_index = 0
        self.path_stack = []
        self.running = True
        self.search_query = ''
        self.search_results = []
        self.show_graph = False
        self.screen = None

    def start(self):
        curses.wrapper(self.main)

    def main(self, stdscr):
        self.screen = stdscr
        curses.curs_set(0)
        self.setup_colors()
        self.display()

        while self.running:
            key = self.screen.getch()
            if key == curses.KEY_UP:
                self.move_selection(-1)
            elif key == curses.KEY_DOWN:
                self.move_selection(1)
            elif key == curses.KEY_LEFT:
                self.go_back()
            elif key == curses.KEY_RIGHT or key == ord('\n'):
                self.enter_directory()
            elif key == ord('q'):
                self.running = False
            elif key == ord('d'):
                self.delete_entry()
            elif key == ord('s'):
                self.change_sort()
            elif key == ord('h'):
                self.toggle_hidden()
            elif key == ord('r'):
                self.rescan()
            elif key == ord('/'):
                self.search()
            elif key == ord('p'):
                self.preview_file()
            elif key == ord('e'):
                self.export_report()
            elif key == ord('b'):
                self.toggle_bookmark()
            elif key == ord('g'):
                self.toggle_graph()
            elif key == ord('t'):
                self.change_theme()
            elif key == ord(' '):
                self.toggle_collapse()
            elif key == ord('i'):
                self.show_info()
            elif key == ord('o'):
                self.open_shell()
            self.display()

    def setup_colors(self):
        curses.start_color()
        curses.use_default_colors()
        curses.init_pair(1, curses.COLOR_BLUE, -1)  # Directories
        curses.init_pair(2, curses.COLOR_RED, -1)   # Errors
        curses.init_pair(3, curses.COLOR_GREEN, -1) # Files
        curses.init_pair(4, curses.COLOR_CYAN, -1)  # Bookmarks

    def display(self):
        self.screen.clear()
        height, width = self.screen.getmaxyx()
        entries = self.get_display_entries()
        for idx, entry in enumerate(entries):
            if idx >= height - 2:
                break
            line = self.format_entry(entry, selected=(idx == self.selected_index))
            self.screen.addstr(idx, 0, line)
        self.screen.addstr(height - 1, 0, self.status_line())
        self.screen.refresh()

    def get_display_entries(self) -> List[FileEntry]:
        entries = self.analyzer.sort_entries(self.current_entry.children)
        return entries

    def format_entry(self, entry: FileEntry, selected: bool = False) -> str:
        mark = '>'
        if selected:
            mark = '>'
        else:
            mark = ' '
        percentage = f"{entry.percentage:.2f}%"
        size = self.format_size(entry.size)
        name = entry.name
        if entry.is_dir:
            color = curses.color_pair(1)
            name += '/'
        else:
            color = curses.color_pair(3)
        if entry.permission_denied:
            color = curses.color_pair(2)
            name += ' [Permission Denied]'
        if entry.path in self.analyzer.bookmarks:
            color = curses.color_pair(4)
            name += ' *'
        line = f"{mark} {percentage:>6} {size:>10} {name}"
        return line

    @staticmethod
    def format_size(size: int) -> str:
        units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
        for unit in units:
            if size < 1024:
                return f"{size:.2f}{unit}"
            size /= 1024
        return f"{size:.2f}EB"

    def move_selection(self, offset: int):
        entries = self.get_display_entries()
        self.selected_index = max(0, min(self.selected_index + offset, len(entries) - 1))

    def enter_directory(self):
        entries = self.get_display_entries()
        if entries:
            selected_entry = entries[self.selected_index]
            if selected_entry.is_dir and not selected_entry.permission_denied:
                self.path_stack.append((self.current_entry, self.selected_index))
                self.current_entry = selected_entry
                self.selected_index = 0

    def go_back(self):
        if self.path_stack:
            self.current_entry, self.selected_index = self.path_stack.pop()
        else:
            self.running = False

    def delete_entry(self):
        entries = self.get_display_entries()
        if entries:
            selected_entry = entries[self.selected_index]
            if self.confirm_action(f"Delete {selected_entry.name}?"):
                try:
                    if selected_entry.is_dir:
                        shutil.rmtree(selected_entry.path)
                    else:
                        os.remove(selected_entry.path)
                    self.current_entry.children.remove(selected_entry)
                    self.selected_index = max(0, self.selected_index - 1)
                except Exception as e:
                    self.show_error(str(e))

    def change_sort(self):
        options = ['size', 'name', 'date']
        current_idx = options.index(self.analyzer.sort_by)
        new_idx = (current_idx + 1) % len(options)
        self.analyzer.sort_by = options[new_idx]

    def toggle_hidden(self):
        self.analyzer.show_hidden = not self.analyzer.show_hidden
        self.rescan_current_directory()

    def rescan_current_directory(self):
        self.current_entry = asyncio.run(self.analyzer.scan_directory(self.current_entry.path))

    def rescan(self):
        self.analyzer.scan()
        self.current_entry = self.analyzer.root_entry
        self.selected_index = 0
        self.path_stack = []

    def search(self):
        query = self.get_input("Search:")
        if query:
            self.search_query = query
            self.search_results = self.find_entries(self.analyzer.root_entry, query)
            if self.search_results:
                self.current_entry = self.search_results[0]
                self.path_stack = []
                self.selected_index = 0
            else:
                self.show_message("No results found.")

    def find_entries(self, entry: FileEntry, query: str) -> List[FileEntry]:
        results = []
        if query.lower() in entry.name.lower():
            results.append(entry)
        for child in entry.children:
            results.extend(self.find_entries(child, query))
        return results

    def preview_file(self):
        entries = self.get_display_entries()
        if entries:
            selected_entry = entries[self.selected_index]
            if not selected_entry.is_dir:
                try:
                    with open(selected_entry.path, 'r') as f:
                        content = f.read()
                    self.show_message(content, title=selected_entry.name)
                except Exception as e:
                    self.show_error(str(e))

    def export_report(self):
        format = self.get_input("Export format (json/csv):")
        if format.lower() in ['json', 'csv']:
            self.analyzer.export_report(format=format.lower())
            self.show_message(f"Report exported as disk_usage_report.{format.lower()}")

    def toggle_bookmark(self):
        entries = self.get_display_entries()
        if entries:
            selected_entry = entries[self.selected_index]
            self.analyzer.toggle_bookmark(selected_entry.path)

    def toggle_graph(self):
        self.show_graph = not self.show_graph

    def change_theme(self):
        # Placeholder for theme change functionality
        pass

    def toggle_collapse(self):
        # Placeholder for collapse/expand functionality
        pass

    def show_info(self):
        entries = self.get_display_entries()
        if entries:
            selected_entry = entries[self.selected_index]
            info = f"Name: {selected_entry.name}\n"
            info += f"Path: {selected_entry.path}\n"
            info += f"Size: {self.format_size(selected_entry.size)}\n"
            info += f"Last Modified: {selected_entry.last_modified}\n"
            info += f"Is Directory: {selected_entry.is_dir}\n"
            self.show_message(info, title="Info")

    def open_shell(self):
        entries = self.get_display_entries()
        if entries:
            selected_entry = entries[self.selected_index]
            path = selected_entry.path if selected_entry.is_dir else os.path.dirname(selected_entry.path)
            self.running = False
            os.chdir(path)
            os.system(os.environ.get('SHELL', '/bin/bash'))
            self.running = True

    def status_line(self) -> str:
        sort_info = f"Sort: {self.analyzer.sort_by.capitalize()}"
        hidden_info = "Hidden: On" if self.analyzer.show_hidden else "Hidden: Off"
        return f"{sort_info} | {hidden_info} | Press 'q' to quit"

    def confirm_action(self, message: str) -> bool:
        self.screen.clear()
        self.screen.addstr(0, 0, message + " (y/n)")
        self.screen.refresh()
        while True:
            key = self.screen.getch()
            if key in [ord('y'), ord('Y')]:
                return True
            elif key in [ord('n'), ord('N')]:
                return False

    def show_message(self, message: str, title: str = ""):
        self.screen.clear()
        if title:
            self.screen.addstr(0, 0, title, curses.A_BOLD)
        self.screen.addstr(1, 0, message)
        self.screen.addstr(self.screen.getmaxyx()[0]-1, 0, "Press any key to continue...")
        self.screen.refresh()
        self.screen.getch()

    def show_error(self, message: str):
        self.show_message("Error: " + message)

    def get_input(self, prompt: str) -> str:
        curses.echo()
        self.screen.clear()
        self.screen.addstr(0, 0, prompt)
        self.screen.refresh()
        input_str = self.screen.getstr(1, 0).decode('utf-8')
        curses.noecho()
        return input_str

    def get_input_char(self, prompt: str) -> str:
        self.screen.clear()
        self.screen.addstr(0, 0, prompt)
        self.screen.refresh()
        key = self.screen.getch()
        return chr(key)


def main():
    parser = argparse.ArgumentParser(description="Python-based ncdu-like Disk Usage Analyzer")
    parser.add_argument("path", nargs="?", default=".", help="Path to scan")
    parser.add_argument("-H", "--show-hidden", action="store_true", help="Show hidden files and directories")
    args = parser.parse_args()

    analyzer = DiskUsageAnalyzer(path=args.path, show_hidden=args.show_hidden)
    analyzer.scan()
    analyzer.calculate_percentages(analyzer.root_entry, analyzer.root_entry.size)

    tui = NcduTUI(analyzer)
    tui.start()

if __name__ == "__main__":
    main()