from pathlib import Path
import tokenize

package_directory = "./pyflowchart"

def analyze_package(package_path):
  # Summary counters
  total_files = 0
  total_blank = 0
  total_comment = 0
  total_code = 0
  
  base_path = Path(package_path)
  ignored_folders = {'.venv', 'env', '__pycache__', 'tests', '.git', 'build', 'dist'}
  
  for file_path in base_path.rglob('*.py'):
    if any(part in ignored_folders for part in file_path.parts):
      continue
      
    try:
      with open(file_path, 'rb') as f:
        tokens = list(tokenize.tokenize(f.readline))
        
      total_files += 1
      
      # Read physical lines to find pure blank lines
      with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
        lines = f.readlines()
      
      # Track types per line number
      line_count = len(lines)
      blank_lines = set()
      comment_lines = set()
      code_lines = set()
      
      # 1. Identify physical blank lines
      for idx, line in enumerate(lines, start=1):
        if not line.strip():
          blank_lines.add(idx)
      
      # 2. Parse tokens to categorize remaining lines
      for token in tokens:
        start_line = token.start[0]
        end_line = token.end[0]
        
        if token.type == tokenize.COMMENT:
          # Capture single or multi-line comments
          for l in range(start_line, end_line + 1):
            if l not in blank_lines:
              comment_lines.add(l)
              
        elif token.type == tokenize.STRING:
          # Detect if string token is a standalone docstring
          # Check if it begins on a fresh line context (indentation or start of line)
          if token.string.startswith(('"""', "'''")):
            # Simple rule: if it looks like a docstring block, treat as comment info
            for l in range(start_line, end_line + 1):
              if l not in blank_lines:
                comment_lines.add(l)
                
        elif token.type not in (tokenize.NL, tokenize.NEWLINE, 
          tokenize.ENDMARKER, tokenize.ENCODING):
          # Any structural code token marks that line as code
          for l in range(start_line, end_line + 1):
            if l not in blank_lines and l not in comment_lines:
              code_lines.add(l)

      # Resolve lines with mixed code and trailing comments (count as code)
      for idx in range(1, line_count + 1):
        if idx in code_lines:
          total_code += 1
        elif idx in comment_lines:
          total_comment += 1
        elif idx in blank_lines:
          total_blank += 1
          
    except (OSError, tokenize.TokenError):
      continue
      
  # Print formatted output table
  print("-" * 61)
  print(f"{'Number of Files':<17} {'Blanks':<13} {'Comments':<15} {'Code Lines':<12}")
  print("-" * 61)
  print(f"{total_files:<17} {total_blank:<13} {total_comment:<15} {total_code:<12}")

# Run analysis on your target folder
analyze_package(package_directory)

