import 'dart:io';
import 'dart:typed_data';

import 'package:charset_converter/charset_converter.dart';
import 'package:esc_pos_bluetooth/esc_pos_bluetooth.dart';
import 'package:esc_pos_utils/esc_pos_utils.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bluetooth_basic/flutter_bluetooth_basic.dart';
import 'package:image/image.dart' as Im;
import 'package:qr_flutter/qr_flutter.dart';
import 'package:satu/core/models/flow/check_bean.dart';
import 'package:satu/core/models/flow/check_row_bean.dart';
import 'package:satu/core/models/settings/printer_setting.dart';

Future<List<int>> getTestReceipt(String encoding, String paperSize) async {
  final CheckBean checkBean = CheckBean();
  final CheckRowBean rowBean1 = CheckRowBean();
  rowBean1.text = 'Тестовый чек';
  rowBean1.center = true;

  final CheckRowBean rowBean2 = CheckRowBean();
  rowBean2.text = ['Левая сторона', 'Правая сторона'];
  rowBean2.center = true;

  checkBean.rows.add(rowBean1);
  checkBean.rows.add(CheckRowBean()..text='{br}'..center=true);
  checkBean.rows.add(CheckRowBean()..text='{line}'..center=true);
  checkBean.rows.add(CheckRowBean()..text='{br}'..center=true);
  checkBean.rows.add(rowBean2);
  checkBean.rows.add(CheckRowBean()..text='{br}'..center=true);
  checkBean.rows.add(CheckRowBean()..text='{line}'..center=true);
  checkBean.qr = 'test qr data';


  return await _generateReceipt(encoding, paperSize, checkBean);
}

Future<List<int>> getReceipt(
    String encoding, String paperSize, CheckBean checkBean) async {
  return await _generateReceipt(encoding, paperSize, checkBean);
}

Future<List<int>> _generateReceipt(
    String encoding, String paperSize, CheckBean checkBean) async {
  List<int> bytes = [];
  final profile = await CapabilityProfile.load();
  final generator = Generator(
    paperSize == PrinterConst.paperSize58mm ? PaperSize.mm58 : PaperSize.mm80,
    profile,
  );
  String codeTable = 'CP866';
  if (encoding == PrinterConst.encodingCP866) {
    codeTable = 'CP866';
  } else if (encoding == PrinterConst.encodingCP1251) {
    codeTable = 'CP1251';
  }
  generator.setGlobalCodeTable(codeTable);

  for (CheckRowBean row in checkBean.rows) {
    String type = row.text.runtimeType.toString();
    final bool isList = type == 'List<dynamic>' || type == 'List<String>';
    final bool isNull = type == 'Null';
    final bool isString = type == 'String';
    final bool center = row.center;

    if (isNull) {
      continue;
    }
    if (isString) {
      String text = row.text as String;
      text = text.replaceAll('&laquo;', '«');
      text = text.replaceAll('&raquo;', '»');
      final bool isLine = '{line}' == text;
      final bool isBr = '{br}' == text;
      if (isBr) {
        bytes += generator.feed(1);
      } else if (isLine) {
        bytes += generator.hr();
      } else {
        final Uint8List firstCol =
            await CharsetConverter.encode(encoding.toLowerCase(), text);
        bytes += generator.textEncoded(firstCol,
            styles: PosStyles(align: center ? PosAlign.center : PosAlign.left));
      }
    }

    if (isList) {
      final List<dynamic> list = row.text as List<dynamic>;
      if (list.length == 2) {
        final Uint8List firstCol = await CharsetConverter.encode(
            encoding.toLowerCase(), list[0] as String);
        final Uint8List secondCol = await CharsetConverter.encode(
            encoding.toLowerCase(), list[1] as String);
        bytes += generator.row([
          PosColumn(
            textEncoded: firstCol,
            width: 6,
            styles: const PosStyles(align: PosAlign.left, underline: true),
          ),
          PosColumn(
            textEncoded: secondCol,
            width: 6,
            styles: const PosStyles(align: PosAlign.right, underline: true),
          ),
        ]);
      }
    }

    print('${row.text.runtimeType}, ${row.text}');
  }
  if (checkBean.qr != null) {
    bytes += generator.feed(2);
    const double qrSize = 200;
    try {
      final ByteData? uiImg = await QrPainter(
        data: checkBean.qr!,
        version: QrVersions.auto,
        gapless: true,
      ).toImageData(qrSize);
      final img = Im.decodePng(uiImg!.buffer.asUint8List());
      bytes += generator.image(img!);
    } catch (e) {
      print(e);
    }

  }

  bytes += generator.feed(2);
  bytes += generator.cut();
  return bytes;
}

Future<List<int>> getTestReceiptImg(String paperSize) async {
  final ByteData data =
      await rootBundle.load('assets/images/aman_kassa_check.png');
  final Uint8List imgBytes = data.buffer.asUint8List();
  return _getReceiptImg(paperSize, imgBytes);
}

Future<List<int>> getReceiptImg(String paperSize, Uint8List imgBytes) async {
  return _getReceiptImg(paperSize, imgBytes);
}

Future<List<int>> _getReceiptImg(String paperSize, Uint8List imgBytes) async {
  final profile = await CapabilityProfile.load();
  final generator = Generator(
    paperSize == PrinterConst.paperSize58mm ? PaperSize.mm58 : PaperSize.mm80,
    profile,
  );
  List<int> bytes = [];
  final Im.Image? image = Im.decodeImage(imgBytes);
// Using `ESC *`
  bytes += generator.image(image!);
  bytes += generator.feed(2);
  bytes += generator.cut();
  return bytes;
}

int getChunkSize() {
  final bool isIos = Platform.isIOS;
  if (isIos) {
    return 75;
  }
  return 1024;
}

int getQueueSleep() {
  final bool isIos = Platform.isIOS;
  if (isIos) {
    return 10;
  }
  return 100;
}

BluetoothDevice printerDeviceToBluetoothDevice(PrinterDevice device) {
  return BluetoothDevice()
    ..name = device.name
    ..address = device.address
    ..connected = device.connected
    ..type = device.type;
}

PrinterDevice bluetoothDeviceToPrinterDevice(PrinterBluetooth device) {
  return PrinterDevice()
    ..name = device.name
    ..address = device.address
    ..type = device.type;
}
