# Encoding Table Generation System

## Problem Statement

Currently, encoding lookup tables require manual maintenance of both forward and reverse mappings:

```javascript
// Must maintain both of these manually
const PRODUCT_COLOR = {
    'BLACK': 1,
    'BRONZE': 2,
    // ... add new color here
};

const PRODUCT_COLOR_REVERSE = {
    1: 'BLACK',
    2: 'BRONZE',
    // ... and remember to add it here too!
};
```

**Issues:**
- ❌ Prone to human error (forgetting reverse mapping)
- ❌ Risk of mismatches between forward/reverse
- ❌ No validation for duplicate values
- ❌ No documentation of available slots
- ❌ Difficult for non-technical users to add values

## Proposed Solution: Python-Generated Tables

### Architecture

```
User edits simple source file
         ↓
Python validation script
         ↓
Generate JavaScript + Python constants
         ↓
Auto-served by Flask (or manual build step)
```

### File Structure

```
app/
  data/
    encoding_values.json (or .yaml)  ← USER EDITS THIS
  js/
    encoding_tables.js               ← AUTO-GENERATED
  generate_encoding_tables.py        ← GENERATOR SCRIPT
```

## Source File Format

### Option 1: JSON with Comments Support

**`app/data/encoding_values.json`**
```json
{
  "metadata": {
    "version": 0,
    "description": "Product encoding value definitions"
  },
  "tables": {
    "product_type": {
      "max_value": 15,
      "bit_size": 4,
      "values": [
        {"name": "PATIO_DOOR", "value": 1, "display": "Patio Door"},
        {"name": "STORM_DOOR", "value": 3, "display": "Storm Door"},
        {"name": "STORM_WINDOW", "value": 5, "display": "Storm Window"},
        {"name": "PRIMARY_WINDOW", "value": 7, "display": "Primary Window"}
      ]
    },
    "product_color": {
      "max_value": 15,
      "bit_size": 4,
      "values": [
        {"name": "BLACK", "value": 1, "display": "Black"},
        {"name": "BRONZE", "value": 2, "display": "Bronze"},
        {"name": "SANDSTONE", "value": 3, "display": "Sandstone"},
        {"name": "WHITE", "value": 4, "display": "White"},
        {"name": "TAN", "value": 5, "display": "Tan"},
        {"name": "MILL", "value": 6, "display": "Mill"}
      ]
    },
    "product_material": {
      "max_value": 15,
      "bit_size": 4,
      "values": [
        {"name": "ALUMINUM", "value": 1, "display": "Aluminum"},
        {"name": "VINYL", "value": 2, "display": "Vinyl"}
      ]
    },
    "hardware_type": {
      "max_value": 31,
      "bit_size": 5,
      "values": [
        {"name": "NONE", "value": 0, "display": "None"},
        {"name": "LEVER", "value": 1, "display": "Lever"},
        {"name": "PULL", "value": 2, "display": "Pull"},
        {"name": "PULL_HANDLE", "value": 3, "display": "Pull Handle"},
        {"name": "DEADBOLT", "value": 4, "display": "Deadbolt"},
        {"name": "HINGE", "value": 5, "display": "Hinge"}
      ]
    },
    "hardware_style": {
      "max_value": 31,
      "bit_size": 5,
      "values": [
        {"name": "NONE", "value": 0, "display": "None"},
        {"name": "STANDARD", "value": 1, "display": "Standard"},
        {"name": "PUSH", "value": 2, "display": "Push"},
        {"name": "ALTERNATIVE", "value": 3, "display": "Alternative"},
        {"name": "CONTEMPORARY", "value": 4, "display": "Contemporary"},
        {"name": "TRADITIONAL", "value": 5, "display": "Traditional"}
      ]
    },
    "hardware_color": {
      "max_value": 31,
      "bit_size": 5,
      "values": [
        {"name": "NONE", "value": 0, "display": "None"},
        {"name": "BRASS", "value": 1, "display": "Brass"},
        {"name": "WHITE", "value": 2, "display": "White"},
        {"name": "BLACK", "value": 3, "display": "Black"},
        {"name": "SATIN", "value": 4, "display": "Satin"},
        {"name": "NICKEL", "value": 5, "display": "Nickel"},
        {"name": "BRONZE", "value": 6, "display": "Bronze"}
      ]
    }
  }
}
```

### Option 2: YAML (Better for Comments)

**`app/data/encoding_values.yaml`**
```yaml
version: 0
description: Product encoding value definitions

tables:
  product_type:
    max_value: 15
    bit_size: 4
    values:
      - name: PATIO_DOOR
        value: 1
        display: Patio Door
      - name: STORM_DOOR
        value: 3
        display: Storm Door
      # Reserved: 2, 4, 6, 8-15
      
  product_color:
    max_value: 15
    bit_size: 4
    values:
      - name: BLACK
        value: 1
        display: Black
      - name: BRONZE
        value: 2
        display: Bronze
      # ... etc
      # Reserved: 7-15 (9 slots available)
```

