ImagePreviewManager.js

48.20 KB
23/06/2025 09:30
JS
ImagePreviewManager.js
/**
 * ImagePreviewManager.js
 * จัดการการแสดงรูปภาพตัวอย่างและการเลือกพื้นที่ QR
 */

class ImagePreviewManager {
  constructor(debug = false) {
    this.debug = debug;
    this.currentImage = null;
    this.selectedBank = 'full';
    this.cropSelector = null;
    this.savedPositions = this.loadSavedPositions();
    this.imageElement = null;
    this.cropOverlay = null;
    this.cropSelection = null;

    // Mouse/Touch selection state
    this.isSelecting = false;
    this.isResizing = false;
    this.isDragging = false;
    this.startX = 0;
    this.startY = 0;
    this.offsetX = 0;
    this.offsetY = 0;

    // สำหรับ debounce การอัปเดตรูปที่ตัด
    this.cropUpdateTimeout = null;

    // ตำแหน่งเริ่มต้นสำหรับแต่ละธนาคาร (เป็นเปอร์เซ็นต์ของรูปภาพ)
    this.defaultBankPositions = {
      'SCB': {x: 0.15, y: 0.7, width: 0.7, height: 0.25},
      'KBANK': {x: 0.1, y: 0.65, width: 0.8, height: 0.3},
      'BBL': {x: 0.1, y: 0.6, width: 0.8, height: 0.35},
      'KTB': {x: 0.15, y: 0.65, width: 0.7, height: 0.3},
      'TTB': {x: 0.1, y: 0.6, width: 0.8, height: 0.35},
      'BAY': {x: 0.1, y: 0.65, width: 0.8, height: 0.3},
      'GSB': {x: 0.15, y: 0.7, width: 0.7, height: 0.25},
      'full': {x: 0, y: 0, width: 1, height: 1},
      'custom': {x: 0.2, y: 0.2, width: 0.6, height: 0.6}
    };

    this.init();
  }

  /**
   * เริ่มต้นระบบ
   */
  init() {
    this.bindEvents();
    if (this.debug) {
      console.log('🖼️ ImagePreviewManager initialized');
    }
  }

  /**
   * ผูก Event Listeners
   */
  bindEvents() {
    // Bank selection
    const bankRadios = document.querySelectorAll('input[name="preview-bank-type"]');
    bankRadios.forEach(radio => {
      radio.addEventListener('change', this.handleBankChange.bind(this));
    });    // Action buttons
    const processBtn = document.getElementById('process-slip-btn');
    const backBtn = document.getElementById('preview-back-btn');
    const changeImageBtn = document.getElementById('change-image-btn');

    if (processBtn) {
      processBtn.addEventListener('click', this.handleProcessSlip.bind(this));
    }

    if (backBtn) {
      backBtn.addEventListener('click', this.handleBack.bind(this));
    }

    if (changeImageBtn) {
      changeImageBtn.addEventListener('click', this.handleChangeImage.bind(this));
    }

    // Image elements
    this.imageElement = document.getElementById('preview-image');
    this.cropOverlay = document.getElementById('qr-crop-overlay');
    this.cropSelection = document.getElementById('crop-selection');

    if (this.imageElement) {
      this.imageElement.addEventListener('load', this.handleImageLoad.bind(this));
      this.imageElement.addEventListener('click', this.handleImageClick.bind(this));
    }
  }

  /**
   * แสดงรูปภาพตัวอย่าง
   */
  showPreview(imageFile, imageDataUrl) {
    this.currentImage = {
      file: imageFile,
      dataUrl: imageDataUrl
    };

    const imageElement = document.getElementById('preview-image');
    if (imageElement) {
      imageElement.src = imageDataUrl;
    }

    // แสดงส่วน preview โดยใช้ SlipVerifier showSection
    if (window.slipVerifier && typeof window.slipVerifier.showSection === 'function') {
      window.slipVerifier.showSection('image-preview');
    } else {
      this.showSection('image-preview');
    }

    // รีเซ็ตการเลือกธนาคาร
    this.resetBankSelection();

    if (this.debug) {
      console.log('🖼️ Showing image preview');
    }
  }

  /**
   * จัดการเมื่อรูปภาพโหลดเสร็จ
   */
  handleImageLoad() {
    if (this.debug) {
      console.log('🖼️ Image loaded, setting up crop area');
    }

    // ตั้งค่าพื้นที่ crop เริ่มต้น
    this.setupCropArea();
  }

  /**
   * ตั้งค่าพื้นที่ crop
   */
  setupCropArea() {
    if (!this.imageElement || !this.cropOverlay || !this.cropSelection) {
      return;
    }

    const position = this.getCurrentBankPosition();
    this.updateCropSelection(position);
    this.updateClickInstruction();
    this.updateImageClickMode();

    // ตั้งค่า interaction สำหรับ crop area
    this.setupCropInteraction();
  }

  /**
   * ได้รับตำแหน่งปัจจุบันตามธนาคารที่เลือก
   */
  getCurrentBankPosition() {
    // ตรวจสอบว่ามีตำแหน่งที่บันทึกไว้หรือไม่
    if (this.savedPositions[this.selectedBank]) {
      return this.savedPositions[this.selectedBank];
    }

    // ใช้ตำแหน่งเริ่มต้น
    return this.defaultBankPositions[this.selectedBank] || this.defaultBankPositions['full'];
  }

  /**
   * อัปเดตพื้นที่เลือก
   */
  updateCropSelection(position) {
    if (!this.imageElement || !this.cropSelection) {
      return;
    }

    const rect = this.imageElement.getBoundingClientRect();
    const imageWidth = rect.width;
    const imageHeight = rect.height;

    const left = position.x * imageWidth;
    const top = position.y * imageHeight;
    const width = position.width * imageWidth;
    const height = position.height * imageHeight;

    this.cropSelection.style.left = `${left}px`;
    this.cropSelection.style.top = `${top}px`;
    this.cropSelection.style.width = `${width}px`;
    this.cropSelection.style.height = `${height}px`;

    // แสดง/ซ่อน crop overlay
    if (this.selectedBank === 'full') {
      this.cropOverlay.style.display = 'none';
      this.showCropControls(false);
    } else {
      this.cropOverlay.style.display = 'block';
      this.showCropControls(true);
      // ตัดรูปและแสดงผลอัตโนมัติ
      setTimeout(() => this.cropAndShowPreview(), 100);
    }

    this.updateAreaDescription();
  }

