part of '../document_viewer.dart';

class _ArchiveViewer extends StatefulWidget {
  final Uint8List bytes;
  final String filename;
  final PyramidTheme pt;
  const _ArchiveViewer({required this.bytes, required this.filename, required this.pt});

  @override
  State<_ArchiveViewer> createState() => _ArchiveViewerState();
}
class _ArchiveViewerState extends State<_ArchiveViewer> {
  List<_ArchiveEntry>? _entries;
  String? _error;

  @override
  void initState() {
    super.initState();
    _decode();
  }

  void _decode() {
    try {
      final ext = widget.filename.contains('.') ? widget.filename.split('.').last.toLowerCase() : '';
      Archive archive;
      if (ext == 'tar') {
        archive = TarDecoder().decodeBytes(widget.bytes);
      } else if (ext == 'gz' || ext == 'tgz') {
        final inner = GZipDecoder().decodeBytes(widget.bytes);
        archive = TarDecoder().decodeBytes(inner);
      } else {
        archive = ZipDecoder().decodeBytes(widget.bytes);
      }

      final entries = archive.files.map((f) => _ArchiveEntry(name: f.name, size: f.size, isDir: !f.isFile)).toList()
        ..sort((a, b) {
          if (a.isDir != b.isDir) return a.isDir ? -1 : 1;
          return a.name.compareTo(b.name);
        });
      setState(() => _entries = entries);
    } catch (e) {
      setState(() => _error = e.toString());
    }
  }

  String _fmtSize(int b) {
    if (b < 1024) return '$b B';
    if (b < 1024 * 1024) return '${(b / 1024).toStringAsFixed(1)} KB';
    return '${(b / (1024 * 1024)).toStringAsFixed(1)} MB';
  }

  @override
  Widget build(BuildContext context) {
    if (_error != null) return _ErrorView(message: _error!, pt: widget.pt);
    if (_entries == null) return Center(child: CircularProgressIndicator(strokeWidth: 2, color: widget.pt.accent));

    final pt = widget.pt;
    final entries = _entries!;
    final totalBytes = entries.fold<int>(0, (s, e) => s + (e.isDir ? 0 : e.size));

    return Column(
      children: [
        Container(
          width: double.infinity,
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          color: pt.bg1,
          child: Row(
            children: [
              Icon(Icons.folder_zip_outlined, size: 15, color: pt.fgDim),
              const SizedBox(width: 8),
              Text('${entries.length} Einträge · ${_fmtSize(totalBytes)} unkomprimiert', style: TextStyle(color: pt.fgDim, fontSize: 12)),
            ],
          ),
        ),
        Expanded(
          child: ListView.builder(
            padding: const EdgeInsets.symmetric(vertical: 4),
            itemCount: entries.length,
            itemBuilder: (ctx, i) {
              final e = entries[i];
              return ListTile(
                dense: true,
                visualDensity: VisualDensity.compact,
                leading: Icon(e.isDir ? Icons.folder_outlined : _fileIcon(e.name), size: 18, color: e.isDir ? pt.accent : pt.fgDim),
                title: Text(e.name, style: TextStyle(color: pt.fg, fontSize: 13), overflow: TextOverflow.ellipsis),
                trailing: e.isDir ? null : Text(_fmtSize(e.size), style: TextStyle(color: pt.fgDim, fontSize: 11)),
              );
            },
          ),
        ),
      ],
    );
  }

  IconData _fileIcon(String name) {
    final ext = name.contains('.') ? name.split('.').last.toLowerCase() : '';
    return switch (ext) {
      'dart' || 'py' || 'js' || 'ts' || 'json' || 'xml' || 'yaml' || 'toml' => Icons.code_rounded,
      'png' || 'jpg' || 'jpeg' || 'gif' || 'webp' || 'svg'                  => Icons.image_outlined,
      'mp4' || 'mov' || 'avi' || 'mkv'                                       => Icons.videocam_outlined,
      'mp3' || 'ogg' || 'flac' || 'wav'                                      => Icons.music_note_outlined,
      'pdf'                                                                    => Icons.picture_as_pdf_outlined,
      'zip' || 'tar' || 'gz' || '7z' || 'rar'                               => Icons.folder_zip_outlined,
      'md' || 'markdown'                                                       => Icons.article_outlined,
      _                                                                        => Icons.insert_drive_file_outlined,
    };
  }
}
class _ArchiveEntry {
  final String name;
  final int size;
  final bool isDir;
  const _ArchiveEntry({required this.name, required this.size, required this.isDir});
}
// Extracts the JPEG thumbnail stored in PSD Image Resource 1036.
Uint8List? _extractPsdThumbnail(Uint8List bytes) {
  try {
    if (bytes.length < 34) return null;
    final data = ByteData.sublistView(bytes);
    // Verify '8BPS' signature
    if (bytes[0] != 0x38 || bytes[1] != 0x42 || bytes[2] != 0x50 || bytes[3] != 0x53) return null;

    // PSD is big-endian throughout.
    // Color Mode Data section length at offset 26.
    final cmdLen = data.getUint32(26, Endian.big);
    // Image Resources section starts after 26-byte header + 4-byte length field + cmdLen bytes.
    final irPos = 30 + cmdLen;
    if (irPos + 4 > bytes.length) return null;
    final irLen = data.getUint32(irPos, Endian.big);

    int pos = irPos + 4;
    final irEnd = pos + irLen;

    while (pos + 10 <= irEnd && pos + 10 <= bytes.length) {
      // Signature: '8BIM'
      if (bytes[pos] != 0x38 || bytes[pos+1] != 0x42 || bytes[pos+2] != 0x49 || bytes[pos+3] != 0x4D) break;
      pos += 4;

      final resourceId = data.getUint16(pos, Endian.big);
      pos += 2;

      // Pascal string: 1-byte length + string, total padded to even.
      final nameLen = bytes[pos];
      pos += (nameLen + 2) & ~1;

      if (pos + 4 > bytes.length) break;
      final dataSize = data.getUint32(pos, Endian.big);
      pos += 4;

      // Resource 1036 (Photoshop 5+) or 1033 (older) = Thumbnail
      if ((resourceId == 0x040C || resourceId == 0x0409) && dataSize >= 28) {
        final format = data.getUint32(pos, Endian.big);
        if (format == 1) { // kJpegRGB
          // Use dataSize-28 for the JPEG extent (more reliable than the
          // compressedSize field at pos+20, which varies across PS versions).
          final jpegStart = pos + 28;
          final jpegEnd = (pos + dataSize).clamp(jpegStart, bytes.length);
          if (jpegEnd > jpegStart) {
            final thumb = Uint8List.sublistView(bytes, jpegStart, jpegEnd);
            // Validate JPEG magic bytes (FF D8)
            if (thumb.length >= 2 && thumb[0] == 0xFF && thumb[1] == 0xD8) {
              return thumb;
            }
            // Some files need a forward scan for the actual JPEG start
            for (var i = 0; i < thumb.length - 1; i++) {
              if (thumb[i] == 0xFF && thumb[i + 1] == 0xD8) {
                return Uint8List.sublistView(thumb, i);
              }
            }
          }
        }
        return null;
      }

      // Advance past this resource's data (padded to even).
      final advance = (dataSize + 1) & ~1;
      if (advance == 0 && dataSize == 0) { pos += 0; } else { pos += advance; }
    }
    return null;
  } catch (_) {
    return null;
  }
}
class _PsdThumbViewer extends StatelessWidget {
  final Uint8List bytes;
  final String filename;
  final PyramidTheme pt;
  const _PsdThumbViewer({required this.bytes, required this.filename, required this.pt});

