import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/services.dart' show rootBundle;
import 'package:syncfusion_flutter_pdf/pdf.dart';
import 'package:flutter/material.dart';

import 'global_data.dart';
import 'models.dart';
import 'cai_mapping.dart';

class PdfEngine {

  static Future<List<int>> generaDocumentoCai() async {
    PdfDocument? document;
    try {
      final ByteData data = await rootBundle.load('assets/CAI_p1.pdf');

      // 1. Caricamento e Copia
      final List<int> bytesCopia = data.buffer.asUint8List().toList();
      document = PdfDocument(inputBytes: bytesCopia);

      final PdfForm form = document.form;
      final PdfPage page = document.pages[0];

      // form.setDefaultAppearance(false); // REMOVED to fix empty fields issue

      // Mappatura Campi
      Map<String, PdfField> mappaCampi = {};
      for (int i = 0; i < form.fields.count; i++) {
        if (form.fields[i].name != null) {
          mappaCampi[form.fields[i].name!.trim().toUpperCase()] = form.fields[i];
        }
      }

      // --- COMPILAZIONE ---

      // 1. TESTI STANDARD (Escludiamo i Danni per gestirli dopo con split a 20 char)
      CaiMapping.testi.forEach((keyGlobal, keyPdf) {
        if (keyGlobal.contains("danni_visibili")) return;

        String valore = _valoreDaGlobal(keyGlobal);
        String keyPdfNorm = keyPdf.trim().toUpperCase();

        if (mappaCampi.containsKey(keyPdfNorm) && valore.isNotEmpty) {
          final field = mappaCampi[keyPdfNorm];
          if (field is PdfTextBoxField) {
            field.font = PdfStandardFont(PdfFontFamily.helvetica, 8); // Font 8
            field.foreColor = PdfColor(0, 0, 0);
            field.textAlignment = PdfTextAlignment.left;
            field.text = valore.toUpperCase();
          }
        }
      });

      // 1.1 GESTIONE SPECIALE DANNI (Split forzato a 20 caratteri)
      _riempiCampoSplit(mappaCampi, GlobalData.danni_visibili_A, "DANNI_VIS_A1", "DANNI_VIS_A2");
      _riempiCampoSplit(mappaCampi, GlobalData.danni_visibili_B, "DANNI_VIS_B1", "DANNI_VIS_B2");


      // 2. CHECKBOX
      _scriviX(mappaCampi, [GlobalData.feriti ? CaiMapping.feriti_SI : CaiMapping.feriti_NO]);
      _scriviX(mappaCampi, [GlobalData.Veicoli_danni_materiali_oltre ? CaiMapping.danni_veicoli_SI : CaiMapping.danni_veicoli_NO]);
      _scriviX(mappaCampi, [GlobalData.Oggetti_diversi_danni_materiali ? CaiMapping.danni_oggetti_SI : CaiMapping.danni_oggetti_NO]);
      _scriviX(mappaCampi, [GlobalData.FLAG_danni_mat_assicurati_A ? CaiMapping.danni_mat_A_SI : CaiMapping.danni_mat_A_NO]);
      _scriviX(mappaCampi, [GlobalData.FLAG_danni_mat_assicurati_B ? CaiMapping.danni_mat_B_SI : CaiMapping.danni_mat_B_NO]);

      String catA = GlobalData.Categoria_cond_A.toUpperCase().trim();
      if (catA == 'A') _scriviX(mappaCampi, ['cat_a_A']);
      else if (catA == 'B') _scriviX(mappaCampi, ['cat_b_A']);
      else if (catA.isNotEmpty) _scriviTesto(mappaCampi, ['cat_altro_A'], catA);

      String catB = GlobalData.Categoria_cond_B.toUpperCase().trim();
      if (catB == 'A') _scriviX(mappaCampi, ['cat_a_B']);
      else if (catB == 'B') _scriviX(mappaCampi, ['cat_b_B']);
      else if (catB.isNotEmpty) _scriviTesto(mappaCampi, ['cat_altro_B'], catB);

      // 3. CIRCOSTANZE
      int countA = 0;
      int countB = 0;
      for (int i = 1; i <= 17; i++) {
        if (GlobalData.circostanzeA[i] == true) {
          if (_scriviX(mappaCampi, [i < 10 ? "A0$i" : "A$i"])) countA++;
        }
        if (GlobalData.circostanzeB[i] == true) {
          List<String> nomiTarget = [];
          if (i == 9) nomiTarget = ["Check Box 26", "CheckBox26", "26"];
          else if (i == 10) nomiTarget = ["Check Box 27", "CheckBox27", "27"];
          else if (i == 11) nomiTarget = ["Check Box 28", "CheckBox28", "28"];
          else if (i == 12) nomiTarget = ["Check Box 29", "CheckBox29", "29"];
          else nomiTarget = [i < 10 ? "B0$i" : "B$i"];
          if (_scriviX(mappaCampi, nomiTarget)) countB++;
        }
      }
      _scriviTestoTotale(mappaCampi, ['A_TOT', 'A_tot'], countA.toString());
      _scriviTestoTotale(mappaCampi, ['B_TOT', 'B_tot'], countB.toString());

      // 4. PUNTI URTO
      for (String punto in GlobalData.puntiUrtoA_List) _scriviXRossa(mappaCampi, [punto]);
      for (String punto in GlobalData.puntiUrtoB_List) _scriviXRossa(mappaCampi, [punto]);

      // 5. IMMAGINI E GRAFICO
      // Render the updated graph (with new vehicle types) to an image
      Uint8List? graficoBytes = await _renderGraficoV40(
          GlobalData.tratti.cast<TrattoPenna>().toList(),
          GlobalData.elementi.cast<ElementoGrafico>().toList()
      );

      // Draw the graph image into the specific PDF box
      await _disegnaInBox(page, mappaCampi, CaiMapping.box_grafico, graficoBytes);

      await _disegnaInBox(page, mappaCampi, CaiMapping.box_firma_A, await _renderFirmaTight(GlobalData.puntiFirmaA, Colors.black));
      await _disegnaInBox(page, mappaCampi, CaiMapping.box_firma_B, await _renderFirmaTight(GlobalData.puntiFirmaB, Colors.black));

      // =================================================================
      // SALVATAGGIO SICURO
      // =================================================================

      List<int> bytesTemporanei = await document.save();
      document.dispose();

      PdfDocument docFinale = PdfDocument(inputBytes: bytesTemporanei);

      try {
        docFinale.form.flattenAllFields();
      } catch (e) {
        debugPrint("⚠️ Errore Flattening: $e");
        docFinale.form.readOnly = true;
      }

      List<int> bytesFinali = await docFinale.save();
      docFinale.dispose();

      return bytesFinali;

    } catch (e) {
      debugPrint("ERRORE GENERAZIONE PDF: $e");
      return [];
    }
  }