  /**
   * ตั้งค่าการ interact กับ crop area
   */
  setupCropInteraction() {
    if (!this.cropSelection) {
      return;
    }

    // Remove existing event listeners first
    this.removeExistingListeners();

    // Setup image click for creating new selection
    this.setupImageClick();

    // Setup crop area interaction
    this.setupCropAreaInteraction();

    // Setup resize handles
    this.setupResizeHandles();

    // Debug: ตรวจสอบ setup
    if (this.debug) {
      console.log('🔧 Setting up crop interaction...');
      console.log('Crop selection element:', this.cropSelection);
      console.log('Image element:', this.imageElement);
    }
  }

  /**
   * ลบ event listeners เดิม
   */
  removeExistingListeners() {
    // Remove document listeners if they exist
    if (this.boundMouseMove) {
      document.removeEventListener('mousemove', this.boundMouseMove);
      document.removeEventListener('touchmove', this.boundTouchMove);
    }
    if (this.boundMouseUp) {
      document.removeEventListener('mouseup', this.boundMouseUp);
      document.removeEventListener('touchend', this.boundTouchEnd);
    }
  }

  /**
   * ตั้งค่าการคลิกบนรูปภาพเพื่อสร้าง crop area ใหม่
   */
  setupImageClick() {
    if (!this.imageElement) return;

    // Remove existing click listener
    this.imageElement.removeEventListener('mousedown', this.boundImageMouseDown);
    this.imageElement.removeEventListener('touchstart', this.boundImageTouchStart);

    // Create new bound functions
    this.boundImageMouseDown = this.handleImageMouseDown.bind(this);
    this.boundImageTouchStart = this.handleImageTouchStart.bind(this);

    // Add new listeners
    this.imageElement.addEventListener('mousedown', this.boundImageMouseDown);
    this.imageElement.addEventListener('touchstart', this.boundImageTouchStart, {passive: false});
  }

  /**
   * ตั้งค่าการโต้ตอบกับ crop area (ลากเพื่อเลื่อน)
   */
  setupCropAreaInteraction() {
    if (!this.cropSelection) return;

    // Remove existing listeners
    this.cropSelection.removeEventListener('mousedown', this.boundCropMouseDown);
    this.cropSelection.removeEventListener('touchstart', this.boundCropTouchStart);

    // Create new bound functions
    this.boundCropMouseDown = this.handleCropMouseDown.bind(this);
    this.boundCropTouchStart = this.handleCropTouchStart.bind(this);

    // Add new listeners
    this.cropSelection.addEventListener('mousedown', this.boundCropMouseDown);
    this.cropSelection.addEventListener('touchstart', this.boundCropTouchStart, {passive: false});

    // Document-level move and up events
    this.boundMouseMove = this.handleMouseMove.bind(this);
    this.boundMouseUp = this.handleMouseUp.bind(this);
    this.boundTouchMove = this.handleTouchMove.bind(this);
    this.boundTouchEnd = this.handleTouchEnd.bind(this);

    document.addEventListener('mousemove', this.boundMouseMove);
    document.addEventListener('mouseup', this.boundMouseUp);
    document.addEventListener('touchmove', this.boundTouchMove, {passive: false});
    document.addEventListener('touchend', this.boundTouchEnd);
  }

  /**
   * ตั้งค่า resize handles
   */
  setupResizeHandles() {
    const handles = this.cropSelection.querySelectorAll('.crop-handle');
    handles.forEach(handle => {
      // Remove existing listeners
      handle.removeEventListener('mousedown', this.boundHandleMouseDown);
      handle.removeEventListener('touchstart', this.boundHandleTouchStart);

      // Create bound functions if not exist
      if (!this.boundHandleMouseDown) {
        this.boundHandleMouseDown = this.handleResizeMouseDown.bind(this);
        this.boundHandleTouchStart = this.handleResizeTouchStart.bind(this);
      }

      // Add new listeners
      handle.addEventListener('mousedown', this.boundHandleMouseDown);
      handle.addEventListener('touchstart', this.boundHandleTouchStart, {passive: false});
    });
  }  /**
   * จัดการการเปลี่ยนธนาคาร
   */
  handleBankChange(event) {
    this.selectedBank = event.target.value;

    if (this.debug) {
      console.log('🏦 Bank changed to:', this.selectedBank);
    }

    // แสดงพื้นที่เริ่มต้นของธนาคารที่เลือก (ยกเว้น "ทั้งรูป")
    if (this.selectedBank !== 'full') {
      const position = this.getCurrentBankPosition();
      this.updateCropSelection(position);
    } else {
      // ซ่อน crop overlay สำหรับ "ทั้งรูป"
      if (this.cropOverlay) {
        this.cropOverlay.style.display = 'none';
      }
    }

    // แสดง/ซ่อนคำแนะนำการคลิก
    this.updateClickInstruction();
    this.updateImageClickMode();
  }

  /**
   * อัปเดตคำแนะนำการคลิก
   */
  updateClickInstruction() {
    const instructionElement = document.getElementById('click-instruction');
    if (!instructionElement) return;

    if (this.selectedBank === 'full') {
      instructionElement.style.display = 'none';
    } else {
      instructionElement.style.display = 'block';
    }
  }

  /**
   * อัปเดต cursor mode สำหรับการคลิก
   */
  updateImageClickMode() {
    const wrapper = document.querySelector('.preview-image-wrapper');
    if (!wrapper) return;

    if (this.selectedBank === 'full') {
      wrapper.classList.remove('click-mode');
    } else {
      wrapper.classList.add('click-mode');
    }
  }

