# -*- coding: utf-8 -*-
# $Id: add_file_to_id_only_references.py $

"""
Makes id-only reference in the given file into dita-compliant file#id
references, using the mapping database generated by build_id_to_file_mapping.py.
"""

__copyright__ = \
"""
Copyright (C) 2023-2025 Oracle and/or its affiliates.

This file is part of VirtualBox base platform packages, as
available from https://www.virtualbox.org.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, in version 3 of the
License.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, see <https://www.gnu.org/licenses>.

SPDX-License-Identifier: GPL-3.0-only
"""
__version__ = "$Revision: 170187 $"

# Standard python imports.
import glob;
import os;
import re;
import sys;


g_oReHref = re.compile(r'\bhref=("[^">#./]+"|\'[^\'>#./]+\')');

def modifyDitaFile(dIdToFile, sContent):
    """
    Modifies the href attributes in this file.
    """
    current_file_is_refentry = False
    if any(current_file_name.lower() in s.lower() for s in refentry_files):
        current_file_is_refentry = True
    sModified = '';
    offPrev   = 0;
    for oMatch in g_oReHref.finditer(sContent):
        sId = oMatch.group(1)[1:-1];
        if sId in dIdToFile:
            reference_is_refentry = False
            if any(os.path.splitext(dIdToFile[sId])[0].lower() in s.lower() for s in refentry_files):
                reference_is_refentry = True
            if current_file_is_refentry and not reference_is_refentry:
                 sModified += sContent[offPrev : oMatch.start(1)] + '"../topics/' + dIdToFile[sId] + '#' + sId + '"';
            else:
                sModified += sContent[offPrev : oMatch.start(1)] + '"' + dIdToFile[sId] + '#' + sId + '"';
            offPrev = oMatch.end(1);
    if offPrev < len(sContent):
        sModified += sContent[offPrev:];
    return sModified;

def info(sMessage):
    """ Info message. """
    print('add_file_to_id_only_references.py: info: %s' % sMessage);
    return 1;

def error(sMessage):
    """ Reports an error. """
    print('add_file_to_id_only_references.py: error: %s' % sMessage, file = sys.stderr);
    return 1;

def syntax(sMessage):
    """ Reports a syntax error. """
    print('add_file_to_id_only_references.py: syntax error: %s' % sMessage, file = sys.stderr);
    return 2;

def usage():
    """ Reports usage. """
    print('usage: add_file_to_id_only_references.py [--verbose|--quiet] --mapping-file <map.db> file1.dita [file2.dita [...]]');
    return 0;

def main(asArgs):
    """
    C-like main function.
    """
    #
    # Process arguments.
    #
    dIdToFile  = None;
    fEndOfArgs = False;
    fVerbose   = False;
    iArg       = 1;
    while iArg < len(asArgs):
        sArg = asArgs[iArg];
        if sArg[0] == '-' and not fEndOfArgs:
            # Options.
            if sArg == '--':
                fEndOfArgs = True;
            elif sArg in ('--help', '-h', '-?'):
                return usage();
            elif sArg in ('--version', '-V' ):
                print(__version__[__version__.find(':') + 2:-2]);
            elif sArg in ('--quiet', '-q' ):
                fVerbose = False;
            elif sArg in ('--verbose', '-v' ):
                fVerbose = True;
            elif sArg in ('--refentry_file_list', '-f' ):
                iArg += 1;
                if iArg >= len(asArgs):
                    return syntax('Expected a list of refentry files following "--refentry_file_list"!');
                global refentry_files
                refentry_files = asArgs[iArg].split(' ')
            elif sArg in ('--mapping-file', '-m'):
                iArg += 1;
                if iArg >= len(asArgs):
                    return syntax('Expected filename following "--mapping-file"!');
                # Load the database file.
                sArg = asArgs[iArg];
                try:
                    with open(sArg, 'r', encoding = 'utf-8') as oFile:
                        dIdToFile = {};
                        for sLine in oFile:
                            sId, sFile = sLine.split('=');
                            dIdToFile[sId.strip()] = sFile.strip();
                except Exception as oXcpt: # pylint: disable=broad-exception-caught
                    return error('Failed to open and parse "%s": %s' % (sArg, oXcpt,));
                if fVerbose:
                    info('Loaded %s IDs from "%s"' % (len(dIdToFile), sArg));
            else:
                return syntax('Unknown option: %s' % (sArg,));
        else:
            # File to modify.
            if dIdToFile is None:
                return syntax('A mapping database must be given before any other files!');

            try:
                with open(sArg, 'r', encoding = 'utf-8') as oFile:
                    sContent = oFile.read();
            except Exception as oXcpt: # pylint: disable=broad-exception-caught
                return error('Failed to open and read "%s": %s' % (sArg, oXcpt,));
            global current_file_name
            current_file_name = os.path.splitext(os.path.basename(sArg))[0]
            prefix = "flat-"
            if current_file_name.startswith(prefix):
                current_file_name = current_file_name[len(prefix):]
            sModified = modifyDitaFile(dIdToFile, sContent);
            if sModified != sContent:
                if fVerbose:
                    info('Writing out modified "%s"...' % (sArg,));
                try:
                    with open(sArg, 'w', encoding = 'utf-8') as oFile:
                        oFile.write(sModified);
                except Exception as oXcpt: # pylint: disable=broad-exception-caught
                    return error('Failed to open and write back "%s": %s' % (sArg, oXcpt,));
            elif fVerbose:
                info('No changes to "%s"...' % (sArg,));

        iArg += 1;
    return 0;

if __name__ == "__main__":
    sys.exit(main(sys.argv));

