#!/usr/bin/env python3
"""
mwrap — MEX file generator for MATLAB and Octave.

Copyright (c) 2007-2008  David Bindel
See the file COPYING for copying permissions

Converted to Python by Zydrunas Gimbutas (2026),
with assistance from Claude Code / Claude Opus 4.6 (Anthropic).
"""

import sys
import os
import argparse

# Ensure the directory containing this script is on the path
_script_dir = os.path.dirname(os.path.abspath(__file__))
if _script_dir not in sys.path:
    sys.path.insert(0, _script_dir)

from mwrap_ast import MwrapContext
from mwrap_lexer import Lexer
from mwrap_parser import Parser
from mwrap_cgen import print_mex_init, print_mex_file


HELP_STRING = """\
mwrap 1.3 - MEX file generator for MATLAB and Octave

Syntax:
  mwrap [-mex outputmex] [-m output.m] [-c outputmex.c] [-mb] [-list]
        [-catch] [-i8] [-c99complex] [-cppcomplex] [-gpu] infile1 infile2 ...

  -mex outputmex -- specify the MATLAB mex function name
  -m output.m    -- generate the MATLAB stub called output.m
  -c outputmex.c -- generate the C file outputmex.c
  -mb            -- generate .m files specified with @ redirections
  -list          -- list files specified with @ redirections
  -catch         -- generate C++ exception handling code
  -i8            -- convert int, long, uint, ulong to int64_t, uint64_t
  -c99complex    -- add support code for C99 complex types
  -cppcomplex    -- add support code for C++ complex types
  -gpu           -- add support code for MATLAB gpuArray
"""

USAGE_STRING = """\
Usage: mwrap [-mex outputmex] [-m output.m] [-c outputmex.c] infile1 ...
Try 'mwrap --help' for more information.
"""


def _load_support():
    """Load the runtime support C file content."""
    support_path = os.path.join(_script_dir, "mwrap_support.c")
    with open(support_path, "r") as f:
        return f.read()


def _build_parser():
    """Build the argparse argument parser."""
    p = argparse.ArgumentParser(add_help=False)
    p.add_argument('--help', action='store_true', dest='help')
    p.add_argument('-m', dest='mfile')
    p.add_argument('-c', dest='cfile')
    p.add_argument('-mex', dest='mexfunc', default='mexfunction')
    p.add_argument('-mb', action='store_true', dest='mbatching')
    p.add_argument('-list', action='store_true', dest='listing')
    p.add_argument('-catch', action='store_true', dest='catch_')
    p.add_argument('-i8', action='store_true')
    p.add_argument('-c99complex', action='store_true')
    p.add_argument('-cppcomplex', action='store_true')
    p.add_argument('-gpu', action='store_true')
    p.add_argument('input_files', nargs='*')
    return p


def main():
    ctx = MwrapContext()
    ctx.init_scalar_types()

    if len(sys.argv) < 2:
        sys.stderr.write(HELP_STRING)
        return 0

    p = _build_parser()
    args = p.parse_args()

    if args.help:
        sys.stderr.write(HELP_STRING)
        return 0

    if args.catch_:
        ctx.mw_generate_catch = True
    if args.i8:
        ctx.mw_promote_int = 4
    if args.c99complex:
        ctx.mw_use_c99_complex = True
    if args.cppcomplex:
        ctx.mw_use_cpp_complex = True
    if args.gpu:
        ctx.mw_use_gpu = True

    if ctx.mw_use_c99_complex or ctx.mw_use_cpp_complex:
        ctx.add_zscalar_type("dcomplex")
        ctx.add_cscalar_type("fcomplex")

    if not args.input_files:
        sys.stderr.write(USAGE_STRING)
        return 0

    # --- Open output files ---
    outfp = None
    outcfp = None
    if args.mfile:
        outfp = open(args.mfile, "w")
    if args.cfile:
        outcfp = open(args.cfile, "w")

    # --- Create lexer and parser ---
    lexer = Lexer(outfp=outfp, outcfp=outcfp,
                  mbatching_flag=args.mbatching,
                  listing_flag=args.listing)
    parser = Parser(lexer, ctx, mexfunc=args.mexfunc)

    err_flag = 0
    emitted_mex_init = False

    for infile in args.input_files:
        lexer.linenum = 1
        parser.type_errs = 0

        try:
            fp_test = open(infile, "r")
            fp_test.close()
        except OSError:
            sys.stderr.write(f"Could not read {infile}\n")
            continue

        lexer.current_ifname = infile

        if outcfp and not emitted_mex_init:
            support_text = _load_support()
            print_mex_init(outcfp, ctx, support_text)
            emitted_mex_init = True

        for tok in lexer.lex_file(infile):
            parser.feed(tok)

        parser.finish_file()
        err_flag += parser.err_flag
        parser.err_flag = 0

    # --- Generate C output ---
    if not err_flag and outcfp:
        print_mex_file(outcfp, ctx, parser.funcs)

    if outfp:
        outfp.close()
    if outcfp:
        outcfp.close()

    return err_flag


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