#!/usr/bin/env python3
import sys, os, subprocess, tempfile, shutil, re

#def lire_fichier_mac(mac_file):
#    if not os.path.exists(mac_file):
#        return None
#    with open(mac_file, 'r', encoding='utf-8') as f:
#        return [re.sub(r';+$', '', l.strip()) for l in f.read().splitlines() 
#                if l.strip() and not l.startswith(('*', '%'))]

     #modification fonction pour transformer dollar fin de ligne en point virgule           
#def lire_fichier_mac(mac_file):
#    if not os.path.exists(mac_file):
#        return None
#    with open(mac_file, 'r', encoding='utf-8') as f:
#        return [re.sub(r';+$', '', re.sub(r'\$$', ';', l.strip())) 
#                for l in f.read().splitlines() 
#                if l.strip() and not l.startswith(('*', '%'))]
 
# avec lecture des blocs de commande sur plusieurs lignes               


def lire_fichier_mac(mac_file):
    if not os.path.exists(mac_file):
        return None
    with open(mac_file, 'r', encoding='utf-8') as f:
        raw_lines = f.read().splitlines()

    cmds = []
    bloc = []

    for l in raw_lines:
        l_strip = l.strip()

        # Ignorer lignes vides et commentaires de début de ligne (* ou %)
        if not l_strip or l_strip.startswith(('*', '%')):
            continue

        # Supprimer les commentaires /* ... */ en ligne
        l_strip = re.sub(r'/\*.*?\*/', '', l_strip).strip()
        if not l_strip:
            continue

        # Éclater les commandes multiples sur une même ligne (séparées par ; ou $)
        sous_cmds = re.split(r'(?<=[;$])', l_strip)
        sous_cmds = [s.strip() for s in sous_cmds if s.strip()]

        for s in sous_cmds:
            bloc.append(s)
            # La commande est complète quand le fragment se termine par ; ou $
            if s.endswith(';') or s.endswith('$'):
                cmd = ' '.join(bloc)
                cmd = re.sub(r'\$$', ';', cmd)
                cmd = re.sub(r';+$', '', cmd)
                cmd = re.sub(r'\bwx(plot|draw)(2d|3d)\b', r'\1\2', cmd)
                cmds.append(cmd)
                bloc = []

    # Cas d'un bloc non terminé (sans ; ni $ final)
    if bloc:
        cmd = ' '.join(bloc)
        cmd = re.sub(r'\$$', ';', cmd)
        cmd = re.sub(r';+$', '', cmd)
        cmds.append(cmd)

    return cmds if cmds else None           

