{% layout none %}


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ESTU Format Parser</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f8f9fa;
        }
        
        .container {
            background: white;
            border-radius: 8px;
            padding: 30px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        
        h1 {
            color: #2c3e50;
            text-align: center;
            margin-bottom: 30px;
        }
        
        .input-section {
            margin-bottom: 30px;
        }
        
        label {
            display: block;
            margin-bottom: 10px;
            font-weight: 600;
            color: #34495e;
        }
        
        textarea {
            width: 100%;
            height: 150px;
            padding: 15px;
            border: 2px solid #e1e8ed;
            border-radius: 6px;
            font-family: 'Courier New', monospace;
            font-size: 12px;
            resize: vertical;
            box-sizing: border-box;
        }
        
        textarea:focus {
            outline: none;
            border-color: #3498db;
        }
        
        .button-section {
            text-align: center;
            margin-bottom: 30px;
        }
        
        button {
            background: #3498db;
            color: white;
            border: none;
            padding: 12px 30px;
            border-radius: 6px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: background 0.3s;
        }
        
        button:hover {
            background: #2980b9;
        }
        
        button:disabled {
            background: #bdc3c7;
            cursor: not-allowed;
        }
        
        .results {
            margin-top: 30px;
        }
        
        .validation-status {
            padding: 15px;
            border-radius: 6px;
            margin-bottom: 20px;
            font-weight: 600;
        }
        
        .valid {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        
        .invalid {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        
        .record {
            margin-bottom: 30px;
            border: 1px solid #e1e8ed;
            border-radius: 6px;
            overflow: hidden;
        }
        
        .record-header {
            background: #34495e;
            color: white;
            padding: 15px;
            font-weight: 600;
        }
        
        .record-content {
            padding: 20px;
            background: #f8f9fa;
        }
        
        .field {
            margin-bottom: 8px;
            padding: 8px;
            background: white;
            border-radius: 4px;
            border-left: 4px solid #3498db;
            font-family: 'Courier New', monospace;
            font-size: 12px;
        }
        
        .field-number {
            color: #e74c3c;
            font-weight: bold;
        }
        
        .field-description {
            color: #2c3e50;
            font-weight: 600;
        }
        
        .field-value {
            color: #27ae60;
            margin-left: 10px;
        }
        
        .error-list {
            background: #fff3cd;
            border: 1px solid #ffeaa7;
            border-radius: 6px;
            padding: 15px;
            margin-top: 15px;
        }
        
        .error-list h4 {
            color: #856404;
            margin-top: 0;
        }
        
        .error-list ul {
            margin: 10px 0;
            padding-left: 20px;
        }
        
        .error-list li {
            color: #856404;
            margin-bottom: 5px;
        }
        
        .hidden {
            display: none;
        }
        
        .loading {
            text-align: center;
            padding: 20px;
            color: #7f8c8d;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>ESTU Format Parser & Validator</h1>
        
        <div class="input-section">
            <label for="textInput">Paste your ESTU format text here:</label>
            <textarea id="textInput" placeholder="Paste your H and D records here..."></textarea>
        </div>
        
        <div class="button-section">
            <button id="parseButton" onclick="parseText()">Parse & Validate</button>
        </div>
        
        <div id="results" class="results hidden">
            <div id="validationStatus" class="validation-status"></div>
            <div id="parsedContent"></div>
        </div>
    </div>

    <script>
     // Field specifications for Header record (existing)
const HEADER_FIELDS = [
    {num: 1, desc: "Record ID", start: 1, end: 1, required: true, format: "x(01)"},
    {num: 2, desc: "Transaction Date", start: 2, end: 9, required: true, format: "9(08)"},
    {num: 3, desc: "Transaction Time", start: 10, end: 15, required: false, format: "9(06)"},
    {num: 4, desc: "Store Number", start: 16, end: 16, required: false, format: "x(01)"},
    {num: 5, desc: "Customer Number", start: 17, end: 22, required: true, format: "9(06)"},
    {num: 6, desc: "Job Number", start: 23, end: 25, required: false, format: "9(03)"},
    {num: 7, desc: "Tax Code", start: 26, end: 28, required: false, format: "x(03)"},
    {num: 8, desc: "Sales Tax Rate", start: 29, end: 33, required: false, format: "v9(5)"},
    {num: 9, desc: "Pricing Indicator", start: 34, end: 34, required: false, format: "x(01)"},
    {num: 10, desc: "Pricing Percent", start: 35, end: 38, required: false, format: "v9(4)"},
    {num: 11, desc: "Clerk", start: 39, end: 48, required: false, format: "x(10)"},
    {num: 12, desc: "Purchase Order Number", start: 49, end: 60, required: false, format: "x(12)"},
    {num: 13, desc: "Transaction Total", start: 61, end: 70, required: true, format: "9(7)v9(2)±"},
    {num: 14, desc: "Sale Taxable", start: 71, end: 71, required: false, format: "x(01)"},
    {num: 15, desc: "Salesperson Number", start: 72, end: 73, required: false, format: "x(02)"},
    {num: 16, desc: "Total Sales Tax", start: 74, end: 83, required: true, format: "9(7)v9(2)±"},
    {num: 17, desc: "* unused *", start: 84, end: 93, required: false, format: "x(10)"},
    {num: 18, desc: "Instructions 1", start: 94, end: 123, required: false, format: "x(30)"},
    {num: 19, desc: "Instructions 2", start: 124, end: 153, required: false, format: "x(30)"},
    {num: 20, desc: "Ship-To Name", start: 154, end: 183, required: false, format: "x(30)"},
    {num: 21, desc: "Ship-To Address 1", start: 184, end: 213, required: false, format: "x(30)"},
    {num: 22, desc: "Ship-To Address 2", start: 214, end: 243, required: false, format: "x(30)"},
    {num: 23, desc: "Ship-To Address 3", start: 244, end: 273, required: false, format: "x(30)"},
    {num: 24, desc: "Reference Information", start: 274, end: 303, required: false, format: "x(30)"},
    {num: 25, desc: "Customer Telephone", start: 304, end: 313, required: false, format: "x(10)"},
    {num: 26, desc: "Customer Resale No.", start: 314, end: 332, required: false, format: "x(19)"},
    {num: 27, desc: "Customer ID", start: 333, end: 342, required: false, format: "x(10)"},
    {num: 28, desc: "Special Order Vendor", start: 343, end: 347, required: false, format: "x(05)"},
    {num: 29, desc: "Total Deposit", start: 348, end: 357, required: false, format: "9(7)v9(2)±"},
    {num: 30, desc: "Expected / Delivery Date", start: 358, end: 365, required: false, format: "9(08)"},
    {num: 31, desc: "Estimate Expiration Date", start: 366, end: 373, required: false, format: "9(08)"},
    {num: 32, desc: "Terminal Number", start: 374, end: 376, required: false, format: "9(03)"},
    {num: 33, desc: "Transaction Number", start: 377, end: 384, required: false, format: "x(08)"},
    {num: 34, desc: "Transaction Type", start: 385, end: 385, required: false, format: "x(01)"},
    {num: 35, desc: "Total Cash Tendered", start: 386, end: 395, required: false, format: "9(7)v9(2)±"},
    {num: 36, desc: "Charge Tendered", start: 396, end: 405, required: false, format: "9(7)v9(2)±"},
    {num: 37, desc: "Change Given", start: 406, end: 415, required: false, format: "9(7)v9(2)±"},
    {num: 38, desc: "Total Check Tendered", start: 416, end: 425, required: false, format: "9(7)v9(2)±"},
    {num: 39, desc: "Check Number", start: 426, end: 431, required: false, format: "9(06)"},
    {num: 40, desc: "Bankcard Tendered", start: 432, end: 441, required: false, format: "9(7)v9(2)±"},
    {num: 41, desc: "Bankcard Number", start: 442, end: 457, required: false, format: "x(16)"},
    {num: 42, desc: "Apply-To Number", start: 458, end: 463, required: false, format: "x(06)"},
    {num: 43, desc: "Third Party Vendor Code", start: 464, end: 465, required: true, format: "x(02)"},
    {num: 44, desc: "Use ESTU cost indicator", start: 466, end: 466, required: false, format: "x(01)"},
    {num: 45, desc: "Private Label Card Type", start: 467, end: 467, required: false, format: "x(01)"},
    {num: 46, desc: "Special Transaction Processing Flag", start: 468, end: 468, required: false, format: "x(01)"},
    {num: 47, desc: "Private Label Card Promo Type", start: 469, end: 471, required: false, format: "x(03)"},
    {num: 48, desc: "TDX Transaction", start: 472, end: 472, required: false, format: "x(01)"},
    {num: 49, desc: "* unused *", start: 473, end: 474, required: false, format: "x(02)"},
    {num: 50, desc: "Direct Ship", start: 475, end: 475, required: false, format: "x(01)"},
    {num: 51, desc: "Transaction Codes", start: 476, end: 479, required: false, format: "x(04)"},
    {num: 52, desc: "Ship Via Code", start: 480, end: 480, required: false, format: "x(01)"},
    {num: 53, desc: "Filler", start: 481, end: 481, required: true, format: "x(01)"}
];

// Field specifications for Detail record (D)
const DETAIL_FIELDS = [
    {num: 1, desc: "Record ID", start: 1, end: 1, required: true, format: "x(01)"},
    {num: 2, desc: "Sku", start: 2, end: 15, required: false, format: "x(14)"},
    {num: 3, desc: "Item Transaction Type", start: 16, end: 16, required: false, format: "x(01)"},
    {num: 4, desc: "Description", start: 17, end: 48, required: false, format: "x(32)"},
    {num: 5, desc: "Taxable", start: 49, end: 49, required: false, format: "x(01)"},
    {num: 6, desc: "Pricing Flag (Promo/Discount)", start: 50, end: 50, required: false, format: "x(01)"},
    {num: 7, desc: "Manual Price", start: 51, end: 51, required: false, format: "x(01)"},
    {num: 8, desc: "Estimate Use Code", start: 52, end: 52, required: false, format: "x(01)"},
    {num: 9, desc: "Trade Discount", start: 53, end: 53, required: false, format: "x(01)"},
    {num: 10, desc: "Discount Percent", start: 54, end: 58, required: false, format: "v9(5)"},
    {num: 11, desc: "Special Order Vendor", start: 59, end: 63, required: false, format: "x(05)"},
    {num: 12, desc: "Unit Of Measure", start: 64, end: 65, required: false, format: "x(02)"},
    {num: 13, desc: "Quantity", start: 66, end: 73, required: true, format: "9(5)v9(3)"},
    {num: 14, desc: "Unit Price", start: 74, end: 81, required: true, format: "9(5)v9(3)"},
    {num: 15, desc: "Extended Price", start: 82, end: 89, required: true, format: "9(6)v9(2)"},
    {num: 16, desc: "Unit Cost", start: 90, end: 97, required: false, format: "9(5)v9(3)"},
    {num: 17, desc: "BOM SKU", start: 98, end: 98, required: false, format: "x(01)"},
    {num: 18, desc: "Reference Number", start: 99, end: 101, required: false, format: "9(03)"},
    {num: 19, desc: "Extended Taxable", start: 102, end: 111, required: false, format: "9(7)v9(3)"},
    {num: 20, desc: "Extended Non-Taxable", start: 112, end: 121, required: false, format: "9(7)v9(3)"},
    {num: 21, desc: "Backorder Quantity", start: 122, end: 129, required: false, format: "9(5)v9(3)"},
    {num: 22, desc: "* unused *", start: 130, end: 139, required: false, format: "x(10)"},
    {num: 23, desc: "Terms Discount", start: 140, end: 140, required: false, format: "x(01)"},
    {num: 24, desc: "Direct Ship", start: 141, end: 141, required: false, format: "x(01)"},
    {num: 25, desc: "* unused *", start: 142, end: 171, required: false, format: "x(30)"},
    {num: 26, desc: "Filler", start: 172, end: 481, required: true, format: "x(310)"}
];

// Field specifications for Information record (I)
const INFO_FIELDS = [
    {num: 1, desc: "Record ID", start: 1, end: 1, required: true, format: "x(01)"},
    {num: 2, desc: "Alt 1 Tendered", start: 2, end: 11, required: false, format: "9(7)v9(2)±"},
    {num: 3, desc: "Alt 1 Reference", start: 12, end: 31, required: false, format: "x(20)"},
    {num: 4, desc: "Alt 2 Tendered", start: 32, end: 41, required: false, format: "9(7)v9(2)±"},
    {num: 5, desc: "Alt 2 Reference", start: 42, end: 61, required: false, format: "x(20)"},
    {num: 6, desc: "Alt 3 Tendered", start: 62, end: 71, required: false, format: "9(7)v9(2)±"},
    {num: 7, desc: "Alt 3 Reference", start: 72, end: 91, required: false, format: "x(20)"},
    {num: 8, desc: "Alt 4 Tendered", start: 92, end: 101, required: false, format: "9(7)v9(2)±"},
    {num: 9, desc: "Alt 4 Reference", start: 102, end: 121, required: false, format: "x(20)"},
    {num: 10, desc: "Alt 5 Tendered", start: 122, end: 131, required: false, format: "9(7)v9(2)±"},
    {num: 11, desc: "Alt 5 Reference", start: 132, end: 151, required: false, format: "x(20)"},
    {num: 12, desc: "Alt 6 Tendered", start: 152, end: 161, required: false, format: "9(7)v9(2)±"},
    {num: 13, desc: "Alt 6 Reference", start: 162, end: 181, required: false, format: "x(20)"},
    {num: 14, desc: "Alt 7 Tendered", start: 182, end: 191, required: false, format: "9(7)v9(2)±"},
    {num: 15, desc: "Alt 7 Reference", start: 192, end: 211, required: false, format: "x(20)"},
    {num: 16, desc: "Alt 8 Tendered", start: 212, end: 221, required: false, format: "9(7)v9(2)±"},
    {num: 17, desc: "Alt 8 Reference", start: 222, end: 241, required: false, format: "x(20)"},
    {num: 18, desc: "Alt 9 Tendered", start: 242, end: 251, required: false, format: "9(7)v9(2)±"},
    {num: 19, desc: "Alt 9 Reference", start: 252, end: 271, required: false, format: "x(20)"},
    {num: 20, desc: "Alt 10 Tendered", start: 272, end: 281, required: false, format: "9(7)v9(2)±"},
    {num: 21, desc: "Alt 10 Reference", start: 282, end: 301, required: false, format: "x(20)"},
    {num: 22, desc: "Filler", start: 302, end: 481, required: true, format: "x(180)"}
];

function parseText() {
    const input = document.getElementById('textInput').value.trim();
    const results = document.getElementById('results');
    const validationStatus = document.getElementById('validationStatus');
    const parsedContent = document.getElementById('parsedContent');
    const parseButton = document.getElementById('parseButton');
    
    if (!input) {
        alert('Please paste some text to parse.');
        return;
    }
    
    // Show loading state
    parseButton.disabled = true;
    parseButton.textContent = 'Parsing...';
    results.classList.remove('hidden');
    parsedContent.innerHTML = '<div class="loading">Processing...</div>';
    
    setTimeout(() => {
        try {
            const parseResult = parseESTUFormat(input);
            displayResults(parseResult, validationStatus, parsedContent);
        } catch (error) {
            validationStatus.className = 'validation-status invalid';
            validationStatus.innerHTML = `<strong>❌ Parsing Error:</strong> ${error.message}`;
            parsedContent.innerHTML = '';
        }
        
        parseButton.disabled = false;
        parseButton.textContent = 'Parse & Validate';
    }, 100);
}

function parseESTUFormat(input) {
    const lines = input.split('\n').filter(line => line.trim());
    const records = [];
    const errors = [];
    let isValid = true;
    
    if (lines.length === 0) {
        // Try to parse as single long line
        lines.push(input);
    }
    
    lines.forEach((line, index) => {
        if (!line.trim()) return;
        
        const recordType = line.charAt(0);
        
        if (recordType === 'H') {
            const headerResult = parseHeaderRecord(line, index + 1);
            records.push(headerResult);
            if (!headerResult.isValid) {
                isValid = false;
                errors.push(...headerResult.errors);
            }
        } else if (recordType === 'D') {
            const detailResult = parseDetailRecord(line, index + 1);
            records.push(detailResult);
            if (!detailResult.isValid) {
                isValid = false;
                errors.push(...detailResult.errors);
            }
        } else if (recordType === 'I') {
            const infoResult = parseInfoRecord(line, index + 1);
            records.push(infoResult);
            if (!infoResult.isValid) {
                isValid = false;
                errors.push(...infoResult.errors);
            }
        } else {
            isValid = false;
            errors.push(`Line ${index + 1}: Unknown record type '${recordType}'. Expected 'H', 'D', or 'I'.`);
            records.push({
                type: 'Unknown',
                lineNumber: index + 1,
                isValid: false,
                fields: [],
                errors: [`Unknown record type '${recordType}'`]
            });
        }
    });
    
    return {
        isValid,
        errors,
        records
    };
}

function parseHeaderRecord(line, lineNumber) {
    const fields = [];
    const errors = [];
    let isValid = true;
    
    // Check minimum length
    if (line.length < 481) {
        errors.push(`Line ${lineNumber}: Header record too short (${line.length} chars), expected 481`);
        isValid = false;
        // Pad with spaces for parsing
        line = line.padEnd(481, ' ');
    }
    
    // Validate record type
    if (line.charAt(0) !== 'H') {
        errors.push(`Line ${lineNumber}: Invalid record type '${line.charAt(0)}', expected 'H'`);
        isValid = false;
    }
    
    HEADER_FIELDS.forEach(field => {
        const startIdx = field.start - 1;
        const endIdx = field.end;
        const value = line.substring(startIdx, endIdx);
        
        fields.push({
            number: field.num,
            description: field.desc,
            value: value,
            required: field.required,
            format: field.format
        });
        
        // Validate required fields - for fixed-width formats, spaces are valid values
        // Only fail if the field is completely missing (undefined/null) or empty string
        if (field.required && (value === undefined || value === null || value === '')) {
            errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' is missing`);
            isValid = false;
        }
        
        // Special validation for specific fields that cannot be just spaces
        if (field.num === 2 && field.required) { // Transaction Date - must have actual content
            if (!value.trim()) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' cannot be empty`);
                isValid = false;
            } else if (!/^\d{8}$/.test(value.trim())) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid date format '${value.trim()}', expected MMDDYYYY`);
                isValid = false;
            }
        } else if (field.num === 5 && field.required) { // Customer Number - must have actual content
            if (!value.trim()) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' cannot be empty`);
                isValid = false;
            }
        } else if (field.num === 13 && field.required) { // Transaction Total - must have actual content
            if (!value.trim()) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' cannot be empty`);
                isValid = false;
            }
        } else if (field.num === 16 && field.required) { // Total Sales Tax - must have actual content
            if (!value.trim()) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' cannot be empty`);
                isValid = false;
            }
        }
        
        // Format validation for non-required fields or fields that have content
        if (field.num === 2 && value.trim()) { // Transaction Date
            if (!/^\d{8}$/.test(value.trim())) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid date format '${value.trim()}', expected MMDDYYYY`);
                isValid = false;
            }
        }
        
        if (field.num === 3 && value.trim()) { // Transaction Time
            if (!/^\d{6}$/.test(value.trim())) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid time format '${value.trim()}', expected HHMMSS`);
                isValid = false;
            }
        }
        
        if (field.num === 34 && value.trim()) { // Transaction Type
            const validTypes = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'E', 'O', 'S'];
            if (!validTypes.includes(value.trim())) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid transaction type '${value.trim()}'`);
                isValid = false;
            }
        }
    });
    
    return {
        type: 'Header',
        lineNumber,
        isValid,
        fields,
        errors
    };
}

function parseDetailRecord(line, lineNumber) {
    const fields = [];
    const errors = [];
    let isValid = true;
    
    // Check minimum length
    if (line.length < 481) {
        errors.push(`Line ${lineNumber}: Detail record too short (${line.length} chars), expected 481`);
        isValid = false;
        // Pad with spaces for parsing
        line = line.padEnd(481, ' ');
    }
    
    // Validate record type
    if (line.charAt(0) !== 'D') {
        errors.push(`Line ${lineNumber}: Invalid record type '${line.charAt(0)}', expected 'D'`);
        isValid = false;
    }
    
    DETAIL_FIELDS.forEach(field => {
        const startIdx = field.start - 1;
        const endIdx = field.end;
        const value = line.substring(startIdx, endIdx);
        
        fields.push({
            number: field.num,
            description: field.desc,
            value: value,
            required: field.required,
            format: field.format
        });
        
        // Validate required fields - for fixed-width formats, spaces are valid values
        // Only fail if the field is completely missing (undefined/null) or empty string
        if (field.required && (value === undefined || value === null || value === '')) {
            errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' is missing`);
            isValid = false;
        }
        
        // Special validation for fields that must have actual content (not just spaces)
        if (field.num === 13 && field.required) { // Quantity - must have actual content
            if (!value.trim()) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' cannot be empty`);
                isValid = false;
            }
        } else if (field.num === 14 && field.required) { // Unit Price - must have actual content
            if (!value.trim()) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' cannot be empty`);
                isValid = false;
            }
        } else if (field.num === 15 && field.required) { // Extended Price - must have actual content
            if (!value.trim()) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' cannot be empty`);
                isValid = false;
            }
        }
        
        // Specific validation for detail fields
        if (field.num === 3 && value.trim()) { // Item Transaction Type
            const validTypes = [' ', 'R', 'X', 'D'];
            if (!validTypes.includes(value)) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid item transaction type '${value}', expected space, R, X, or D`);
                isValid = false;
            }
        }
        
        if (field.num === 5 && value.trim()) { // Taxable
            if (!['Y', 'N'].includes(value.trim())) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid taxable value '${value.trim()}', expected Y or N`);
                isValid = false;
            }
        }
        
        if (field.num === 6 && value.trim()) { // Pricing Flag
            const validFlags = ['D', 'P', 'Q', 'S', '*', '1', '2', '3', '4', '5'];
            if (!validFlags.includes(value.trim())) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid pricing flag '${value.trim()}'`);
                isValid = false;
            }
        }
        
        if (field.num === 7 && value.trim()) { // Manual Price
            if (value.trim() !== 'M' && value.trim() !== '') {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid manual price flag '${value.trim()}', expected M or blank`);
                isValid = false;
            }
        }
        
        if (field.num === 9 && value.trim()) { // Trade Discount
            if (!['Y', 'N'].includes(value.trim())) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid trade discount value '${value.trim()}', expected Y or N`);
                isValid = false;
            }
        }
        
        if (field.num === 23 && value.trim()) { // Terms Discount
            if (!['Y', 'N'].includes(value.trim())) {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid terms discount value '${value.trim()}', expected Y or N`);
                isValid = false;
            }
        }
        
        if (field.num === 24 && value.trim()) { // Direct Ship
            if (value.trim() !== 'Y' && value.trim() !== '') {
                errors.push(`Line ${lineNumber}, Field ${field.num}: Invalid direct ship flag '${value.trim()}', expected Y or blank`);
                isValid = false;
            }
        }
    });
    
    return {
        type: 'Detail',
        lineNumber,
        isValid,
        fields,
        errors
    };
}

