"""
CHRONOS AST-Based Cryptographic Code Scanner
=============================================

Advanced code analysis using Abstract Syntax Tree parsing for detecting
cryptographic implementations, patterns, and security recommendations.
Supports Python and Java code with dependency analysis and SARIF output.
"""

import ast
import json
import re
from pathlib import Path
from dataclasses import dataclass, field, asdict
from typing import List, Dict, Optional, Set, Tuple, Any
from enum import Enum
from datetime import datetime
from collections import defaultdict


class CryptoAlgorithm(Enum):
    """Cryptographic algorithms detected."""
    RSA = "RSA"
    ECDSA = "ECDSA"
    DSA = "DSA"
    AES_GCM = "AES-GCM"
    AES_ECB = "AES-ECB"
    DES = "DES"
    MD5 = "MD5"
    SHA1 = "SHA1"
    HMAC = "HMAC"
    PBE = "PBE"


class RiskLevel(Enum):
    """Risk severity levels."""
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"
    INFO = "info"


class MigrationSuggestion(Enum):
    """Types of migration suggestions."""
    ALGORITHM_UPGRADE = "algorithm_upgrade"
    MODE_UPGRADE = "mode_upgrade"
    PQC_REPLACEMENT = "pqc_replacement"
    LIBRARY_UPDATE = "library_update"
    BEST_PRACTICE = "best_practice"


@dataclass
class CodeLocation:
    """Location of detected code."""
    file_path: str
    line: int
    column: int
    end_line: Optional[int] = None
    end_column: Optional[int] = None
    code_snippet: str = ""


@dataclass
class CryptoUsage:
    """Detected cryptographic usage."""
    algorithm: CryptoAlgorithm
    usage_type: str  # "key_generation", "encryption", "signing", etc.
    location: CodeLocation
    context: Dict[str, Any] = field(default_factory=dict)
    risk_level: RiskLevel = RiskLevel.MEDIUM
    recommendation: Optional[str] = None


@dataclass
class Dependency:
    """Python/Java dependency with version."""
    name: str
    version: Optional[str] = None
    is_crypto: bool = False
    has_pqc: bool = False
    latest_version: Optional[str] = None
    source: str = "requirements.txt"  # requirements.txt, pom.xml, build.gradle, etc.


@dataclass
class MigrationPlan:
    """Migration plan for a single item."""
    from_algorithm: str
    to_algorithm: str
    location: CodeLocation
    priority: str  # critical, high, medium, low
    effort_hours: float
    timeline_months: float
    steps: List[str] = field(default_factory=list)
    suggestion_type: MigrationSuggestion = MigrationSuggestion.ALGORITHM_UPGRADE


@dataclass
class DependencyNode:
    """Node in dependency graph."""
    name: str
    version: str
    is_crypto: bool
    dependencies: List[str] = field(default_factory=list)


@dataclass
class ScanResult:
    """Complete code scan result."""
    scan_time: datetime
    file_path: str
    language: str
    crypto_usages: List[CryptoUsage] = field(default_factory=list)
    dependencies: List[Dependency] = field(default_factory=list)
    migration_plans: List[MigrationPlan] = field(default_factory=list)
    vulnerable_patterns: List[Dict[str, Any]] = field(default_factory=list)
    dependency_graph: Dict[str, DependencyNode] = field(default_factory=dict)
    total_risk_score: float = 0.0