  /**
   * อัปเดตคำอธิบายพื้นที่
   */
  updateAreaDescription() {
    const descElement = document.getElementById('qr-area-description');
    if (!descElement) return;

    if (this.selectedBank === 'full') {
      descElement.textContent = 'ทั้งรูป';
    } else if (this.selectedBank === 'custom') {
      descElement.textContent = 'กำหนดเอง';
    } else {
      const bankNames = {
        'SCB': 'ไทยพาณิชย์',
        'KBANK': 'กสิกรไทย',
        'BBL': 'กรุงเทพ',
        'KTB': 'กรุงไทย',
        'TTB': 'ทหารไทยธนชาต',
        'BAY': 'กรุงศรีอยุธยา',
        'GSB': 'ออมสิน'
      };
      descElement.textContent = `${bankNames[this.selectedBank]} (QR Area)`;
    }
  }

  /**
   * แสดง/ซ่อนการควบคุม crop
   */
  showCropControls(show) {
    // ไม่ต้องแสดงปุ่มควบคุมแล้ว เพราะใช้ auto-save
    return;
  }

  /**
   * จัดการการคลิกบนรูปภาพ (สร้าง crop area ใหม่ด้วยการลาก)
   */
  handleImageMouseDown(event) {
    // ตรวจสอบว่าไม่ใช่การคลิกบน crop area หรือ handle
    if (event.target.closest('.crop-selection') || event.target.closest('.crop-handle')) {
      return;
    }

    // เฉพาะเมื่อไม่ใช่ "ทั้งรูป"
    if (this.selectedBank === 'full') {
      return;
    }

    this.isSelecting = true;
    const rect = this.imageElement.getBoundingClientRect();
    this.startX = event.clientX - rect.left;
    this.startY = event.clientY - rect.top;

    // ซ่อน crop area ปัจจุบันชั่วคราว
    this.cropSelection.style.display = 'none';

    event.preventDefault();
  }

  /**
   * จัดการการลาก crop area (เลื่อนตำแหน่ง)
   */
  handleCropMouseDown(event) {
    // ตรวจสอบว่าไม่ใช่ handle
    if (event.target.classList.contains('crop-handle')) {
      return;
    }

    this.isDragging = true;
    const cropRect = this.cropSelection.getBoundingClientRect();
    const imageRect = this.imageElement.getBoundingClientRect();

    this.offsetX = event.clientX - cropRect.left;
    this.offsetY = event.clientY - cropRect.top;

    // เพิ่ม visual feedback
    this.cropSelection.style.opacity = '0.8';

    event.preventDefault();
    event.stopPropagation();
  }

  /**
   * จัดการการปรับขนาด
   */
  handleResizeMouseDown(event) {
    this.isResizing = true;
    this.resizeHandle = event.target.classList[1]; // เช่น 'bottom-right'

    // เพิ่ม visual feedback
    this.cropSelection.style.opacity = '0.8';

    event.stopPropagation();
    event.preventDefault();
  }

  /**
   * จัดการการเคลื่อนไหวของเมาส์ (สำหรับทุกโหมด)
   */
  handleMouseMove(event) {
    if (this.isSelecting) {
      this.updateSelection(event.clientX, event.clientY);
    } else if (this.isDragging) {
      this.updateCropPosition(event.clientX, event.clientY);
    } else if (this.isResizing) {
      this.updateCropSize(event.clientX, event.clientY);
    }
  }

  /**
   * จัดการการปล่อยเมาส์
   */
  handleMouseUp(event) {
    if (this.isSelecting) {
      this.finishSelection();
    } else if (this.isDragging || this.isResizing) {
      this.finishDragging();
    }

    // รีเซ็ต state
    this.isSelecting = false;
    this.isDragging = false;
    this.isResizing = false;
    this.resizeHandle = null;

    // คืน visual feedback
    if (this.cropSelection) {
      this.cropSelection.style.opacity = '1';
    }
  }

  /**
   * อัปเดตการเลือกพื้นที่ (ขณะลาก)
   */
  updateSelection(clientX, clientY) {
    if (!this.imageElement || !this.cropSelection) return;

    const rect = this.imageElement.getBoundingClientRect();
    const currentX = clientX - rect.left;
    const currentY = clientY - rect.top;

    // คำนวณขนาดและตำแหน่ง
    const left = Math.min(this.startX, currentX);
    const top = Math.min(this.startY, currentY);
    const width = Math.abs(currentX - this.startX);
    const height = Math.abs(currentY - this.startY);

    // ขนาดขั้นต่ำ
    const minSize = 30;
    if (width < minSize || height < minSize) return;

    // จำกัดไม่ให้เกินขอบรูปภาพ
    const maxWidth = rect.width - left;
    const maxHeight = rect.height - top;
    const finalWidth = Math.min(width, maxWidth);
    const finalHeight = Math.min(height, maxHeight);

    // แสดงและอัปเดต crop area
    this.cropSelection.style.display = 'block';
    this.cropSelection.style.left = `${left}px`;
    this.cropSelection.style.top = `${top}px`;
    this.cropSelection.style.width = `${finalWidth}px`;
    this.cropSelection.style.height = `${finalHeight}px`;
  }

  /**
   * เสร็จสิ้นการเลือกพื้นที่
   */
  finishSelection() {
    if (!this.cropSelection) return;

    // ตรวจสอบว่ามีขนาดพอสมควร
    const width = this.cropSelection.offsetWidth;
    const height = this.cropSelection.offsetHeight;

    if (width < 30 || height < 30) {
      // คืนสู่ตำแหน่งเดิม
      const position = this.getCurrentBankPosition();
      this.updateCropSelection(position);
      return;
    }

    // บันทึกตำแหน่งและอัปเดตรูปตัด
    this.saveCropPosition();
    this.cropAndShowPreview();
  }