function parseInfoRecord(line, lineNumber) {
    const fields = [];
    const errors = [];
    let isValid = true;
    
    // Check minimum length
    if (line.length < 481) {
        errors.push(`Line ${lineNumber}: Info record too short (${line.length} chars), expected 481`);
        isValid = false;
        // Pad with spaces for parsing
        line = line.padEnd(481, ' ');
    }
    
    // Validate record type
    if (line.charAt(0) !== 'I') {
        errors.push(`Line ${lineNumber}: Invalid record type '${line.charAt(0)}', expected 'I'`);
        isValid = false;
    }
    
    INFO_FIELDS.forEach(field => {
        const startIdx = field.start - 1;
        const endIdx = field.end;
        const value = line.substring(startIdx, endIdx);
        
        fields.push({
            number: field.num,
            description: field.desc,
            value: value,
            required: field.required,
            format: field.format
        });
        
        // Validate required fields - for fixed-width formats, spaces are valid values
        // Only fail if the field is completely missing (undefined/null) or empty string
        if (field.required && (value === undefined || value === null || value === '')) {
            errors.push(`Line ${lineNumber}, Field ${field.num}: Required field '${field.desc}' is missing`);
            isValid = false;
        }
    });
    
    return {
        type: 'Information',
        lineNumber,
        isValid,
        fields,
        errors
    };
}