  // ... (Keep existing helper methods: _riempiCampoSplit, _scriviX, _scriviTesto, _scriviXRossa, _scriviTestoTotale, _disegnaInBox, _renderFirmaTight, _valoreDaGlobal)
  // Re-pasting them here for completeness to ensure no missing dependencies

  static void _riempiCampoSplit(Map<String, PdfField> mappa, String testoCompleto, String key1, String key2) {
    if (testoCompleto.isEmpty) return;
    String riga1 = "";
    String riga2 = "";
    int limite = 20;
    if (testoCompleto.length <= limite) {
      riga1 = testoCompleto;
    } else {
      int splitIndex = testoCompleto.lastIndexOf(" ", limite);
      if (splitIndex == -1) splitIndex = limite;
      riga1 = testoCompleto.substring(0, splitIndex).trim();
      riga2 = testoCompleto.substring(splitIndex).trim();
    }
    if (mappa.containsKey(key1)) {
      final f1 = mappa[key1] as PdfTextBoxField;
      f1.font = PdfStandardFont(PdfFontFamily.helvetica, 8);
      f1.textAlignment = PdfTextAlignment.left;
      f1.text = riga1.toUpperCase();
    }
    if (mappa.containsKey(key2)) {
      final f2 = mappa[key2] as PdfTextBoxField;
      f2.font = PdfStandardFont(PdfFontFamily.helvetica, 8);
      f2.textAlignment = PdfTextAlignment.left;
      f2.text = riga2.toUpperCase();
    }
  }