  @override
  Widget build(BuildContext context) {
    final thumb = _extractPsdThumbnail(bytes);
    if (thumb == null) {
      return _ProprietaryViewer(filename: filename, bytes: bytes, pt: pt);
    }
    return Column(
      children: [
        Container(
          width: double.infinity,
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
          color: pt.bg2,
          child: Row(
            children: [
              Icon(Icons.info_outline_rounded, size: 14, color: pt.fgDim),
              const SizedBox(width: 8),
              Text('Eingebettete Vorschau (Vollbild nicht verfügbar)', style: TextStyle(color: pt.fgDim, fontSize: 12)),
            ],
          ),
        ),
        Expanded(
          child: InteractiveViewer(
            minScale: 0.5,
            maxScale: 10,
            child: Padding(
              padding: const EdgeInsets.all(24),
              child: Image.memory(thumb, fit: BoxFit.contain),
            ),
          ),
        ),
      ],
    );
  }
}
// ZIP-preview viewer (Sketch, XD).
// These formats are ZIP archives that embed a preview PNG.
class _ZipPreviewViewer extends StatefulWidget {
  final Uint8List bytes;
  final String filename;
  final PyramidTheme pt;
  const _ZipPreviewViewer({required this.bytes, required this.filename, required this.pt});

  @override
  State<_ZipPreviewViewer> createState() => _ZipPreviewViewerState();
}
class _ZipPreviewViewerState extends State<_ZipPreviewViewer> {
  Uint8List? _preview;
  bool _loaded = false;
  bool _isImage = false;

  @override
  void initState() {
    super.initState();
    _extract();
  }

  void _extract() {
    try {
      final archive = ZipDecoder().decodeBytes(widget.bytes);
      // Priority search list
      const candidates = [
        'previews/preview.png', 'preview.png', 'thumbnail.png',
        'thumbnails/thumbnail.png', 'preview.jpg', 'thumbnail.jpg',
      ];
      ArchiveFile? found;
      for (final path in candidates) {
        found = archive.findFile(path);
        if (found != null && found.isFile) break;
      }
      // Fallback: any file with preview/thumbnail in the name
      if (found == null) {
        for (final f in archive.files) {
          if (!f.isFile) continue;
          final n = f.name.toLowerCase();
          if ((n.contains('preview') || n.contains('thumb')) &&
              (n.endsWith('.png') || n.endsWith('.jpg') || n.endsWith('.jpeg'))) {
            found = f;
            break;
          }
        }
      }
      if (found != null) {
        setState(() {
          _preview = Uint8List.fromList(found!.content as List<int>);
          _isImage = true;
          _loaded = true;
        });
      } else {
        setState(() => _loaded = true);
      }
    } catch (_) {
      setState(() => _loaded = true);
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    if (!_loaded) {
      return Center(child: CircularProgressIndicator(strokeWidth: 2, color: pt.accent));
    }
    if (_isImage && _preview != null) {
      return Column(
        children: [
          Container(
            width: double.infinity,
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
            color: pt.bg2,
            child: Row(
              children: [
                Icon(Icons.info_outline_rounded, size: 14, color: pt.fgDim),
                const SizedBox(width: 8),
                Text('Eingebettete Vorschau', style: TextStyle(color: pt.fgDim, fontSize: 12)),
              ],
            ),
          ),
          Expanded(
            child: InteractiveViewer(
              minScale: 0.5,
              maxScale: 10,
              child: Padding(
                padding: const EdgeInsets.all(24),
                child: Image.memory(_preview!, fit: BoxFit.contain),
              ),
            ),
          ),
        ],
      );
    }
    // No preview image found — fall back to archive listing
    return _ArchiveViewer(bytes: widget.bytes, filename: widget.filename, pt: pt);
  }
}