  /**
   * เสร็จสิ้นการลาก/ปรับขนาด
   */
  finishDragging() {
    // บันทึกตำแหน่งและอัปเดตรูปตัด
    this.saveCropPosition();
    this.cropAndShowPreview();
  }

  /**
   * อัปเดตตำแหน่ง crop (ขณะลาก)
   */
  updateCropPosition(clientX, clientY) {
    if (!this.imageElement || !this.cropSelection) return;

    const imageRect = this.imageElement.getBoundingClientRect();

    // คำนวณตำแหน่งใหม่
    const newLeft = clientX - this.offsetX - imageRect.left;
    const newTop = clientY - this.offsetY - imageRect.top;

    // จำกัดไม่ให้เกินขอบรูปภาพ
    const cropWidth = this.cropSelection.offsetWidth;
    const cropHeight = this.cropSelection.offsetHeight;

    const maxLeft = imageRect.width - cropWidth;
    const maxTop = imageRect.height - cropHeight;

    const constrainedLeft = Math.max(0, Math.min(newLeft, maxLeft));
    const constrainedTop = Math.max(0, Math.min(newTop, maxTop));

    this.cropSelection.style.left = `${constrainedLeft}px`;
    this.cropSelection.style.top = `${constrainedTop}px`;

    // อัปเดตรูปที่ตัดแล้วแบบ real-time (debounced)
    this.debouncedCropUpdate();
  }

  /**
   * อัปเดตขนาด crop (ขณะปรับขนาด)
   */
  updateCropSize(clientX, clientY) {
    if (!this.imageElement || !this.cropSelection || !this.resizeHandle) return;

    const imageRect = this.imageElement.getBoundingClientRect();

    // คำนวณตำแหน่งเมาส์ relative กับรูปภาพ
    const mouseX = clientX - imageRect.left;
    const mouseY = clientY - imageRect.top;

    const currentLeft = this.cropSelection.offsetLeft;
    const currentTop = this.cropSelection.offsetTop;
    const currentWidth = this.cropSelection.offsetWidth;
    const currentHeight = this.cropSelection.offsetHeight;

    let newLeft = currentLeft;
    let newTop = currentTop;
    let newWidth = currentWidth;
    let newHeight = currentHeight;

    // คำนวณขนาดใหม่ตาม handle ที่ถูกลาก
    switch (this.resizeHandle) {
      case 'top-left':
        newLeft = mouseX;
        newTop = mouseY;
        newWidth = currentLeft + currentWidth - mouseX;
        newHeight = currentTop + currentHeight - mouseY;
        break;
      case 'top-right':
        newTop = mouseY;
        newWidth = mouseX - currentLeft;
        newHeight = currentTop + currentHeight - mouseY;
        break;
      case 'bottom-left':
        newLeft = mouseX;
        newWidth = currentLeft + currentWidth - mouseX;
        newHeight = mouseY - currentTop;
        break;
      case 'bottom-right':
        newWidth = mouseX - currentLeft;
        newHeight = mouseY - currentTop;
        break;
      case 'top-center':
        newTop = mouseY;
        newHeight = currentTop + currentHeight - mouseY;
        break;
      case 'bottom-center':
        newHeight = mouseY - currentTop;
        break;
      case 'left-center':
        newLeft = mouseX;
        newWidth = currentLeft + currentWidth - mouseX;
        break;
      case 'right-center':
        newWidth = mouseX - currentLeft;
        break;
    }

    // ตรวจสอบขนาดขั้นต่ำ
    const minSize = 30;
    if (newWidth < minSize || newHeight < minSize) return;

    // ตรวจสอบไม่ให้เกินขอบรูปภาพ
    if (newLeft < 0 || newTop < 0 ||
      newLeft + newWidth > imageRect.width ||
      newTop + newHeight > imageRect.height) return;

    // อัปเดตตำแหน่งและขนาด
    this.cropSelection.style.left = `${newLeft}px`;
    this.cropSelection.style.top = `${newTop}px`;
    this.cropSelection.style.width = `${newWidth}px`;
    this.cropSelection.style.height = `${newHeight}px`;

    // อัปเดตรูปที่ตัดแล้วแบบ real-time (debounced)
    this.debouncedCropUpdate();
  }

  /**
   * Debounced crop update เพื่อไม่ให้อัปเดตบ่อยเกินไป
   */
  debouncedCropUpdate() {
    if (this.cropUpdateTimeout) {
      clearTimeout(this.cropUpdateTimeout);
    }

    this.cropUpdateTimeout = setTimeout(() => {
      this.cropAndShowPreview();
    }, 200); // รอ 200ms หลังจากหยุดลากแล้วจึงอัปเดต
  }

  /**
   * รีเซ็ตการเลือกธนาคาร
   */
  resetBankSelection() {
    const defaultRadio = document.querySelector('input[name="preview-bank-type"][value="full"]');
    if (defaultRadio) {
      defaultRadio.checked = true;
      this.selectedBank = 'full';
      this.updateCropSelection(this.defaultBankPositions['full']);
    }
  }

  /**
   * รีเซ็ต crop area
   */
  handleResetCrop() {
    const position = this.defaultBankPositions[this.selectedBank] || this.defaultBankPositions['custom'];
    this.updateCropSelection(position);

    if (this.debug) {
      console.log('🔄 Crop area reset');
    }
  }

  /**
   * บันทึกตำแหน่ง crop
   */
  handleSaveCropPosition() {
    if (this.selectedBank === 'full') {
      this.showNotification('ไม่สามารถบันทึกตำแหน่งสำหรับ "ทั้งรูป" ได้', 'warning');
      return;
    }

    const position = this.getCurrentCropPosition();
    this.savedPositions[this.selectedBank] = position;
    this.savePosistionsToStorage();

    this.showNotification('บันทึกตำแหน่งเรียบร้อยแล้ว', 'success');

    if (this.debug) {
      console.log('💾 Saved position for', this.selectedBank, position);
    }
  }

