import {Register, RegisterAccess, RegisterDetail, Note, detailHasContent, isIssueRestricted} from "@/utils/register_parser";

// Default parser for the common register format: optional (R) / (W) / (R/W)
// access blocks, each holding "bit(s) N = description" rows, with footnotes
// (*, **, ...) that may span multiple indented lines, plus free prose. Produces
// a single RegisterDetail (mode) per register.
export const parseDescriptionDefault = (reg: Register, description: string) => {
    const descriptionLines = description.split('\n');
    let currentAccess: 'read' | 'write' | 'common' | null = null;
    let accessData: RegisterAccess = { operations: [], notes: [] };
    // Prepare a new RegisterDetail for this description block
    const detail: RegisterDetail = { read: undefined, write: undefined, common: undefined, text: ''};

    // Footnote multiline state
    let inFootnote = false;
    let footnoteBaseIndent = 0;
    let currentFootnote: Note | null = null;

    const endFootnoteIfActive = () => {
        inFootnote = false;
        footnoteBaseIndent = 0;
        currentFootnote = null;
    };

    for (let idx = 0; idx < descriptionLines.length; idx++) {
        const line = descriptionLines[idx];
        reg.source.push(line);

        const trimmedLine = line.trim();
        if (trimmedLine.startsWith('//')) continue;

        reg.search += line.toLowerCase() + " ";
        const spaces_at_start = line.match(/^(\s*)/)?.[0].length || 0;

        if (isIssueRestricted(line)) reg.issue_4_only = true;

        // Handle multiline footnote continuation
        if (inFootnote) {
            if (spaces_at_start > footnoteBaseIndent && trimmedLine.length > 0) {
                // continuation line; append preserving original line (trim left to the base indent)
                const continuation = line.substring(footnoteBaseIndent + 1); // +1 to skip at least one extra indent
                if (currentFootnote) {
                    currentFootnote.text += `\n${continuation.trimEnd()}`;
                }
                continue;
            } else {
                // indentation returned -> end footnote, and fall through to process this line normally
                endFootnoteIfActive();
            }
        }

        // Access state markers
        if (trimmedLine.startsWith('(R)')) {
            if (currentAccess) {
                // finalize previous access block into detail
                detail[currentAccess] = accessData;
            }
            accessData = { operations: [], notes: [] };
            currentAccess = 'read';
            continue;
        }
        if (trimmedLine.startsWith('(W)')) {
            if (currentAccess) {
                detail[currentAccess] = accessData;
            }
            accessData = { operations: [], notes: [] };
            currentAccess = 'write';
            continue;
        }
        if (trimmedLine.startsWith('(R/W')) {
            if (currentAccess) {
                detail[currentAccess] = accessData;
            }
            accessData = { operations: [], notes: [] };
            currentAccess = 'common';
            continue;
        }
        // A line with no leading whitespace (line === its own trimmed form)
        // starts a new top-level text block, so close any open access block.
        if (line.startsWith(trimmedLine)) {
            if (currentAccess) {
                detail[currentAccess] = accessData;
            }
            accessData = { operations: [], notes: [] };
            currentAccess = null;
        }

        // Start of a footnote (works both inside and outside an access block)
        if (trimmedLine.startsWith('*')) {
            const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
            if (noteMatch) {
                const note: Note = { ref: noteMatch[1], text: noteMatch[2] };
                if (currentAccess) {
                    accessData.notes.push(note);
                } else {
                    reg.notes.push(note);
                }
                currentFootnote = note;
                inFootnote = true;
                footnoteBaseIndent = spaces_at_start;
                continue;
            }
        }

        if (currentAccess) {
            const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\s*=\s*(.*)/);

            if (bitMatch) {
                let bitDescription = bitMatch[3];
                const footnoteMatch = bitDescription.match(/(\*+)$/);
                let footnoteRef: string | undefined = undefined;
                if (footnoteMatch) {
                    footnoteRef = footnoteMatch[1];
                    bitDescription = bitDescription.substring(0, bitDescription.length - footnoteRef.length).trim();
                }
                accessData.operations.push({
                    bits: bitMatch[2],
                    description: bitDescription,
                    footnoteRef: footnoteRef,
                });
            } else if (trimmedLine) {
                // Prose indented exactly two spaces inside an access block is
                // register-level commentary rather than part of the bit table.
                if(spaces_at_start == 2) {
                    reg.text += `${line}\n`;
                    continue;
                }
                if (line.match(/^\s+/) && accessData.operations.length > 0) {
                    accessData.operations[accessData.operations.length - 1].description += `\n${line}`;
                } else {
                    if (!accessData.description) {
                        accessData.description = '';
                    }
                    accessData.description += `\n${trimmedLine}`;
                }
            }
        } else {
            if (trimmedLine) {
                detail.text += `${line}\n`;
            }
        }
    }
    // close any dangling footnote
    if (inFootnote) {
        endFootnoteIfActive();
    }
    if (currentAccess) {
        detail[currentAccess] = accessData;
    }
    // Push the parsed detail into modes, unless it is empty — body-less
    // registers (e.g. the "Reserved" entries) would otherwise add a blank mode.
    reg.modes = reg.modes || [];
    if (detailHasContent(detail)) {
        reg.modes.push(detail);
    }
};