def couper_ligne_longue(texte, largeur_max=75):
    """Coupe intelligemment les lignes trop longues en LaTeX."""
    if len(texte) <= largeur_max:
        return texte
    
    # Caractères où on peut couper
    separateurs = ['+', '-', '*', '/', '=', ',', '(', ')', '[', ']', '{', '}', ' ']
    
    lignes = []
    reste = texte
    
    while len(reste) > largeur_max:
        # Chercher le meilleur point de coupure
        meilleur_pos = -1
        for i in range(largeur_max, largeur_max // 2, -1):
            if i < len(reste) and reste[i] in separateurs:
                meilleur_pos = i + 1
                break
        
        if meilleur_pos == -1:
            # Pas de bon séparateur trouvé, couper à largeur_max
            meilleur_pos = largeur_max
        
        lignes.append(reste[:meilleur_pos].rstrip())
        reste = reste[meilleur_pos:].lstrip()
    
    if reste:
        lignes.append(reste)
    
    return '\n'.join(lignes)

def generer_script_maxima(cmds, tex_path, tmpdir):
    esc_path = os.path.abspath(tex_path).replace('\\', '/')
    script = [
        'texput(matrix, ["pmatrix"]);',
        'display2d: false;',
        'load(draw);',
        f'with_stdout("{esc_path}",'
    ]
    for i, cmd in enumerate(cmds, 1):
        cmd_esc = cmd.replace('"', '\\"')
        img_name = f"plot_{i}.png"
        img_path = os.path.join(tmpdir, img_name).replace('\\', '/')
        img_base = img_path.replace(".png", "")
        script.append(f'  print("---BLOCK---"), print("ID: {i}"), print("CMD: {cmd_esc}"),')
        if any(x in cmd for x in ['draw2d', 'draw3d', 'plot2d', 'plot3d']):
            if 'draw2d' in cmd:
                gfx_cmd = cmd.replace('draw2d(', f'draw2d(terminal=png, file_name="{img_base}", ')
            elif 'draw3d' in cmd:
                gfx_cmd = cmd.replace('draw3d(', f'draw3d(terminal=png, file_name="{img_base}", ')
            elif 'plot2d' in cmd:
                gfx_cmd = re.sub(r'\)$', f', [png_file, "{img_path}"] )', cmd)
            elif 'plot3d' in cmd:
                gfx_cmd = re.sub(r'\)$', f', [png_file, "{img_path}"] )', cmd)
            script.append(f'  errcatch({gfx_cmd}), print("GFX: {img_name}"),')
        else:
            script.append(f'  val : errcatch({cmd}),')
            script.append('  if val = [] then print("RES: MATH_ERR") else (print("RES:"), tex(val[1], true)),')
    script[-1] = script[-1].rstrip(',')
    script.append('); quit();')
    return '\n'.join(script)

def finaliser_tex(tmp_tex, font_size, img_width):
    if not os.path.exists(tmp_tex): return False
    with open(tmp_tex, 'r', encoding='utf-8') as f:
        lines = f.readlines()
    header = r"""\documentclass[FONTSIZE]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{amsmath,amsfonts,amssymb,mathtools}
\usepackage{graphicx}
\usepackage[left=1.5cm, right=1.5cm, top=1.5cm, bottom=2cm]{geometry}
\usepackage[french]{babel}
\usepackage{fancyhdr}
\usepackage{lastpage}
\usepackage[useregional]{datetime2}
\usepackage{breqn}
\pagestyle{fancy}
\lhead{} \chead{} \rhead{}
\cfoot{ }
\rfoot{page \thepage}
\lfoot{G\'en\'er\'e le \DTMnow}
\renewcommand{\headrulewidth}{0.0pt}
\renewcommand{\footrulewidth}{0.5pt}
\title{Résultats Maxima (Yamwi)}
\author{Généré automatiquement}
\begin{document}
\noindent\fbox{\begin{minipage}{\textwidth}\centering\LARGE\scshape Maxima Online 2\end{minipage}}
"""
    header = header.replace("FONTSIZE", font_size)
    body = []
    in_res = False
    res_lines = []  # Pour accumuler les lignes de résultat
    
    for line in lines:
        l = line.strip()
        if "ID:" in l: 
            body.append(f"\\subsection*{{Calcul {l.split(':')[-1].strip()}}}")
        elif "CMD:" in l:
            c = l.split('CMD:')[-1].strip().replace('_', '\\_').replace('%', '\\%').replace('^', '\\string^')
            # Couper la commande si elle est trop longue
            c_coupe = couper_ligne_longue(c, largeur_max=90)
            if '\n' in c_coupe:
                # Multi-lignes : utiliser verbatim
                body.append(f"\\textbf{{Commande :}}")
                body.append(f"\\begin{{verbatim}}")
                body.append(c_coupe)
                body.append(f"\\end{{verbatim}}")
                body.append(f"\\medskip")
            else:
                # Une seule ligne : utiliser texttt
                body.append(f"\\textbf{{Commande :}} \\texttt{{{c_coupe}}} \\par\\medskip")
        elif "RES:" in l: 
            in_res = True
            res_lines = []
            continue
        elif "GFX:" in l:
            img = l.split('GFX:')[-1].strip()
            body.append(f"\\begin{{center}}\\includegraphics[width={img_width}\\textwidth]{{{img}}}\\end{{center}}")
        elif "---BLOCK---" in l:
            # Fin d'un bloc : traiter les résultats accumulés
            if in_res and res_lines:
                res_complet = ' '.join(res_lines)
                
                body.append(f"\\textbf{{Résultat :}}")
                body.append(f"\\par\\noindent")
                
                # Pour les très longs résultats, traiter spécialement
                if len(res_complet) > 120 and res_complet.startswith('$$') and res_complet.endswith('$$'):
                    # Extraire le contenu sans les $$
                    contenu = res_complet[2:-2].strip()
                    
                    # Pour les listes simples (comme [1,2,3,...]), découper manuellement
                    if '\\left[' in contenu and '\\right]' in contenu:
                        # Supprimer \left et \right
                        contenu = contenu.replace('\\left', '').replace('\\right', '')
                        
                        # Découper tous les 10 éléments (environ tous les 30-40 caractères)
                        parties = contenu.split(',')
                        lignes = []
                        ligne_temp = ""
                        
                        for i, partie in enumerate(parties):
                            if i == 0:
                                ligne_temp = partie + ','
                            elif (i % 10 == 0):
                                lignes.append(ligne_temp.strip())
                                ligne_temp = partie + ','
                            else:
                                ligne_temp += partie + ','
                        
                        # Ajouter la dernière ligne (enlever la dernière virgule)
                        if ligne_temp:
                            lignes.append(ligne_temp.rstrip(',').strip())
                        
                        # Afficher en utilisant aligned
                        if len(lignes) > 1:
                            body.append("\\[")
                            body.append("\\begin{aligned}")
                            for ligne in lignes:
                                body.append(f"&{ligne} \\\\")
                            # Enlever le dernier \\
                            body[-1] = body[-1].rstrip(' \\\\')
                            body.append("\\end{aligned}")
                            body.append("\\]")
                        else:
                            body.append(f"$${contenu}$$")
                    else:
                        # Pour les autres types de résultats longs, utiliser dmath*
                        body.append("\\begin{dmath*}")
                        body.append(contenu)
                        body.append("\\end{dmath*}")
                else:
                    # Résultat normal
                    body.append(res_complet)
                
                body.append(f"\\par\\medskip")
            in_res = False
            res_lines = []
        elif in_res and l:
            # Accumuler les lignes de résultat
            res_lines.append(l)
    
    # Traiter le dernier résultat si la boucle se termine sans ---BLOCK---
    if in_res and res_lines:
        res_complet = ' '.join(res_lines)
        
        body.append(f"\\textbf{{Résultat :}}")
        body.append(f"\\par\\noindent")
        
        # Pour les très longs résultats, traiter spécialement
        if len(res_complet) > 120 and res_complet.startswith('$$') and res_complet.endswith('$$'):
            # Extraire le contenu sans les $$
            contenu = res_complet[2:-2].strip()
            
            # Pour les listes simples (comme [1,2,3,...]), découper manuellement
            if '\\left[' in contenu and '\\right]' in contenu:
                # Supprimer \left et \right
                contenu = contenu.replace('\\left', '').replace('\\right', '')
                
                # Découper tous les 10 éléments (environ tous les 30-40 caractères)
                parties = contenu.split(',')
                lignes = []
                ligne_temp = ""
                
                for i, partie in enumerate(parties):
                    if i == 0:
                        ligne_temp = partie + ','
                    elif (i % 10 == 0):
                        lignes.append(ligne_temp.strip())
                        ligne_temp = partie + ','
                    else:
                        ligne_temp += partie + ','
                
                # Ajouter la dernière ligne (enlever la dernière virgule)
                if ligne_temp:
                    lignes.append(ligne_temp.rstrip(',').strip())
                
                # Afficher en utilisant aligned
                if len(lignes) > 1:
                    body.append("\\[")
                    body.append("\\begin{aligned}")
                    for ligne in lignes:
                        body.append(f"&{ligne} \\\\")
                    # Enlever le dernier \\
                    body[-1] = body[-1].rstrip(' \\\\')
                    body.append("\\end{aligned}")
                    body.append("\\]")
                else:
                    body.append(f"$${contenu}$$")
            else:
                # Pour les autres types de résultats longs, utiliser dmath*
                body.append("\\begin{dmath*}")
                body.append(contenu)
                body.append("\\end{dmath*}")
        else:
            # Résultat normal
            body.append(res_complet)
        
        body.append(f"\\par\\medskip")
    
    with open(tmp_tex, 'w', encoding='utf-8') as f:
        f.write(header + "\n".join(body) + r"\end{document}")
    return True

def main():
    if len(sys.argv) < 3:
        print("Usage: python3 mac2pdf_cli.py <fichier.mac> <sortie.pdf> [font_size] [img_scale]")
        sys.exit(1)
    
    mac_file = sys.argv[1]
    pdf_output = sys.argv[2]
    font_size = sys.argv[3] if len(sys.argv) > 3 else "10pt"
    img_scale = float(sys.argv[4]) if len(sys.argv) > 4 else 0.5
    
    cmds = lire_fichier_mac(mac_file)
    if not cmds:
        print("Erreur: Fichier vide ou introuvable", file=sys.stderr)
        sys.exit(1)
    
    tmp_dir = tempfile.mkdtemp()
    try:
        tex_path = os.path.join(tmp_dir, "out.tex")
        drv_path = os.path.join(tmp_dir, "drv.mac")
        
        with open(drv_path, 'w', encoding='utf-8') as f:
            f.write(generer_script_maxima(cmds, tex_path, tmp_dir))
        
        subprocess.run(['maxima', '--very-quiet', '-b', drv_path], 
                      capture_output=True, check=True)
        
        if finaliser_tex(tex_path, font_size, img_scale):
            subprocess.run(['pdflatex', '-interaction=nonstopmode', 'out.tex'], 
                          cwd=tmp_dir, capture_output=True, check=True)
            
            pdf_path = os.path.join(tmp_dir, "out.pdf")
            if os.path.exists(pdf_path):
                shutil.copy2(pdf_path, pdf_output)
                print("PDF généré avec succès")
            else:
                print("Erreur: PDF non généré", file=sys.stderr)
                sys.exit(1)
        else:
            print("Erreur: Maxima n'a produit aucune sortie", file=sys.stderr)
            sys.exit(1)
    finally:
        shutil.rmtree(tmp_dir)

if __name__ == "__main__":
    main()