  /**
   * ได้รับตำแหน่ง crop ปัจจุบัน
   */
  getCurrentCropPosition() {
    if (!this.imageElement || !this.cropSelection) {
      return this.defaultBankPositions[this.selectedBank];
    }

    // ใช้ getBoundingClientRect เพื่อให้ได้ตำแหน่งที่แม่นยำ
    const imageRect = this.imageElement.getBoundingClientRect();
    const cropRect = this.cropSelection.getBoundingClientRect();

    // คำนวณตำแหน่ง relative กับรูปภาพ
    const left = cropRect.left - imageRect.left;
    const top = cropRect.top - imageRect.top;
    const width = cropRect.width;
    const height = cropRect.height;

    // แปลงเป็นเปอร์เซ็นต์ของรูปภาพ
    return {
      x: left / imageRect.width,
      y: top / imageRect.height,
      width: width / imageRect.width,
      height: height / imageRect.height
    };
  }

  /**
   * ได้รับพื้นที่ crop ในรูปแบบพิกเซล
   */
  getCropAreaPixels() {
    if (this.selectedBank === 'full') {
      return null; // ใช้รูปเต็ม
    }

    const position = this.getCurrentCropPosition();

    // สร้าง canvas เพื่อวัดขนาดรูปจริง
    const img = new Image();
    img.src = this.currentImage.dataUrl;

    return new Promise((resolve) => {
      img.onload = () => {
        const cropArea = {
          x: Math.round(position.x * img.naturalWidth),
          y: Math.round(position.y * img.naturalHeight),
          width: Math.round(position.width * img.naturalWidth),
          height: Math.round(position.height * img.naturalHeight)
        };
        resolve(cropArea);
      };
    });
  }  /**
   * จัดการการประมวลผลสลิป
   */
  async handleProcessSlip() {
    if (!this.currentImage) {
      this.showNotification('ไม่มีรูปภาพให้ประมวลผล', 'error');
      return;
    }

    console.log('🔍 Processing slip...');

    // บันทึกตำแหน่งอัตโนมัติก่อนประมวลผล
    this.saveCropPosition();

    try {
      const cropArea = await this.getCropAreaPixels();

      // ตรวจสอบ SlipVerifier instance
      const verifier = window.slipVerifier || window.app?.slipVerifier;

      if (!verifier) {
        console.error('SlipVerifier not found');
        this.showNotification('ไม่พบระบบประมวลผล', 'error');
        return;
      }

      console.log('📤 Sending to SlipVerifier:', {
        file: this.currentImage.file?.name,
        bank: this.selectedBank,
        cropArea: cropArea
      });

      // ส่งข้อมูลไปยัง SlipVerifier
      await verifier.processImageWithCrop(
        this.currentImage.file,
        this.currentImage.dataUrl,
        cropArea,
        this.selectedBank
      );

    } catch (error) {
      console.error('Error processing slip:', error);
      this.showNotification('เกิดข้อผิดพลาดในการประมวลผล: ' + error.message, 'error');
    }
  }

  /**
   * จัดการการกลับ
   */
  handleBack() {
    // กลับไปหน้าอัปโหลด โดยใช้ SlipVerifier showSection
    if (window.slipVerifier && typeof window.slipVerifier.showSection === 'function') {
      window.slipVerifier.showSection('upload');
    } else {
      this.showSection('upload');
    }
  }

  /**
   * จัดการการเปลี่ยนรูป
   */
  handleChangeImage() {
    const fileInput = document.getElementById('file-input');
    if (fileInput) {
      fileInput.click();
    }
  }

  /**
   * แสดงส่วนต่างๆ
   */
  showSection(sectionName) {
    const sections = ['upload', 'camera', 'image-preview', 'qr', 'processing', 'results', 'error'];
    sections.forEach(section => {
      const element = document.getElementById(`${section}-section`);
      if (element) {
        element.style.display = section === sectionName ? 'block' : 'none';
      }
    });
  }

  /**
   * โหลดตำแหน่งที่บันทึกไว้
   */
  loadSavedPositions() {
    try {
      const saved = localStorage.getItem('slip-verifier-crop-positions');
      return saved ? JSON.parse(saved) : {};
    } catch (error) {
      console.warn('Failed to load saved positions:', error);
      return {};
    }
  }

  /**
   * บันทึกตำแหน่งลง localStorage
   */
  savePosistionsToStorage() {
    try {
      localStorage.setItem('slip-verifier-crop-positions', JSON.stringify(this.savedPositions));
    } catch (error) {
      console.warn('Failed to save positions:', error);
    }
  }

  /**
   * แสดงการแจ้งเตือน
   */
  showNotification(message, type = 'info') {
    if (window.slipVerifier && window.slipVerifier.showNotification) {
      window.slipVerifier.showNotification(message, type);
    } else {
      console.log(`${type.toUpperCase()}: ${message}`);
    }
  }

  /**
   * จัดการการคลิกที่รูปภาพ (เริ่มต้นระบบ click-and-drag)
   */
  handleImageClick(event) {
    if (this.selectedBank === 'full') {
      return; // ไม่ต้องเลือกพื้นที่สำหรับ "ทั้งรูป"
    }

    // เริ่มระบบ click-and-drag
    this.startRectangleSelection(event);
  }

  /**
   * เริ่มการเลือกพื้นที่แบบสี่เหลี่ยม (click-and-drag)
   */
  startRectangleSelection(event) {
    if (!this.imageElement) return;

    event.preventDefault();
    event.stopPropagation();

    const imageRect = this.imageElement.getBoundingClientRect();
    const startX = event.clientX - imageRect.left;
    const startY = event.clientY - imageRect.top;

    // ตรวจสอบว่าคลิกภายในรูปภาพหรือไม่
    if (startX < 0 || startY < 0 || startX > imageRect.width || startY > imageRect.height) {
      return;
    }

    // บันทึกจุดเริ่มต้น
    this.selectionStart = {
      x: startX,
      y: startY
    };
    this.isCreatingSelection = true;

    // แสดง crop overlay
    if (this.cropOverlay) {
      this.cropOverlay.style.display = 'block';
    }

    // ซ่อน crop selection ชั่วคราวจนกว่าจะมีการลาก
    if (this.cropSelection) {
      this.cropSelection.style.display = 'none';
    }

    // เปลี่ยน cursor
    document.body.style.cursor = 'crosshair';

    // เพิ่ม event listeners
    document.addEventListener('mousemove', this.handleRectangleMove);
    document.addEventListener('mouseup', this.handleRectangleEnd);

    if (this.debug) {
      console.log('🎯 Start selection at:', {x: startX, y: startY});
    }
  }

