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

class ReplaceContent {
  DocxImage? img;
  String? value; /// Only Text to replace; null if it's image!

  ReplaceContent({this.value, this.img}) {
    if (img != null && value != null)
      throw StateError('Can\'t have value and image define');
    if (img == null && value == null)
      throw StateError('You need define value or image');
  }
}

/// 16cm * 360 000 = 5 760 000 EMU
const int maxWidth = 16 * 360000;
/// 24cm * 360 000 = 8 640 000 EMU
const int maxHeight = 24 * 360000;

class DocxImage {

  int? id;
  int? sizeX; /// in EMU
  int? sizeY; /// in EMU
  late File file;

  /// [file] support JPG/JPEG and PNG
  /// [sizeX] and [sizeY] use EMU metrics BUT init a instance in PIXEL
  DocxImage({required File file, this.sizeX, this.sizeY}) {
    this.file = file;

    if (sizeX == null && sizeY == null) {
      Uint8List bytes = file.readAsBytesSync();
      String extensions = file.path.substring(file.path.lastIndexOf('.') + 1);

      if (extensions == 'png') {
        sizeX = (bytes.buffer.asByteData().getUint32(16)) * 9525;
        sizeY = (bytes.buffer.asByteData().getUint32(20)) * 9525;
      }
      else if (extensions == 'jpg' || extensions == 'jpeg') {
        List<int> sizes = getSizeJpg(bytes);
        if (sizes.isEmpty)
          throw StateError('Can\'t get the size of Jpg image');
        sizeX = sizes[0];
        sizeY = sizes[1];
      }
    }
    else if (sizeX != null && sizeY != null) {
      sizeX = sizeX! * 9525;
      sizeY = sizeY! * 9525;
    }
    else
      throw StateError('Only sizeX or sizeY is defined!');

    // Limit size of image
    double scale = min(maxWidth / sizeX!, maxHeight / sizeY!);
    scale = scale > 1 ? 1 : scale;
    sizeX = (sizeX! * scale).round();
    sizeY = (sizeY! * scale).round();
  }
}

/// Return size of Jpg => [width, height]
List<int> getSizeJpg(Uint8List bytes) {
  int i = 0;
  while (i < bytes.length) {
    while(bytes[i]==0xff) 
      i++;
    int marker = bytes[i++];

    if(marker==0xd8) // SOI
      continue;
    if(marker==0xd9) // EOI
      break;
    if(0xd0<=marker && marker<=0xd7) 
      continue;
    if(marker==0x01) // TEM
      continue;    
      
    int length = (bytes[i] << 8) | bytes[i + 1];
    i += 2;
    if (marker == 0xc0)
      return [(bytes[i + 1] << 8) + bytes[i + 2],  // Width
              (bytes[i + 3] << 8) + bytes[i + 4]]; // Height

    i += length - 2;
  }
  return [];
}