function displayResults(parseResult, validationStatus, parsedContent) {
    // Display validation status
    if (parseResult.isValid) {
        validationStatus.className = 'validation-status valid';
        validationStatus.innerHTML = '<strong>✅ Valid Format</strong> - All records parsed successfully!';
    } else {
        validationStatus.className = 'validation-status invalid';
        validationStatus.innerHTML = '<strong>❌ Invalid Format</strong> - Found validation errors.';
    }
    
    // Display errors if any
    let errorHtml = '';
    if (parseResult.errors.length > 0) {
        errorHtml = `
            <div class="error-list">
                <h4>Validation Errors:</h4>
                <ul>
                    ${parseResult.errors.map(error => `<li>${error}</li>`).join('')}
                </ul>
            </div>
        `;
    }
    
    // Display parsed records
    let recordsHtml = '';
    parseResult.records.forEach((record, index) => {
        const statusIcon = record.isValid ? '✅' : '❌';
        const statusClass = record.isValid ? 'valid' : 'invalid';
        
        recordsHtml += `
            <div class="record">
                <div class="record-header">
                    ${statusIcon} ${record.type} Record - Line ${record.lineNumber}
                </div>
                <div class="record-content">
                    ${record.fields.map(field => `
                        <div class="field">
                            <span class="field-number">${field.number.toString().padStart(2, '0')}:</span>
                            <span class="field-description">${field.description}</span>
                            <span class="field-value">"${field.value}"</span>
                            ${field.required ? ' <em>(Required)</em>' : ''}
                        </div>
                    `).join('')}
                </div>
            </div>
        `;
    });
    
    parsedContent.innerHTML = errorHtml + recordsHtml;
}

// Allow Enter key to trigger parse
document.getElementById('textInput').addEventListener('keydown', function(e) {
    if (e.ctrlKey && e.key === 'Enter') {
        parseText();
    }
});

// Auto-resize textarea
document.getElementById('textInput').addEventListener('input', function() {
    this.style.height = 'auto';
    this.style.height = Math.max(150, this.scrollHeight) + 'px';
});

    </script>
</body>
</html>