  static bool _scriviX(Map<String, PdfField> mappa, List<String> nomiPossibili) {
    for (String nome in nomiPossibili) {
      String key = nome.trim().toUpperCase();
      if (mappa.containsKey(key)) {
        final field = mappa[key]!;
        if (field is PdfTextBoxField) {
          field.font = PdfStandardFont(PdfFontFamily.helvetica, 14);
          field.foreColor = PdfColor(0, 0, 0);
          field.textAlignment = PdfTextAlignment.center;
          field.text = "X";
        } else if (field is PdfCheckBoxField) {
          field.isChecked = true;
        }
        return true;
      }
    }
    return false;
  }

  static void _scriviTesto(Map<String, PdfField> mappa, List<String> nomiPossibili, String testo) {
    for (String nome in nomiPossibili) {
      String key = nome.trim().toUpperCase();
      if (mappa.containsKey(key)) {
        final field = mappa[key]!;
        if (field is PdfTextBoxField) {
          field.font = PdfStandardFont(PdfFontFamily.helvetica, 8);
          field.foreColor = PdfColor(0, 0, 0);
          field.text = testo;
        }
        return;
      }
    }
  }

  static bool _scriviXRossa(Map<String, PdfField> mappa, List<String> nomiPossibili) {
    for (String nome in nomiPossibili) {
      String key = nome.trim().toUpperCase();
      if (mappa.containsKey(key)) {
        final field = mappa[key]!;
        if (field is PdfTextBoxField) {
          field.font = PdfStandardFont(PdfFontFamily.helvetica, 16, style: PdfFontStyle.bold);
          field.foreColor = PdfColor(255, 0, 0);
          field.textAlignment = PdfTextAlignment.center;
          field.text = "X";
          return true;
        }
      }
    }
    return false;
  }

  static void _scriviTestoTotale(Map<String, PdfField> mappa, List<String> nomi, String testo) {
    for (String nome in nomi) {
      String key = nome.trim().toUpperCase();
      if (mappa.containsKey(key)) {
        final field = mappa[key]!;
        if (field is PdfTextBoxField) {
          field.font = PdfStandardFont(PdfFontFamily.helvetica, 8);
          field.textAlignment = PdfTextAlignment.center;
          field.text = testo;
        }
        return;
      }
    }
  }

  static Future<void> _disegnaInBox(PdfPage page, Map<String, PdfField> mappa, String nomeCampo, Uint8List? imgBytes) async {
    String key = nomeCampo.trim().toUpperCase();
    if (imgBytes == null || !mappa.containsKey(key)) return;
    Rect boxRect = mappa[key]!.bounds;
    PdfBitmap bitmap = PdfBitmap(imgBytes);
    double imageW = bitmap.width.toDouble();
    double imageH = bitmap.height.toDouble();
    if (imageW <= 0 || imageH <= 0) return;
    double ratioX = boxRect.width / imageW;
    double ratioY = boxRect.height / imageH;
    double scale = (ratioX < ratioY) ? ratioX : ratioY;
    double drawW = imageW * scale;
    double drawH = imageH * scale;
    double offsetX = boxRect.left + (boxRect.width - drawW) / 2;
    double offsetY = boxRect.top + (boxRect.height - drawH) / 2;
    page.graphics.drawImage(bitmap, Rect.fromLTWH(offsetX, offsetY, drawW, drawH));
  }