## User Workflow

### Adding a New Color

**Before (Manual):**
1. Open `encoding_helper.js`
2. Find `PRODUCT_COLOR` object
3. Add new entry with unused value
4. Find `PRODUCT_COLOR_REVERSE` object
5. Add matching reverse entry
6. Hope you didn't make a mistake

**After (Generated):**
1. Open `encoding_values.json`
2. Add one line: `{"name": "CHARCOAL", "value": 7, "display": "Charcoal"}`
3. Run `python generate_encoding_tables.py` (or auto-regenerated by Flask)
4. Done! JavaScript and Python constants updated

## Generator Script

**`app/generate_encoding_tables.py`**

```python
#!/usr/bin/env python3
"""
Generate JavaScript and Python encoding tables from source JSON/YAML.

Usage:
    python generate_encoding_tables.py
    
    Or add to Flask app for auto-generation on file change.
"""

import json
import os
from datetime import datetime
from pathlib import Path

def load_source_file(filepath):
    """Load encoding values from JSON or YAML."""
    with open(filepath, 'r') as f:
        if filepath.endswith('.yaml') or filepath.endswith('.yml'):
            import yaml
            return yaml.safe_load(f)
        else:
            return json.load(f)

def validate_table(table_name, table_data):
    """Validate a single table for errors."""
    errors = []
    max_value = table_data['max_value']
    values = table_data['values']
    
    used_values = set()
    used_names = set()
    
    for item in values:
        name = item['name']
        value = item['value']
        
        # Check for duplicate values
        if value in used_values:
            errors.append(f"{table_name}: Duplicate value {value}")
        used_values.add(value)
        
        # Check for duplicate names
        if name in used_names:
            errors.append(f"{table_name}: Duplicate name '{name}'")
        used_names.add(name)
        
        # Check value range
        if value < 0 or value > max_value:
            errors.append(f"{table_name}: Value {value} out of range (0-{max_value})")
        
        # Validate naming convention (uppercase with underscores)
        if not name.isupper() or not name.replace('_', '').isalpha():
            errors.append(f"{table_name}: Invalid name format '{name}' (use UPPERCASE_WITH_UNDERSCORES)")
    
    # Report available slots
    available = sorted(set(range(max_value + 1)) - used_values)
    info = f"{table_name}: {len(used_values)}/{max_value + 1} slots used, {len(available)} available"
    if available and len(available) <= 10:
        info += f" {available}"
    
    return errors, info

def generate_javascript(data, output_path):
    """Generate JavaScript constants file."""
    lines = [
        "// AUTO-GENERATED FILE - DO NOT EDIT MANUALLY",
        "// Generated from: app/data/encoding_values.json",
        f"// Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
        "//",
        "// To add new values:",
        "//   1. Edit app/data/encoding_values.json",
        "//   2. Run: python app/generate_encoding_tables.py",
        "//   3. Restart Flask server (or it auto-reloads)",
        "",
        "// ============================================================================",
        "// ENCODING CONSTANTS",
        "// ============================================================================",
        "",
        f"const ENCODING_VERSION = {data['metadata']['version']};",
        ""
    ]
    
    for table_name, table_data in data['tables'].items():
        const_name = table_name.upper()
        
        # Generate forward mapping
        lines.append(f"// {const_name} (max: {table_data['max_value']}, bits: {table_data['bit_size']})")
        lines.append(f"const {const_name} = {{")
        for item in table_data['values']:
            lines.append(f"    '{item['name']}': {item['value']},")
        lines.append("};")
        lines.append("")
        
        # Generate reverse mapping
        lines.append(f"const {const_name}_REVERSE = {{")
        for item in table_data['values']:
            lines.append(f"    {item['value']}: '{item['name']}',")
        lines.append("};")
        lines.append("")
        
        # Generate display mapping
        lines.append(f"const {const_name}_DISPLAY = {{")
        for item in table_data['values']:
            lines.append(f"    '{item['name']}': '{item['display']}',")
        lines.append("};")
        lines.append("")
    
    # Write file
    with open(output_path, 'w') as f:
        f.write('\n'.join(lines))
    
    print(f"✓ Generated: {output_path}")

def generate_python(data, output_path):
    """Generate Python constants file (optional)."""
    lines = [
        '"""',
        "AUTO-GENERATED FILE - DO NOT EDIT MANUALLY",
        "Generated from: app/data/encoding_values.json",
        f"Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
        '"""',
        "",
        f"ENCODING_VERSION = {data['metadata']['version']}",
        ""
    ]
    
    for table_name, table_data in data['tables'].items():
        const_name = table_name.upper()
        
        lines.append(f"# {const_name} (max: {table_data['max_value']}, bits: {table_data['bit_size']})")
        lines.append(f"{const_name} = {{")
        for item in table_data['values']:
            lines.append(f"    '{item['name']}': {item['value']},")
        lines.append("}")
        lines.append("")
        
        lines.append(f"{const_name}_REVERSE = {{")
        for item in table_data['values']:
            lines.append(f"    {item['value']}: '{item['name']}',")
        lines.append("}")
        lines.append("")
    
    with open(output_path, 'w') as f:
        f.write('\n'.join(lines))
    
    print(f"✓ Generated: {output_path}")

def generate_documentation(data, output_path):
    """Generate markdown documentation."""
    lines = [
        "# Encoding Values Reference",
        "",
        f"**Version:** {data['metadata']['version']}  ",
        f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}  ",
        "",
        "## Overview",
        "",
        "This document lists all currently defined encoding values.",
        "",
        "## Quick Reference",
        ""
    ]
    
    for table_name, table_data in data['tables'].items():
        lines.append(f"### {table_name.replace('_', ' ').title()}")
        lines.append("")
        lines.append(f"**Capacity:** {len(table_data['values'])}/{table_data['max_value'] + 1} slots used  ")
        lines.append(f"**Bit Size:** {table_data['bit_size']} bits  ")
        lines.append("")
        lines.append("| Value | Name | Display |")
        lines.append("|-------|------|---------|")
        
        for item in sorted(table_data['values'], key=lambda x: x['value']):
            lines.append(f"| {item['value']} | `{item['name']}` | {item['display']} |")
        
        lines.append("")
    
    with open(output_path, 'w') as f:
        f.write('\n'.join(lines))
    
    print(f"✓ Generated: {output_path}")

def main():
    """Main generation function."""
    base_dir = Path(__file__).parent
    source_file = base_dir / 'data' / 'encoding_values.json'
    
    # Load source data
    print(f"Loading: {source_file}")
    data = load_source_file(source_file)
    
    # Validate all tables
    print("\nValidating tables...")
    all_errors = []
    for table_name, table_data in data['tables'].items():
        errors, info = validate_table(table_name, table_data)
        print(f"  {info}")
        all_errors.extend(errors)
    
    if all_errors:
        print("\n❌ VALIDATION ERRORS:")
        for error in all_errors:
            print(f"  - {error}")
        return 1
    
    print("\n✓ All tables valid")
    
    # Generate files
    print("\nGenerating files...")
    generate_javascript(data, base_dir / 'js' / 'encoding_tables.js')
    generate_python(data, base_dir / 'encoding_tables_constants.py')
    generate_documentation(data, base_dir.parent / 'planning' / 'ENCODING_VALUES_REFERENCE.md')
    
    print("\n✓ Generation complete!")
    return 0

if __name__ == '__main__':
    exit(main())
```