class CryptoPatternDetector(ast.NodeVisitor):
    """AST visitor for detecting cryptographic patterns in Python code."""
    
    # RSA patterns
    RSA_PATTERNS = {
        'RSA': ['RSA', 'rsa'],
        'OAEP': ['OAEP', 'oaep'],
        'PSS': ['PSS', 'pss'],
        'PKCS1v15': ['PKCS1v15', 'pkcs1v15'],
        'generate_private_key': ['generate_private_key'],
        'rsa_key_sizes': [1024, 2048, 3072, 4096],
    }
    
    # ECDSA patterns
    ECDSA_PATTERNS = {
        'ECDSA': ['ECDSA', 'ecdsa'],
        'EC': ['EC', 'ec'],
        'curves': ['SECP256R1', 'SECP384R1', 'SECP521R1', 'SECT163R2'],
    }
    
    # AES patterns
    AES_PATTERNS = {
        'AES_GCM': ['AES', 'GCM', 'aes', 'gcm'],
        'AES_ECB': ['AES', 'ECB', 'aes', 'ecb'],
        'AES_CBC': ['AES', 'CBC', 'aes', 'cbc'],
        'AES_CTR': ['AES', 'CTR', 'aes', 'ctr'],
    }
    
    # Weak patterns
    WEAK_PATTERNS = {
        'DES': ['DES', 'des'],
        'MD5': ['MD5', 'md5', 'md5()'],
        'SHA1': ['SHA1', 'sha1', 'SHA-1'],
        'ECB': ['ECB', 'ecb'],
        'PKCS1v15': ['PKCS1v15'],
    }
    
    CRYPTO_LIBRARIES = {
        'cryptography': True,
        'pycryptodome': True,
        'pyca': True,
        'tink': True,
        'libnacl': True,
        'cryptodomex': True,
        'Cryptodome': True,
    }
    
    def __init__(self, file_path: str, source_code: str):
        self.file_path = file_path
        self.source_code = source_code
        self.lines = source_code.split('\n')
        self.usages: List[CryptoUsage] = []
        self.current_context = {}
    
    def get_code_snippet(self, line: int, context_lines: int = 2) -> str:
        """Get code snippet around line."""
        start = max(0, line - 1 - context_lines)
        end = min(len(self.lines), line + context_lines)
        return '\n'.join(self.lines[start:end])
    
    def visit_Import(self, node: ast.Import):
        """Handle import statements."""
        for alias in node.names:
            module = alias.name
            # Check for crypto libraries
            for lib in self.CRYPTO_LIBRARIES:
                if lib in module.lower():
                    self.current_context['crypto_library'] = module
                    break
        self.generic_visit(node)
    
    def visit_ImportFrom(self, node: ast.ImportFrom):
        """Handle from...import statements."""
        if node.module:
            for lib in self.CRYPTO_LIBRARIES:
                if lib in node.module.lower():
                    self.current_context['crypto_library'] = node.module
                    self.current_context['imported_items'] = [
                        alias.name for alias in node.names
                    ]
                    break
        self.generic_visit(node)
    
    def visit_Call(self, node: ast.Call):
        """Detect function calls to crypto operations."""
        func_name = self._get_function_name(node.func)
        
        if func_name:
            # RSA detection
            if any(pattern in func_name for pattern in self.RSA_PATTERNS.get('RSA', [])):
                self._add_usage('RSA', 'key_generation', node, func_name)
            
            # ECDSA detection
            elif any(pattern in func_name for pattern in self.ECDSA_PATTERNS.get('ECDSA', [])):
                self._add_usage('ECDSA', 'key_generation', node, func_name)
            
            # AES-GCM detection
            elif all(p in func_name.upper() for p in ['AES', 'GCM']):
                self._add_usage('AES_GCM', 'encryption', node, func_name)
            
            # Weak pattern detection
            elif any(pattern in func_name for pattern in self.WEAK_PATTERNS.get('MD5', [])):
                self._add_usage('MD5', 'hashing', node, func_name, RiskLevel.HIGH, 
                              "MD5 is cryptographically broken. Use SHA-256 or SHA-3 instead.")
            
            elif any(pattern in func_name for pattern in self.WEAK_PATTERNS.get('SHA1', [])):
                self._add_usage('SHA1', 'hashing', node, func_name, RiskLevel.HIGH,
                              "SHA-1 is deprecated. Use SHA-256 or SHA-3 instead.")
            
            elif any(pattern in func_name for pattern in self.WEAK_PATTERNS.get('DES', [])):
                self._add_usage('DES', 'encryption', node, func_name, RiskLevel.CRITICAL,
                              "DES is insecure. Use AES-256-GCM instead.")
        
        self.generic_visit(node)
    
    def visit_Assign(self, node: ast.Assign):
        """Detect variable assignments with crypto values."""
        # Check assignments with string patterns
        if isinstance(node.value, ast.Constant) and isinstance(node.value.value, str):
            value_str = node.value.value.upper()
            
            # Detect algorithm names in strings
            if 'RSA' in value_str:
                self._add_usage('RSA', 'configuration', node, 'algorithm_string')
            elif 'ECDSA' in value_str:
                self._add_usage('ECDSA', 'configuration', node, 'algorithm_string')
            elif 'AES' in value_str and 'GCM' in value_str:
                self._add_usage('AES_GCM', 'configuration', node, 'mode_string')
        
        self.generic_visit(node)
    
    def _get_function_name(self, node: ast.expr) -> Optional[str]:
        """Extract function name from AST node."""
        if isinstance(node, ast.Name):
            return node.id
        elif isinstance(node, ast.Attribute):
            parts = []
            current = node
            while isinstance(current, ast.Attribute):
                parts.append(current.attr)
                current = current.value
            if isinstance(current, ast.Name):
                parts.append(current.id)
            return '.'.join(reversed(parts))
        return None
    
    def _add_usage(self, algorithm: str, usage_type: str, node: ast.AST,
                  context: str, risk: RiskLevel = RiskLevel.MEDIUM,
                  recommendation: Optional[str] = None):
        """Add detected crypto usage."""
        location = CodeLocation(
            file_path=self.file_path,
            line=node.lineno,
            column=node.col_offset,
            end_line=node.end_lineno,
            end_column=node.end_col_offset,
            code_snippet=self.get_code_snippet(node.lineno)
        )
        
        usage = CryptoUsage(
            algorithm=CryptoAlgorithm[algorithm] if algorithm in CryptoAlgorithm.__members__ else CryptoAlgorithm.RSA,
            usage_type=usage_type,
            location=location,
            context={'library': self.current_context.get('crypto_library'), 'pattern': context},
            risk_level=risk,
            recommendation=recommendation
        )
        
        self.usages.append(usage)