  static Future<Uint8List?> _renderFirmaTight(List<Offset?> punti, Color colore) async {
    if (punti.isEmpty) return null;
    double minX = double.infinity, minY = double.infinity, maxX = double.negativeInfinity, maxY = double.negativeInfinity;
    for (var p in punti) { if (p != null) { if (p.dx < minX) minX = p.dx; if (p.dx > maxX) maxX = p.dx; if (p.dy < minY) minY = p.dy; if (p.dy > maxY) maxY = p.dy; } }
    double padding = 20.0;
    double firmaW = maxX - minX;
    double firmaH = maxY - minY;
    if (firmaW <= 0) firmaW = 1; if (firmaH <= 0) firmaH = 1;
    double resolutionScale = 3.0;
    double canvasW = (firmaW + padding * 2) * resolutionScale;
    double canvasH = (firmaH + padding * 2) * resolutionScale;
    final recorder = ui.PictureRecorder();
    final canvas = Canvas(recorder);
    canvas.scale(resolutionScale);
    canvas.translate(-minX + padding, -minY + padding);
    final paint = Paint()..color = colore..strokeWidth = 5.0..style = PaintingStyle.stroke..strokeCap = StrokeCap.round..strokeJoin = StrokeJoin.round;
    for (int i = 0; i < punti.length - 1; i++) { if (punti[i] != null && punti[i+1] != null) { canvas.drawLine(punti[i]!, punti[i+1]!, paint); } }
    final img = await recorder.endRecording().toImage(canvasW.toInt(), canvasH.toInt());
    final byteData = await img.toByteData(format: ui.ImageByteFormat.png);
    return byteData?.buffer.asUint8List();
  }

  static Future<Uint8List?> _renderGraficoV40(List<TrattoPenna> tratti, List<ElementoGrafico> elementi) async {
    final recorder = ui.PictureRecorder();
    final canvas = Canvas(recorder);
    final size = const Size(2000, 800);
    final painter = PainterV40(tratti, elementi);
    painter.paint(canvas, size);
    final img = await recorder.endRecording().toImage(size.width.toInt(), size.height.toInt());
    final byteData = await img.toByteData(format: ui.ImageByteFormat.png);
    return byteData?.buffer.asUint8List();
  }