  /**
   * จัดการการเคลื่อนไหวเมาส์ขณะลาก
   */
  handleRectangleMove = (event) => {
    if (!this.isCreatingSelection || !this.imageElement || !this.cropSelection) return;

    const imageRect = this.imageElement.getBoundingClientRect();
    const currentX = event.clientX - imageRect.left;
    const currentY = event.clientY - imageRect.top;

    // จำกัดไม่ให้เกินขอบรูป
    const constrainedX = Math.max(0, Math.min(currentX, imageRect.width));
    const constrainedY = Math.max(0, Math.min(currentY, imageRect.height));

    // คำนวณกรอบสี่เหลี่ยม
    const left = Math.min(this.selectionStart.x, constrainedX);
    const top = Math.min(this.selectionStart.y, constrainedY);
    const width = Math.abs(constrainedX - this.selectionStart.x);
    const height = Math.abs(constrainedY - this.selectionStart.y);

    // ถ้าขนาดมากกว่า 5px ค่อยแสดงกรอบ
    if (width > 5 && height > 5) {
      this.cropSelection.style.display = 'block';
      this.cropSelection.style.left = `${left}px`;
      this.cropSelection.style.top = `${top}px`;
      this.cropSelection.style.width = `${width}px`;
      this.cropSelection.style.height = `${height}px`;
    }

    if (this.debug) {
      console.log('📏 Rectangle:', {left, top, width, height});
    }
  }

  /**
   * จัดการการปล่อยเมาส์ (สิ้นสุดการเลือก)
   */
  handleRectangleEnd = (event) => {
    if (!this.isCreatingSelection) return;

    this.isCreatingSelection = false;

    // ลบ event listeners
    document.removeEventListener('mousemove', this.handleRectangleMove);
    document.removeEventListener('mouseup', this.handleRectangleEnd);

    // คืนค่า cursor
    document.body.style.cursor = '';

    if (!this.imageElement || !this.cropSelection) return;

    const imageRect = this.imageElement.getBoundingClientRect();
    const currentX = event.clientX - imageRect.left;
    const currentY = event.clientY - imageRect.top;

    const constrainedX = Math.max(0, Math.min(currentX, imageRect.width));
    const constrainedY = Math.max(0, Math.min(currentY, imageRect.height));

    const left = Math.min(this.selectionStart.x, constrainedX);
    const top = Math.min(this.selectionStart.y, constrainedY);
    const width = Math.abs(constrainedX - this.selectionStart.x);
    const height = Math.abs(constrainedY - this.selectionStart.y);

    // ตรวจสอบขนาดขั้นต่ำ
    if (width < 10 || height < 10) {
      // ถ้าเล็กเกินไป ให้สร้างกรอบเล็กๆ รอบจุดที่คลิก
      this.createMinimumSizeRectangle(this.selectionStart.x, this.selectionStart.y);
      return;
    }

    // แสดงกรอบขั้นสุดท้าย
    this.cropSelection.style.display = 'block';
    this.cropSelection.style.left = `${left}px`;
    this.cropSelection.style.top = `${top}px`;
    this.cropSelection.style.width = `${width}px`;
    this.cropSelection.style.height = `${height}px`;

    // เพิ่ม animation
    this.cropSelection.classList.add('selection-created');
    setTimeout(() => {
      this.cropSelection.classList.remove('selection-created');
    }, 300);

    // ตั้งค่า interaction handles
    this.setupCropInteraction();

    // บันทึกและตัดรูป
    this.saveCropPosition();
    this.cropAndShowPreview();

    if (this.debug) {
      console.log('✅ Rectangle completed:', {left, top, width, height});
    }
  }

  /**
   * สร้างกรอบขนาดขั้นต่ำเมื่อคลิกเดียว
   */
  createMinimumSizeRectangle(centerX, centerY) {
    if (!this.imageElement || !this.cropSelection) return;

    const imageRect = this.imageElement.getBoundingClientRect();
    const size = 80; // ขนาดขั้นต่ำ
    const halfSize = size / 2;

    let left = centerX - halfSize;
    let top = centerY - halfSize;
    let width = size;
    let height = size;

    // ปรับให้อยู่ในขอบเขตรูป
    if (left < 0) left = 0;
    if (top < 0) top = 0;
    if (left + width > imageRect.width) left = imageRect.width - width;
    if (top + height > imageRect.height) top = imageRect.height - height;
    if (width > imageRect.width) width = imageRect.width;
    if (height > imageRect.height) height = imageRect.height;

    // แสดงกรอบ
    this.cropSelection.style.display = 'block';
    this.cropSelection.style.left = `${left}px`;
    this.cropSelection.style.top = `${top}px`;
    this.cropSelection.style.width = `${width}px`;
    this.cropSelection.style.height = `${height}px`;

    // เพิ่ม animation
    this.cropSelection.classList.add('click-created');
    setTimeout(() => {
      this.cropSelection.classList.remove('click-created');
    }, 300);

    // ตั้งค่า interaction handles
    this.setupCropInteraction();

    // บันทึกและตัดรูป
    this.saveCropPosition();
    this.cropAndShowPreview();

    if (this.debug) {
      console.log('🎯 Created minimum rectangle at:', {left, top, width, height});
    }
  }