## Flask Integration (Auto-Generation)

Add to `app.py`:

```python
import os
from datetime import datetime

@app.before_request
def check_encoding_tables():
    """Auto-regenerate encoding tables if source has changed."""
    source_file = 'data/encoding_values.json'
    output_file = 'js/encoding_tables.js'
    
    # Check if regeneration needed
    if not os.path.exists(output_file):
        print("Encoding tables not found, generating...")
        os.system('python generate_encoding_tables.py')
    else:
        source_mtime = os.path.getmtime(source_file)
        output_mtime = os.path.getmtime(output_file)
        
        if source_mtime > output_mtime:
            print("Encoding values changed, regenerating tables...")
            os.system('python generate_encoding_tables.py')
```

## Benefits

### For Non-Technical Users
- ✅ Edit simple JSON file (no JavaScript knowledge needed)
- ✅ Clear structure with examples
- ✅ Automatic validation catches errors
- ✅ Can't create mismatches between forward/reverse

### For Developers
- ✅ Single source of truth
- ✅ Auto-generated documentation
- ✅ Validation prevents bugs
- ✅ Easy to add new tables
- ✅ Can generate for multiple targets (JS, Python, docs)

### For Maintenance
- ✅ Version control friendly (one file to track)
- ✅ Easy to review changes (just JSON diff)
- ✅ Reports available slots automatically
- ✅ Enforces naming conventions

## Migration Path

### Phase 1: Create Source File
1. Extract existing values to `encoding_values.json`
2. Test generator script
3. Verify output matches current `encoding_helper.js`

### Phase 2: Integrate Generator
1. Add generator script to project
2. Update build process / Flask app
3. Test auto-generation

### Phase 3: Switch to Generated Tables
1. Update `encoding_helper.js` to import generated file
2. Remove manual mappings
3. Update documentation

### Phase 4: User Documentation
1. Create guide for adding new values
2. Document validation rules
3. Add examples

## Future Enhancements

- Web UI for editing values (no need to edit JSON directly)
- Export to CSV for client review
- Import from Excel/CSV
- Visual capacity indicators
- Conflict detection across tables
- Automatic value assignment (find next available slot)

## Related Files

- [ENCODING_SYSTEM.md](ENCODING_SYSTEM.md) - Technical specification
- `app/js/encoding_helper.js` - Current manual implementation
- `app/js/encoding_tables.js` - Future generated file

---

**Status:** Planning  
**Priority:** Medium  
**Effort:** ~4-6 hours implementation + testing  
**Dependencies:** None