  static String _valoreDaGlobal(String key) {
    switch (key) {
      case 'data_incidente': return GlobalData.data_incidente;
      case 'ora': return GlobalData.ora;
      case 'luogo': return GlobalData.luogo;
      case 'testimoni': return GlobalData.testimoni;
      case 'danni_visibili_A': return GlobalData.danni_visibili_A;
      case 'osservazioni_A': return GlobalData.osservazioni_A;
      case 'danni_visibili_B': return GlobalData.danni_visibili_B;
      case 'osservazioni_B': return GlobalData.osservazioni_B;
      case 'Cognome_contraente_A': return GlobalData.Cognome_contraente_A;
      case 'Nome_contraente_A': return GlobalData.Nome_contraente_A;
      case 'Codice_Fiscale_contraente_A': return GlobalData.Codice_Fiscale_contraente_A;
      case 'Indirizzo_contraente_A': return GlobalData.Indirizzo_contraente_A;
      case 'CAP_contraente_A': return GlobalData.CAP_contraente_A;
      case 'Stato_contraente_A': return GlobalData.Stato_contraente_A;
      case 'N_telefono_mail_contraente_A': return GlobalData.N_telefono_mail_contraente_A;
      case 'Marca_e_Tipo_A': return GlobalData.Marca_e_Tipo_A;
      case 'Targa_A': return GlobalData.Targa_A;
      case 'Stato_immatricolazione_A': return GlobalData.Stato_immatricolazione_A;
      case 'Rimorchio_A': return GlobalData.Rimorchio_A;
      case 'Stato_immatricolazione2_A': return GlobalData.Stato_immatricolazione2_A;
      case 'Denominazione_A': return GlobalData.Denominazione_A;
      case 'Numero_Polizza_A': return GlobalData.Numero_Polizza_A;
      case 'N_carta_verde_A': return GlobalData.N_carta_verde_A;
      case 'Data_Inizio_Dal_A': return GlobalData.Data_Inizio_Dal_A;
      case 'Data_Scadenza_Al_A': return GlobalData.Data_Scadenza_Al_A;
      case 'Agenzia_A': return GlobalData.Agenzia_A;
      case 'Indirizzo_agenzia_A': return GlobalData.Indirizzo_agenzia_A;
      case 'Stato_agenzia_A': return GlobalData.Stato_agenzia_A;
      case 'Denominazione_agenzia_A': return GlobalData.Denominazione_agenzia_A;
      case 'N_tel_mail_agenzia_A': return GlobalData.N_tel_mail_agenzia_A;
      case 'Cognome_cond_A': return GlobalData.Cognome_cond_A;
      case 'Nome_cond_A': return GlobalData.Nome_cond_A;
      case 'Data_nascita_cond_A': return GlobalData.Data_nascita_cond_A;
      case 'Cod_fiscale_cond_A': return GlobalData.Cod_fiscale_cond_A;
      case 'Indirizzo_cond_A': return GlobalData.Indirizzo_cond_A;
      case 'Stato_cond_A': return GlobalData.Stato_cond_A;
      case 'N_tel_mail_cond_A': return GlobalData.N_tel_mail_cond_A;
      case 'N_Patente_cond_A': return GlobalData.N_Patente_cond_A;
      case 'Scadenza_cond_A': return GlobalData.Scadenza_cond_A;
      case 'Cognome_contraente_B': return GlobalData.Cognome_contraente_B;
      case 'Nome_contraente_B': return GlobalData.Nome_contraente_B;
      case 'Codice_Fiscale_contraente_B': return GlobalData.Codice_Fiscale_contraente_B;
      case 'Indirizzo_contraente_B': return GlobalData.Indirizzo_contraente_B;
      case 'CAP_contraente_B': return GlobalData.CAP_contraente_B;
      case 'Stato_contraente_B': return GlobalData.Stato_contraente_B;
      case 'N_telefono_mail_contraente_B': return GlobalData.N_telefono_mail_contraente_B;
      case 'Marca_e_Tipo_B': return GlobalData.Marca_e_Tipo_B;
      case 'Targa_B': return GlobalData.Targa_B;
      case 'Stato_immatricolazione_B': return GlobalData.Stato_immatricolazione_B;
      case 'Rimorchio_B': return GlobalData.Rimorchio_B;
      case 'Stato_immatricolazione2_B': return GlobalData.Stato_immatricolazione2_B;
      case 'Denominazione_B': return GlobalData.Denominazione_B;
      case 'Numero_Polizza_B': return GlobalData.Numero_Polizza_B;
      case 'N_carta_verde_B': return GlobalData.N_carta_verde_B;
      case 'Data_Inizio_Dal_B': return GlobalData.Data_Inizio_Dal_B;
      case 'Data_Scadenza_Al_B': return GlobalData.Data_Scadenza_Al_B;
      case 'Agenzia_B': return GlobalData.Agenzia_B;
      case 'Indirizzo_agenzia_B': return GlobalData.Indirizzo_agenzia_B;
      case 'Stato_agenzia_B': return GlobalData.Stato_agenzia_B;
      case 'Denominazione_agenzia_B': return GlobalData.Denominazione_agenzia_B;
      case 'N_tel_mail_agenzia_B': return GlobalData.N_tel_mail_agenzia_B;
      case 'Cognome_cond_B': return GlobalData.Cognome_cond_B;
      case 'Nome_cond_B': return GlobalData.Nome_cond_B;
      case 'Data_nascita_cond_B': return GlobalData.Data_nascita_cond_B;
      case 'Cod_fiscale_cond_B': return GlobalData.Cod_fiscale_cond_B;
      case 'Indirizzo_cond_B': return GlobalData.Indirizzo_cond_B;
      case 'Stato_cond_B': return GlobalData.Stato_cond_B;
      case 'N_tel_mail_cond_B': return GlobalData.N_tel_mail_cond_B;
      case 'N_Patente_cond_B': return GlobalData.N_Patente_cond_B;
      case 'Scadenza_cond_B': return GlobalData.Scadenza_cond_B;
      default: return "";
    }
  }
}

class PainterV40 extends CustomPainter {
  final List<TrattoPenna> tr;
  final List<ElementoGrafico> el;

  PainterV40(this.tr, this.el);

  final List<Color> palette = [Colors.blue, Colors.orange, Colors.green, Colors.purple, Colors.red];