  /**
   * เริ่มระบบ click-and-drag selection
   */
  startClickAndDragSelection(event) {
    if (!this.imageElement) return;

    event.preventDefault();
    event.stopPropagation();

    const imageRect = this.imageElement.getBoundingClientRect();
    const startX = event.clientX - imageRect.left;
    const startY = event.clientY - imageRect.top;

    // ตรวจสอบว่าคลิกภายในรูปภาพหรือไม่
    if (startX < 0 || startY < 0 || startX > imageRect.width || startY > imageRect.height) {
      return;
    }

    // เก็บตำแหน่งเริ่มต้น
    this.dragStartX = startX;
    this.dragStartY = startY;
    this.isCreatingSelection = true;

    // สร้าง crop selection ใหม่ (เริ่มต้นขนาดเล็ก)
    if (this.cropSelection) {
      this.cropSelection.style.left = `${startX}px`;
      this.cropSelection.style.top = `${startY}px`;
      this.cropSelection.style.width = '2px';
      this.cropSelection.style.height = '2px';
      this.cropSelection.style.display = 'block';
    }

    // แสดง crop overlay
    if (this.cropOverlay) {
      this.cropOverlay.style.display = 'block';
    }

    // เพิ่ม cursor แบบ crosshair
    this.imageElement.style.cursor = 'crosshair';

    // เพิ่ม event listeners สำหรับการลาก
    const handleMouseMove = (e) => this.updateClickAndDragSelection(e);
    const handleMouseUp = (e) => this.finishClickAndDragSelection(e, handleMouseMove, handleMouseUp);

    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);