class CodeScanner:
    """Main code scanner for detecting cryptographic patterns."""
    
    # Common PQC replacements
    PQC_REPLACEMENTS = {
        'RSA': 'ML-KEM (Kyber)',
        'ECDSA': 'ML-DSA (Dilithium)',
        'DSA': 'ML-DSA (Dilithium)',
        'AES-GCM': 'AES-GCM (still safe, but consider hybrid)',
    }
    
    # Crypto library mapping
    CRYPTO_LIBRARIES_PQC = {
        'cryptography': {'pqc': False, 'latest': '43.0.0', 'hybrid_support': True},
        'liboqs-python': {'pqc': True, 'latest': '0.9.0', 'hybrid_support': True},
        'pqcrypto': {'pqc': True, 'latest': '0.1.0', 'hybrid_support': False},
        'tink': {'pqc': False, 'latest': '1.7.0', 'hybrid_support': False},
    }
    
    def __init__(self, verbose: bool = False):
        self.verbose = verbose
        self.results: List[ScanResult] = []
        self.dependency_graph: Dict[str, DependencyNode] = {}
    
    def scan_file(self, file_path: str) -> Optional[ScanResult]:
        """Scan a single Python file for crypto patterns."""
        path = Path(file_path)
        
        if not path.exists():
            return None
        
        if path.suffix == '.py':
            return self._scan_python_file(file_path)
        elif path.suffix in ['.java', '.kt']:
            return self._scan_java_file(file_path)
        
        return None
    
    def _scan_python_file(self, file_path: str) -> ScanResult:
        """Scan Python file using AST."""
        with open(file_path, 'r', encoding='utf-8') as f:
            source_code = f.read()
        
        result = ScanResult(
            scan_time=datetime.now(),
            file_path=file_path,
            language='Python'
        )
        
        try:
            tree = ast.parse(source_code)
            detector = CryptoPatternDetector(file_path, source_code)
            detector.visit(tree)
            result.crypto_usages = detector.usages
        except SyntaxError as e:
            result.vulnerable_patterns.append({
                'type': 'syntax_error',
                'message': str(e),
                'line': e.lineno
            })
        
        # Generate migration plans
        result.migration_plans = self._generate_migration_plans(result.crypto_usages)
        
        # Calculate risk score
        result.total_risk_score = self._calculate_risk_score(result.crypto_usages)
        
        return result
    
    def _scan_java_file(self, file_path: str) -> ScanResult:
        """Scan Java file using regex patterns (simple implementation)."""
        with open(file_path, 'r', encoding='utf-8') as f:
            source_code = f.read()
        
        result = ScanResult(
            scan_time=datetime.now(),
            file_path=file_path,
            language='Java'
        )
        
        # Java crypto patterns
        java_patterns = {
            r'KeyPairGenerator\.getInstance\("RSA"\)': ('RSA', 'key_generation'),
            r'KeyPairGenerator\.getInstance\("DSA"\)': ('DSA', 'key_generation'),
            r'KeyPairGenerator\.getInstance\("EC"\)': ('ECDSA', 'key_generation'),
            r'Cipher\.getInstance\("AES/GCM/NoPadding"\)': ('AES_GCM', 'encryption'),
            r'Cipher\.getInstance\("DES"\)': ('DES', 'encryption'),
            r'MessageDigest\.getInstance\("MD5"\)': ('MD5', 'hashing'),
            r'MessageDigest\.getInstance\("SHA1"\)': ('SHA1', 'hashing'),
        }
        
        lines = source_code.split('\n')
        for line_num, line in enumerate(lines, 1):
            for pattern, (algo, usage_type) in java_patterns.items():
                if re.search(pattern, line):
                    location = CodeLocation(
                        file_path=file_path,
                        line=line_num,
                        column=0,
                        code_snippet=line.strip()
                    )
                    
                    risk = RiskLevel.HIGH if algo in ['DES', 'MD5', 'SHA1'] else RiskLevel.MEDIUM
                    
                    usage = CryptoUsage(
                        algorithm=CryptoAlgorithm[algo],
                        usage_type=usage_type,
                        location=location,
                        risk_level=risk
                    )
                    
                    result.crypto_usages.append(usage)
        
        result.migration_plans = self._generate_migration_plans(result.crypto_usages)
        result.total_risk_score = self._calculate_risk_score(result.crypto_usages)
        
        return result
    
    def scan_directory(self, directory: str) -> List[ScanResult]:
        """Scan all Python/Java files in directory."""
        results = []
        path = Path(directory)
        
        for file_path in path.rglob('*.py'):
            if '__pycache__' not in str(file_path):
                result = self.scan_file(str(file_path))
                if result and result.crypto_usages:
                    results.append(result)
        
        for file_path in path.rglob('*.java'):
            result = self.scan_file(str(file_path))
            if result and result.crypto_usages:
                results.append(result)
        
        self.results = results
        return results
    
    def analyze_dependencies(self, requirements_file: str) -> List[Dependency]:
        """Analyze Python dependencies from requirements.txt."""
        dependencies = []
        
        if not Path(requirements_file).exists():
            return dependencies
        
        with open(requirements_file, 'r') as f:
            for line in f:
                line = line.strip()
                if not line or line.startswith('#'):
                    continue
                
                # Parse requirement line
                match = re.match(r'([a-zA-Z0-9\-_]+)(?:==|>=|<=|>|<|~=)?(.+)?', line)
                if not match:
                    continue
                
                name = match.group(1)
                version = match.group(2) if match.group(2) else None
                
                # Check if crypto library
                is_crypto = any(
                    crypto_lib.lower() in name.lower()
                    for crypto_lib in self.CRYPTO_LIBRARIES_PQC
                )
                
                dep = Dependency(
                    name=name,
                    version=version,
                    is_crypto=is_crypto,
                    has_pqc=is_crypto and self.CRYPTO_LIBRARIES_PQC.get(name, {}).get('pqc', False),
                    source='requirements.txt'
                )
                
                dependencies.append(dep)
        
        return dependencies
    
    def build_dependency_graph(self, dependencies: List[Dependency]) -> Dict[str, DependencyNode]:
        """Build dependency graph from requirements."""
        graph = {}
        
        for dep in dependencies:
            node = DependencyNode(
                name=dep.name,
                version=dep.version or 'unknown',
                is_crypto=dep.is_crypto,
                dependencies=[]
            )
            graph[dep.name] = node
        
        return graph
    
    def _generate_migration_plans(self, usages: List[CryptoUsage]) -> List[MigrationPlan]:
        """Generate migration plans for detected usages."""
        plans = []
        
        for usage in usages:
            algo = usage.algorithm.value
            
            # Determine migration priority and effort
            if algo in ['DES', 'MD5', 'SHA1']:
                priority = 'critical'
                effort = 4.0
                timeline = 1.0
            elif algo == 'RSA' and usage.risk_level in [RiskLevel.HIGH, RiskLevel.CRITICAL]:
                priority = 'high'
                effort = 8.0
                timeline = 2.0
            else:
                priority = 'medium'
                effort = 4.0
                timeline = 1.0
            
            # Get PQC replacement
            pqc_algo = self.PQC_REPLACEMENTS.get(algo, f'{algo} (modern variant)')
            
            plan = MigrationPlan(
                from_algorithm=algo,
                to_algorithm=pqc_algo,
                location=usage.location,
                priority=priority,
                effort_hours=effort,
                timeline_months=timeline,
                steps=self._generate_migration_steps(algo, pqc_algo),
                suggestion_type=self._get_suggestion_type(algo)
            )
            
            plans.append(plan)
        
        return plans
    
    def _generate_migration_steps(self, from_algo: str, to_algo: str) -> List[str]:
        """Generate specific migration steps."""
        steps = [
            f"1. Update import statements to use {to_algo}",
            f"2. Replace {from_algo} algorithm with {to_algo}",
            "3. Update key generation parameters",
            "4. Test cryptographic operations",
            "5. Validate performance impact",
            "6. Update documentation",
            "7. Deploy and monitor in production",
        ]
        return steps
    
    def _get_suggestion_type(self, algorithm: str) -> MigrationSuggestion:
        """Get suggestion type for algorithm."""
        if algorithm in ['RSA', 'ECDSA', 'DSA']:
            return MigrationSuggestion.PQC_REPLACEMENT
        elif algorithm in ['AES-GCM', 'AES-CBC']:
            return MigrationSuggestion.BEST_PRACTICE
        else:
            return MigrationSuggestion.ALGORITHM_UPGRADE
    
    def _calculate_risk_score(self, usages: List[CryptoUsage]) -> float:
        """Calculate overall risk score (0-100)."""
        if not usages:
            return 0.0
        
        risk_weights = {
            RiskLevel.CRITICAL: 25,
            RiskLevel.HIGH: 15,
            RiskLevel.MEDIUM: 8,
            RiskLevel.LOW: 3,
            RiskLevel.INFO: 1,
        }
        
        total_risk = sum(risk_weights.get(usage.risk_level, 0) for usage in usages)
        
        # Normalize to 0-100 scale
        return min(100.0, (total_risk / (len(usages) * 25)) * 100)
    
    def generate_sarif_report(self, results: Optional[List[ScanResult]] = None,
                             output_file: Optional[str] = None) -> Dict[str, Any]:
        """Generate SARIF (Static Analysis Results Interchange Format) report."""
        if results is None:
            results = self.results
        
        # SARIF structure
        sarif = {
            "version": "2.1.0",
            "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
            "runs": [
                {
                    "tool": {
                        "driver": {
                            "name": "CHRONOS-AST-Scanner",
                            "version": "1.0.0",
                            "informationUri": "https://github.com/chronos-quantum-security",
                            "rules": self._generate_sarif_rules(),
                        }
                    },
                    "results": []
                }
            ]
        }
        
        # Add results
        for result in results:
            for usage in result.crypto_usages:
                sarif_result = {
                    "ruleId": f"CRYPTO-{usage.algorithm.value}",
                    "level": usage.risk_level.value,
                    "message": {
                        "text": f"Detected {usage.algorithm.value} {usage.usage_type}",
                        "id": "detectCryptoUsage"
                    },
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": result.file_path
                                },
                                "region": {
                                    "startLine": usage.location.line,
                                    "startColumn": usage.location.column,
                                    "endLine": usage.location.end_line or usage.location.line,
                                    "endColumn": usage.location.end_column or usage.location.column + 1,
                                    "snippet": {
                                        "text": usage.location.code_snippet
                                    }
                                }
                            }
                        }
                    ],
                    "partialFingerprints": {
                        "primaryLocationLineHash": str(hash(usage.location.code_snippet))
                    }
                }
                
                if usage.recommendation:
                    sarif_result["message"]["markdown"] = f"**Recommendation:** {usage.recommendation}"
                
                sarif["runs"][0]["results"].append(sarif_result)
        
        # Write to file if specified
        if output_file:
            with open(output_file, 'w') as f:
                json.dump(sarif, f, indent=2)
        
        return sarif
    
    def _generate_sarif_rules(self) -> List[Dict[str, Any]]:
        """Generate SARIF rule definitions."""
        rules = []
        
        risk_to_level = {
            'RSA': 'note',
            'ECDSA': 'note',
            'AES-GCM': 'note',
            'DES': 'error',
            'MD5': 'error',
            'SHA1': 'warning',
        }
        
        for algo in CryptoAlgorithm:
            rules.append({
                "id": f"CRYPTO-{algo.value}",
                "shortDescription": {
                    "text": f"Detected {algo.value} cryptographic usage"
                },
                "fullDescription": {
                    "text": f"Analysis detected usage of {algo.value} cryptographic algorithm",
                    "markdown": f"**Algorithm:** {algo.value}\n\n"
                                f"Consider evaluating for quantum-safe alternatives."
                },
                "defaultConfiguration": {
                    "level": risk_to_level.get(algo.value, 'note')
                }
            })
        
        return rules
    
    def generate_refactoring_suggestions(self, results: Optional[List[ScanResult]] = None) -> List[Dict[str, Any]]:
        """Generate code refactoring suggestions with line numbers."""
        if results is None:
            results = self.results
        
        suggestions = []
        
        for result in results:
            for plan in result.migration_plans:
                suggestion = {
                    "file": plan.location.file_path,
                    "line": plan.location.line,
                    "column": plan.location.column,
                    "from_code": plan.location.code_snippet,
                    "from_algorithm": plan.from_algorithm,
                    "to_algorithm": plan.to_algorithm,
                    "priority": plan.priority,
                    "effort_hours": plan.effort_hours,
                    "steps": plan.steps,
                    "refactoring_pattern": self._generate_refactoring_pattern(
                        plan.from_algorithm,
                        plan.to_algorithm
                    )
                }
                suggestions.append(suggestion)
        
        return suggestions
    
    def _generate_refactoring_pattern(self, from_algo: str, to_algo: str) -> Dict[str, str]:
        """Generate refactoring code pattern."""
        patterns = {
            'RSA': {
                'old': 'from cryptography.hazmat.primitives.asymmetric import rsa\n'
                       'private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)',
                'new': 'from liboqs.python import KeyEncapsulation\n'
                       'client = KeyEncapsulation("ML-KEM-768")\n'
                       'public_key = client.generate_keypair()',
            },
            'ECDSA': {
                'old': 'from cryptography.hazmat.primitives.asymmetric import ec\n'
                       'private_key = ec.generate_private_key(ec.SECP256R1())',
                'new': 'from liboqs.python import Signature\n'
                       'signer = Signature("ML-DSA-65")\n'
                       'public_key = signer.generate_keypair()',
            },
        }
        
        return patterns.get(from_algo, {
            'old': f'# {from_algo} usage detected',
            'new': f'# Replace with {to_algo}',
        })


def scan_python_code(code: str, file_path: str = "code.py") -> List[CryptoUsage]:
    """Convenience function to scan Python code string."""
    try:
        tree = ast.parse(code)
        detector = CryptoPatternDetector(file_path, code)
        detector.visit(tree)
        return detector.usages
    except SyntaxError:
        return []


def analyze_project(project_path: str, verbose: bool = False) -> Dict[str, Any]:
    """Analyze entire project for crypto patterns."""
    scanner = CodeScanner(verbose=verbose)
    
    # Scan code files
    code_results = scanner.scan_directory(project_path)
    
    # Analyze dependencies
    requirements_file = Path(project_path) / 'requirements.txt'
    dependencies = scanner.analyze_dependencies(str(requirements_file))
    
    # Build dependency graph
    dep_graph = scanner.build_dependency_graph(dependencies)
    
    return {
        'code_results': code_results,
        'dependencies': dependencies,
        'dependency_graph': dep_graph,
        'total_usages': sum(len(r.crypto_usages) for r in code_results),
        'total_risk_score': sum(r.total_risk_score for r in code_results) / len(code_results) if code_results else 0,
        'migration_plans': sum((r.migration_plans for r in code_results), []),
    }