  @override
  void paint(Canvas canvas, Size size) {
    // 1. SFONDO BIANCO
    final Paint backgroundPaint = Paint()..color = Colors.white;
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), backgroundPaint);

    // 2. GRIGLIA
    final Paint gridPaint = Paint()..color = Colors.grey.shade300..strokeWidth = 2.0;
    double step = 40.0;
    for (double x = 0; x <= size.width; x += step) {
      canvas.drawLine(Offset(x, 0), Offset(x, size.height), gridPaint);
    }
    for (double y = 0; y <= size.height; y += step) {
      canvas.drawLine(Offset(0, y), Offset(size.width, y), gridPaint);
    }

    if (tr.isEmpty && el.isEmpty) return;

    // --- BOUNDING BOX ---
    double minX = double.infinity, minY = double.infinity;
    double maxX = double.negativeInfinity, maxY = double.negativeInfinity;

    for (var t in tr) {
      for (var p in t.punti) {
        if (p.dx < minX) minX = p.dx;
        if (p.dx > maxX) maxX = p.dx;
        if (p.dy < minY) minY = p.dy;
        if (p.dy > maxY) maxY = p.dy;
      }
    }
    for (var e in el) {
      if (e.posizione.dx - 30 < minX) minX = e.posizione.dx - 30;
      if (e.posizione.dx + 30 > maxX) maxX = e.posizione.dx + 30;
      if (e.posizione.dy - 30 < minY) minY = e.posizione.dy - 30;
      if (e.posizione.dy + 30 > maxY) maxY = e.posizione.dy + 30;
    }

    if (minX == double.infinity) { minX = 0; maxX = 100; minY = 0; maxY = 100; }

    double padding = 40.0;
    double drawingW = maxX - minX + (padding * 2);
    double drawingH = maxY - minY + (padding * 2);

    if (drawingW <= 0) drawingW = 100;
    if (drawingH <= 0) drawingH = 100;

    double scaleX = size.width / drawingW;
    double scaleY = size.height / drawingH;
    double scale = (scaleX < scaleY) ? scaleX : scaleY;

    double offsetX = (size.width - (drawingW * scale)) / 2;
    double offsetY = (size.height - (drawingH * scale)) / 2;

    canvas.save();
    canvas.translate(offsetX, offsetY);
    canvas.scale(scale);
    canvas.translate(-minX + padding, -minY + padding);

    // --- DISEGNO STRADE E FRECCE ---
    Paint pStrada = Paint()
      ..color = Colors.black
      ..strokeWidth = 4.0 / scale
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    for (var t in tr) {
      if (t.punti.length > 1) {
        Path path = Path()..moveTo(t.punti[0].dx, t.punti[0].dy);
        for (var pt in t.punti) path.lineTo(pt.dx, pt.dy);
        canvas.drawPath(path, pStrada);

        if (t.tipo == 'freccia') {
          double a = (t.punti.last - t.punti[t.punti.length - 2]).direction;
          canvas.drawLine(t.punti.last, t.punti.last - Offset.fromDirection(a - 0.5, 15), pStrada);
          canvas.drawLine(t.punti.last, t.punti.last - Offset.fromDirection(a + 0.5, 15), pStrada);
        }
      }
    }

    // --- DISEGNO ELEMENTI ---
    for (var e in el) {
      canvas.save();
      canvas.translate(e.posizione.dx, e.posizione.dy);
      canvas.rotate(e.rotazione);

      if (e.tipo == 'testo') {
        final tp = TextPainter(
            text: TextSpan(text: e.label ?? "", style: const TextStyle(color: Colors.black, fontSize: 24, fontWeight: FontWeight.bold)),
            textDirection: TextDirection.ltr
        )..layout();
        tp.paint(canvas, Offset(-tp.width/2, -tp.height/2));
      }
      else {
        String lettera = e.label ?? "A";
        int idx = (lettera.isNotEmpty) ? (lettera.codeUnitAt(0) - 65) % palette.length : 0;
        Color colore = palette[idx];

        if (e.tipo.startsWith('auto')) {
          _disegnaAuto(canvas, colore, lettera);
        } else if (e.tipo.startsWith('moto')) {
          _disegnaMoto(canvas, colore, lettera);
        } else if (e.tipo.startsWith('furgone')) {
          _disegnaFurgone(canvas, colore, lettera);
        }
      }
      canvas.restore();
    }

    canvas.restore();
  }

  // --- METODI DI DISEGNO SPECIFICI ---

  void _disegnaAuto(Canvas canvas, Color colore, String lettera) {
    double w = 48.0; double h = 24.0;
    Paint pBody = Paint()..color = colore;
    Paint pBorder = Paint()..style = PaintingStyle.stroke..color = Colors.black..strokeWidth = 1.5;

    // Corpo
    RRect bodyRect = RRect.fromRectAndRadius(Rect.fromCenter(center: Offset.zero, width: w, height: h), const Radius.circular(5));
    canvas.drawRRect(bodyRect, pBody);

    // Cabina
    Paint pCabin = Paint()..color = Colors.black.withValues(alpha: 0.2);
    canvas.drawRect(Rect.fromLTRB(-w/2 + 6, -h/2 + 3, w/4, h/2 - 3), pCabin);

    // Fari
    Paint pLights = Paint()..color = Colors.yellow;
    canvas.drawCircle(Offset(w/2 - 2, h/2 - 4), 2.5, pLights);
    canvas.drawCircle(Offset(w/2 - 2, -h/2 + 4), 2.5, pLights);

    // Bordo
    canvas.drawRRect(bodyRect, pBorder);

    // Lettera
    _disegnaLettera(canvas, lettera);
  }

  void _disegnaMoto(Canvas canvas, Color colore, String lettera) {
    double w = 34.0; double h = 12.0;
    Paint pBody = Paint()..color = colore;
    Paint pBorder = Paint()..style = PaintingStyle.stroke..color = Colors.black..strokeWidth = 1.0;
    Paint pBlack = Paint()..color = Colors.black;

    // Ruote (piene)
    canvas.drawCircle(Offset(-w/2 + 4, 0), 5.0, pBlack);
    canvas.drawCircle(Offset(w/2 - 4, 0), 5.0, pBlack);

    // Corpo
    Rect bodyRect = Rect.fromCenter(center: Offset.zero, width: w - 10, height: h - 4);
    canvas.drawRRect(RRect.fromRectAndRadius(bodyRect, const Radius.circular(4)), pBody);
    canvas.drawRRect(RRect.fromRectAndRadius(bodyRect, const Radius.circular(4)), pBorder);

    // Sella
    canvas.drawRect(Rect.fromCenter(center: Offset(-5, 0), width: 12, height: 8), pBlack);

    // Manubrio
    Paint pManubrio = Paint()..color = Colors.black..strokeWidth = 3.0..strokeCap = StrokeCap.round;
    canvas.drawLine(Offset(w/2 - 12, -10), Offset(w/2 - 12, 10), pManubrio);

    // Faro
    canvas.drawCircle(Offset(w/2, 0), 3.0, Paint()..color = Colors.yellow);

    _disegnaLettera(canvas, lettera, fontSize: 10);
  }

  void _disegnaFurgone(Canvas canvas, Color colore, String lettera) {
    double w = 60.0; double h = 26.0;
    Paint pBody = Paint()..color = colore;
    Paint pBorder = Paint()..style = PaintingStyle.stroke..color = Colors.black..strokeWidth = 1.5;

    // Vano Carico (Box posteriore)
    Rect caricoRect = Rect.fromLTRB(-w/2, -h/2, w/4, h/2);
    canvas.drawRect(caricoRect, pBody);
    canvas.drawRect(caricoRect, pBorder);

    // Cabina (Box anteriore)
    Rect cabinaRect = Rect.fromLTRB(w/4, -h/2 + 1, w/2, h/2 - 1);
    canvas.drawRect(cabinaRect, pBody);
    canvas.drawRect(cabinaRect, pBorder);

    // Parabrezza
    Paint pVetro = Paint()..color = Colors.black.withValues(alpha: 0.3);
    canvas.drawRect(Rect.fromLTRB(w/4 + 2, -h/2 + 3, w/2 - 2, h/2 - 3), pVetro);

    // Fari
    Paint pLights = Paint()..color = Colors.yellow;
    canvas.drawRect(Rect.fromLTWH(w/2 - 2, -h/2 + 2, 2, 4), pLights);
    canvas.drawRect(Rect.fromLTWH(w/2 - 2, h/2 - 6, 2, 4), pLights);

    _disegnaLettera(canvas, lettera);
  }

  void _disegnaLettera(Canvas canvas, String txt, {double fontSize = 16}) {
    final tp = TextPainter(
        text: TextSpan(text: txt, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: fontSize)),
        textDirection: TextDirection.ltr
    )..layout();
    tp.paint(canvas, Offset(-tp.width/2, -tp.height/2));
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}