    if (this.debug) {
      console.log('🎯 Started click-and-drag at:', {x: startX, y: startY});
    }
  }

  /**
   * อัปเดตการเลือกพื้นที่ขณะลาก
   */
  updateClickAndDragSelection(event) {
    if (!this.isCreatingSelection || !this.imageElement || !this.cropSelection) return;

    const imageRect = this.imageElement.getBoundingClientRect();
    const currentX = event.clientX - imageRect.left;
    const currentY = event.clientY - imageRect.top;

    // จำกัดไม่ให้เกินขอบรูป
    const constrainedX = Math.max(0, Math.min(currentX, imageRect.width));
    const constrainedY = Math.max(0, Math.min(currentY, imageRect.height));

    // คำนวณขนาดและตำแหน่งของกรอบ
    const left = Math.min(this.dragStartX, constrainedX);
    const top = Math.min(this.dragStartY, constrainedY);
    const width = Math.abs(constrainedX - this.dragStartX);
    const height = Math.abs(constrainedY - this.dragStartY);

    // อัปเดตตำแหน่งและขนาด
    this.cropSelection.style.left = `${left}px`;
    this.cropSelection.style.top = `${top}px`;
    this.cropSelection.style.width = `${width}px`;
    this.cropSelection.style.height = `${height}px`;

    // เพิ่ม class สำหรับ animation ขณะสร้าง
    if (width > 5 && height > 5) {
      this.cropSelection.classList.add('creating');
    }

    // อัปเดตข้อมูลขนาดแบบเรียลไทม์
    this.updateSelectionInfo(width, height);

    // แสดงข้อมูลขนาดในเวลาจริง (ถ้าต้องการ)
    if (this.debug && width > 5 && height > 5) {
      console.log('📏 Selection size:', {width, height});
    }
  }

  /**
   * อัปเดตข้อมูลขนาดการเลือก
   */
  updateSelectionInfo(width, height) {
    // หาบริเวณแสดงข้อมูลขนาด
    let sizeInfo = document.getElementById('selection-size-info');
    if (!sizeInfo) {
      sizeInfo = document.createElement('div');
      sizeInfo.id = 'selection-size-info';
      sizeInfo.className = 'selection-size-info';

      const wrapper = document.querySelector('.preview-image-wrapper');
      if (wrapper) {
        wrapper.appendChild(sizeInfo);
      }
    }

    if (width > 5 && height > 5) {
      sizeInfo.textContent = `${Math.round(width)} × ${Math.round(height)} px`;
      sizeInfo.style.display = 'block';
    } else {
      sizeInfo.style.display = 'none';
    }
  }

  /**
   * ซ่อนข้อมูลขนาดการเลือก
   */
  hideSelectionInfo() {
    const sizeInfo = document.getElementById('selection-size-info');
    if (sizeInfo) {
      sizeInfo.style.display = 'none';
    }
  }

  /**
   * เสร็จสิ้นการเลือกพื้นที่ click-and-drag
   */
  finishClickAndDragSelection(event, mouseMoveHandler, mouseUpHandler) {
    this.isCreatingSelection = false;

    // ลบ event listeners
    document.removeEventListener('mousemove', mouseMoveHandler);
    document.removeEventListener('mouseup', mouseUpHandler);

    // คืนค่า cursor
    this.imageElement.style.cursor = '';

    // ซ่อนข้อมูลขนาด
    this.hideSelectionInfo();

    if (!this.imageElement || !this.cropSelection) return;

    // ลบ class creating
    this.cropSelection.classList.remove('creating');

    const finalWidth = this.cropSelection.offsetWidth;
    const finalHeight = this.cropSelection.offsetHeight;

    // ตรวจสอบขนาดขั้นต่ำ
    const minSize = 10;
    if (finalWidth < minSize || finalHeight < minSize) {
      // ถ้าเล็กเกินไป ให้สร้างกรอบขนาดมาตรฐานรอบจุดที่คลิก
      this.createStandardCropArea(event);
      return;
    }

    // เพิ่ม animation class
    this.cropSelection.classList.add('selection-created');
    setTimeout(() => {
      this.cropSelection.classList.remove('selection-created');
    }, 300);

    // ตั้งค่า interaction สำหรับ crop area
    this.setupCropInteraction();

    // บันทึกตำแหน่งอัตโนมัติและตัดรูป
    this.saveCropPosition();
    this.cropAndShowPreview();

    if (this.debug) {
      const position = this.getCurrentCropPosition();
      console.log('🎯 Finished click-and-drag selection:', position);
    }
  }

  /**
   * สร้างพื้นที่ crop ขนาดมาตรฐานรอบจุดที่คลิก
   */
  createStandardCropArea(event) {
    if (!this.imageElement) return;

    const imageRect = this.imageElement.getBoundingClientRect();
    const clickX = event.clientX - imageRect.left;
    const clickY = event.clientY - imageRect.top;

    // สร้างพื้นที่ crop รอบๆ จุดที่คลิก
    const cropSize = 120; // ขนาดพื้นที่ crop มาตรฐาน
    const halfSize = cropSize / 2;

    let left = clickX - halfSize;
    let top = clickY - halfSize;
    let width = cropSize;
    let height = cropSize;

    // ปรับให้อยู่ในขอบเขตรูปภาพ
    if (left < 0) left = 0;
    if (top < 0) top = 0;
    if (left + width > imageRect.width) left = imageRect.width - width;
    if (top + height > imageRect.height) top = imageRect.height - height;
    if (width > imageRect.width) width = imageRect.width;
    if (height > imageRect.height) height = imageRect.height;

    // แปลงเป็นเปอร์เซ็นต์
    const position = {
      x: left / imageRect.width,
      y: top / imageRect.height,
      width: width / imageRect.width,
      height: height / imageRect.height
    };

    this.updateCropSelection(position);

    // แสดง crop overlay
    if (this.cropOverlay) {
      this.cropOverlay.style.display = 'block';
    }

    // เพิ่ม animation class
    if (this.cropSelection) {
      this.cropSelection.classList.add('click-created');
      setTimeout(() => {
        this.cropSelection.classList.remove('click-created');
      }, 300);
    }

    // ตั้งค่า interaction สำหรับ crop area
    this.setupCropInteraction();

    // บันทึกตำแหน่งอัตโนมัติและตัดรูป
    this.saveCropPosition();
    this.cropAndShowPreview();

    if (this.debug) {
      console.log('🎯 Created standard crop area at:', position);
    }
  }

  /**
   * บันทึกตำแหน่ง crop อัตโนมัติ
   */
  saveCropPosition() {
    if (this.selectedBank === 'full') {
      return;
    }

    const position = this.getCurrentCropPosition();
    this.savedPositions[this.selectedBank] = position;
    this.savePosistionsToStorage();

    if (this.debug) {
      console.log('💾 Auto-saved position for', this.selectedBank, position);
    }
  }

  /**
   * ตัดรูปตาม crop selection และแสดงผล
   */
  async cropAndShowPreview() {
    if (!this.currentImage || !this.currentImage.dataUrl) {
      this.showNotification('ไม่มีรูปภาพให้ตัด', 'error');
      return null;
    }

    try {
      const cropArea = await this.getCropAreaPixels();

      if (this.debug) {
        console.log('🔍 Crop area pixels:', cropArea);
        console.log('🔍 Current crop position:', this.getCurrentCropPosition());
      }

      if (!cropArea) {
        // ถ้าเป็น "ทั้งรูป" ให้ใช้รูปต้นฉบับ
        return this.currentImage.dataUrl;
      }

      const croppedImageUrl = await this.cropImageWithCanvas(
        this.currentImage.dataUrl,
        cropArea
      );

      return croppedImageUrl;

    } catch (error) {
      console.error('Error cropping image:', error);
      this.showNotification('เกิดข้อผิดพลาดในการตัดรูป: ' + error.message, 'error');
      return null;
    }
  }

  /**
   * ตัดรูปด้วย Canvas
   */
  async cropImageWithCanvas(imageDataUrl, cropArea) {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => {
        try {
          const canvas = document.createElement('canvas');
          const ctx = canvas.getContext('2d');

          // ตั้งขนาด canvas ตามพื้นที่ที่ต้องการตัด
          canvas.width = cropArea.width;
          canvas.height = cropArea.height;

          // วาดส่วนที่ต้องการตัดลงใน canvas
          ctx.drawImage(
            img,
            cropArea.x, cropArea.y, cropArea.width, cropArea.height, // source rectangle
            0, 0, cropArea.width, cropArea.height // destination rectangle
          );

          // แปลงเป็น data URL
          const croppedDataUrl = canvas.toDataURL('image/jpeg', 0.9);
          resolve(croppedDataUrl);

        } catch (error) {
          reject(error);
        }
      };

      img.onerror = () => reject(new Error('Failed to load image for cropping'));
      img.src = imageDataUrl;
    });
  }

  // Touch events for mobile support
  handleImageTouchStart(event) {
    if (event.touches.length === 1) {
      const touch = event.touches[0];
      this.handleImageMouseDown({
        clientX: touch.clientX,
        clientY: touch.clientY,
        target: event.target,
        preventDefault: () => event.preventDefault(),
        stopPropagation: () => event.stopPropagation()
      });
    }
  }

  handleCropTouchStart(event) {
    if (event.touches.length === 1) {
      const touch = event.touches[0];
      this.handleCropMouseDown({
        clientX: touch.clientX,
        clientY: touch.clientY,
        target: event.target,
        preventDefault: () => event.preventDefault(),
        stopPropagation: () => event.stopPropagation()
      });
    }
  }

  handleResizeTouchStart(event) {
    if (event.touches.length === 1) {
      const touch = event.touches[0];
      this.handleResizeMouseDown({
        clientX: touch.clientX,
        clientY: touch.clientY,
        target: event.target,
        preventDefault: () => event.preventDefault(),
        stopPropagation: () => event.stopPropagation()
      });
    }
  }

  handleTouchMove(event) {
    if (event.touches.length === 1) {
      const touch = event.touches[0];
      this.handleMouseMove({
        clientX: touch.clientX,
        clientY: touch.clientY
      });
      event.preventDefault();
    }
  }

  handleTouchEnd(event) {
    this.handleMouseUp();
    event.preventDefault();
  }
}