import { app } from '../../../scripts/app.js'
// import { api } from '../../../scripts/api.js'
import { ComfyWidgets } from '../../../scripts/widgets.js'
import { $el } from '../../../scripts/ui.js'

import {
  getQueue,
  interrupt,
  get_position_style,
  base64Df,
  getUrl,
  createImage,
  sleep
} from './common.js'

// let url = getUrl()

async function clipboardWriteImage (win, url) {
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')
  // console.log(url)
  const img = await createImage(url)
  // console.log(img)
  canvas.width = img.naturalWidth
  canvas.height = img.naturalHeight

  ctx.clearRect(0, 0, canvas.width, canvas.height)
  ctx.drawImage(img, 0, 0)
  // 将canvas转为blob
  canvas.toBlob(async blob => {
    const data = [
      new ClipboardItem({
        [blob.type]: blob
      })
    ]

    win.navigator.clipboard
      .write(data)
      .then(() => {
        console.log('Image copied to clipboard')
      })
      .catch(error => {
        console.error('Failed to copy image to clipboard:', error)
      })
  })
}

async function uploadFile (file) {
  try {
    const body = new FormData()
    body.append('image', file)
    body.append('overwrite', 'true')
    body.append('type', 'temp')

    const resp = await fetch(`${url}/upload/image`, {
      method: 'POST',
      body
    })

    if (resp.status === 200) {
      const data = await resp.json()
      let path = data.name
      if (data.subfolder) path = data.subfolder + '/' + path
      return path
    } else {
      alert(resp.status + ' - ' + resp.statusText)
    }
  } catch (error) {
    alert(error)
  }
}

// function switchCamera (deviceId = 'desktop') {
//   const constraints = {
//     audio: false,
//     video: {
//       width: { ideal: 1920, max: 1920 },
//       height: { ideal: 1080, max: 1080 },
//       deviceId: mediaDevices[webcamsEl.value].deviceId
//     }
//   }
//   console.log('switchCamera', constraints)

//   let mediaStreamPro
//   if (deviceId === 'desktop') {
//     mediaStreamPro = navigator.mediaDevices.getDisplayMedia(constraints)
//   } else {
//     mediaStreamPro = navigator.mediaDevices.getUserMedia(constraints)
//   }
//   return mediaStreamPro
// }

// alert(navigator.mediaDevices)

async function shareScreen (
  isCamera = false,
  webcamVideo,
  shareBtn,
  liveBtn,
  previewArea
) {
  try {
    let mediaStream

    if (!isCamera) {
      mediaStream = await navigator.mediaDevices.getDisplayMedia({
        video: true
      })
    } else {
      if (!localStorage.getItem('_mixlab_webcamera_select')) return
      let constraints =
        JSON.parse(localStorage.getItem('_mixlab_webcamera_select')) || {}
      mediaStream = await navigator.mediaDevices.getUserMedia(constraints)
    }

    webcamVideo.removeEventListener('timeupdate', videoTimeUpdateHandler)
    webcamVideo.srcObject = mediaStream
    webcamVideo.onloadedmetadata = () => {
      let x = 0,
        y = 0,
        width = webcamVideo.videoWidth,
        height = webcamVideo.videoHeight,
        imgWidth = webcamVideo.videoWidth,
        imgHeight = webcamVideo.videoHeight

      let d = getSetAreaData()
      if (
        d &&
        d.x >= 0 &&
        d.imgWidth === imgWidth &&
        d.imgHeight === imgHeight
      ) {
        x = d.x
        y = d.y
        width = d.width
        height = d.height
        imgWidth = d.imgWidth
        imgHeight = d.imgHeight
        console.log('#screen_share::使用上一次选区')
      }
      updateSetAreaData(x, y, width, height, imgWidth, imgHeight)

      webcamVideo.play()

      createBlobFromVideo(webcamVideo, true)

      webcamVideo.addEventListener('timeupdate', videoTimeUpdateHandler)

      // window._mixlab_screen_x = 0
      // window._mixlab_screen_y = 0
      // // console.log(webcamVideo)
      // window._mixlab_screen_width = webcamVideo.videoWidth
      // window._mixlab_screen_height = webcamVideo.videoHeight
    }

    mediaStream.addEventListener('inactive', handleStopSharing)

    // 停止共享的回调函数
    function handleStopSharing () {
      // console.log('用户停止了共享')
      // 执行其他操作
      if (window._mixlab_stopVideo) {
        window._mixlab_stopVideo()
        window._mixlab_stopVideo = null
        shareBtn.innerText = 'Share Screen'
      }
      if (window._mixlab_stopLive) {
        window._mixlab_stopLive()
        window._mixlab_stopLive = null
        liveBtn.innerText = 'Live Run'
      }
      return
    }

    window._mixlab_screen_webcamVideo = webcamVideo

    async function videoTimeUpdateHandler () {
      if (!window._mixlab_screen_live) return
      createBlobFromVideo(webcamVideo)
    }
  } catch (error) {
    alert('Error accessing screen stream: ' + error)
  }
  return () => {
    webcamVideo.pause() // 暂停视频播放
    webcamVideo.srcObject.getTracks().forEach(track => {
      track.stop()
    })
    webcamVideo.srcObject = null // 清空视频源对象
    window._mixlab_screen_live = false
    window._mixlab_screen_blob = null
    previewArea.innerHTML = ''
    interrupt()
  }
}

async function compareImages (threshold, previousImage, currentImage) {
  // 将 base64 转换为 Image 对象
  var previousImg = await createImage(previousImage)
  var currentImg = await createImage(currentImage)

  if (
    previousImg.naturalWidth != currentImg.naturalWidth ||
    previousImg.naturalHeight != currentImg.naturalHeight
  ) {
    return true // 图片有变化
  }

  // 创建一个 canvas 元素
  var canvas1 = document.createElement('canvas')
  canvas1.width = previousImg.naturalWidth
  canvas1.height = previousImg.naturalHeight
  var context1 = canvas1.getContext('2d')

  // 将图片绘制到 canvas 上
  context1.drawImage(previousImg, 0, 0)

  // 获取图片的像素数据
  var previousData = context1.getImageData(
    0,
    0,
    previousImg.naturalWidth,
    previousImg.naturalHeight
  ).data

  var canvas2 = document.createElement('canvas')
  canvas2.width = currentImg.naturalWidth
  canvas2.height = currentImg.naturalHeight
  var context2 = canvas2.getContext('2d')
  context2.drawImage(currentImg, 0, 0)
  var currentData = context2.getImageData(
    0,
    0,
    currentImg.naturalWidth,
    currentImg.naturalHeight
  ).data

  // 遍历每个像素点，计算像素差异
  var pixelDiff = 0
  for (var i = 0; i < previousData.length; i += 4) {
    var diffR = Math.abs(previousData[i] - currentData[i])
    var diffG = Math.abs(previousData[i + 1] - currentData[i + 1])
    var diffB = Math.abs(previousData[i + 2] - currentData[i + 2])

    // 计算像素差异总和
    pixelDiff += diffR + diffG + diffB
  }

  // 计算平均像素差异
  var averageDiff = pixelDiff / (previousData.length / 4)

  // 判断平均像素差异是否超过阈值
  // console.log(
  //   pixelDiff,
  //   averageDiff,
  //   threshold,
  //   currentImg.naturalWidth,
  //   currentImg.naturalHeight,previousData,currentData
  // )
  if (averageDiff > threshold) {
    return true // 图片有变化
  } else {
    return false // 图片无变化
  }
}

async function startLive (btn) {
  if (btn) window._mixlab_screen_live = !window._mixlab_screen_live

  if (btn) btn.innerText = `Stop Live`
  // console.log('#ML', 'live run', window._mixlab_screen_time)
  // if (window._mixlab_screen_time) {
  //   // console.log('#ML', 'live')
  //   return
  // }
  const { Pending, Running } = await getQueue()
  // console.log('#ML', Pending, window._mixlab_screen_blob)
  if (Pending <= 1 && window._mixlab_screen_blob && Running === 0) {
    // window._mixlab_screen_time = true

    const threshold = 1 // 阈值
    const previousImage = window._mixlab_screen_imagePath // 上一张图片的 base64
    let currentImage = await blobToBase64(window._mixlab_screen_blob)

    if (previousImage) {
      // 现在新的图片的 base64
      const imageChanged = await compareImages(
        threshold,
        previousImage,
        currentImage
      )
      console.log('#图片是否有变化:', imageChanged)

      if (imageChanged) {
        window._mixlab_screen_imagePath = currentImage
        document.querySelector('#queue-button').click()
      }
    } else {
      window._mixlab_screen_imagePath = currentImage
      // console.log(window._mixlab_screen_imagePath)
      document.querySelector('#queue-button').click()
    }

    // await uploadFile(file)
    // window._mixlab_screen_time = false

    await sleep(window._mixlab_screen_refresh_rate || 200)
    // console.log('#ML', window._mixlab_screen_imagePath)
  }

  if (btn) {
    startLive()
    return () => {
      // stop
      window._mixlab_screen_live = false
      window._mixlab_screen_blob = null
      interrupt()
    }
  } else if (window._mixlab_screen_live) {
    startLive()
  }
}

async function createBlobFromVideoForArea (webcamVideo) {
  const videoW = webcamVideo.videoWidth
  const videoH = webcamVideo.videoHeight
  const aspectRatio = videoW / videoH
  const WIDTH = videoW,
    HEIGHT = videoH
  const canvas = new OffscreenCanvas(WIDTH, HEIGHT)
  const ctx = canvas.getContext('2d')
  ctx.drawImage(webcamVideo, 0, 0, videoW, videoH, 0, 0, WIDTH, HEIGHT)

  const blob = await canvas.convertToBlob({
    type: 'image/jpeg',
    quality: 1
  })

  return blob
}

async function createBlobFromVideo (webcamVideo, updateImageBase64 = false) {
  const videoW = webcamVideo.videoWidth
  const videoH = webcamVideo.videoHeight
  const aspectRatio = videoW / videoH

  const { x, y, width, height } = window._mixlab_share_screen

  const canvas = new OffscreenCanvas(width, height)
  const ctx = canvas.getContext('2d')

  ctx.drawImage(webcamVideo, x, y, width, height, 0, 0, width, height)

  const blob = await canvas.convertToBlob({
    type: 'image/jpeg',
    quality: 1
  })
  // imgElement.src = await blobToBase64(blob)
  window._mixlab_screen_blob = blob

  console.log(
    '########updateImageBase64 ',
    updateImageBase64,
    x,
    y,
    width,
    height
  )
  if (updateImageBase64) {
    window._mixlab_screen_imagePath = await blobToBase64(blob)
  }
}

async function blobToBase64 (blob) {
  const reader = new FileReader()
  return new Promise((res, rej) => {
    reader.onload = function (event) {
      res(event.target.result)
    }
    reader.readAsDataURL(blob)
  })
}
function base64ToBlob (base64) {
  // 将Base64分割成类型和数据部分
  const parts = base64.split(';base64,')
  const type = parts[0].split(':')[1]
  const data = window.atob(parts[1])
  const arrayBuffer = new ArrayBuffer(data.length)
  const uint8Array = new Uint8Array(arrayBuffer)

  // 将Base64数据转换为Uint8Array
  for (let i = 0; i < data.length; i++) {
    uint8Array[i] = data.charCodeAt(i)
  }

  // 创建Blob对象
  const blob = new Blob([arrayBuffer], { type })

  return blob
}

async function requestCamera () {
  // 请求授权
  try {
    let stream = await navigator.mediaDevices.getUserMedia({ video: true })
    console.log('摄像头授权成功')
    // 获取视频轨道
    var videoTrack = stream.getVideoTracks()[0]

    // 停止视频轨道
    videoTrack.stop()

    return true
  } catch (error) {
    // 用户拒绝授权或发生其他错误
    console.error('摄像头授权失败：', error)

    // 提示用户授权摄像头访问权限
    if (error.name === 'NotAllowedError') {
      alert('请授权摄像头访问权限 chrome://settings/content/camera')
    } else {
      alert('摄像头访问权限请求失败，请重试 chrome://settings/content/camera')
    }

    // // 跳转到浏览器的授权设置页面
    // window.location.href = 'chrome://settings/content/camera'
  }
  return false
}

app.registerExtension({
  name: 'Mixlab.image.ScreenShareNode',
  async getCustomWidgets (app) {
    // console.log('#Mixlab.image.ScreenShareNode', app)
    return {
      CHEESE (node, inputName, inputData, app) {
        // We return an object containing a field CHEESE which has a function (taking node, name, data, app)
        const widget = {
          type: inputData[0], // the type, CHEESE
          name: inputName, // the name, slice
          size: [128, 12], // a default size
          draw (ctx, node, width, y) {
            // a method to draw the widget (ctx is a CanvasRenderingContext2D)
          },
          computeSize (...args) {
            return [128, 12] // a method to compute the current size of the widget
          },
          async serializeValue (nodeId, widgetIndex) {
            return window._mixlab_screen_imagePath || base64Df
          }
        }
        // console.log('#Mixlab.image.ScreenShareNode',widget)
        node.addCustomWidget(widget)
        return widget // and returns it.
      },
      PROMPT (node, inputName, inputData, app) {
        // We return an object containing a field CHEESE which has a function (taking node, name, data, app)
        const widget = {
          type: inputData[0], // the type, CHEESE
          name: inputName, // the name, slice
          size: [128, 12], // a default size
          draw (ctx, node, width, y) {
            // a method to draw the widget (ctx is a CanvasRenderingContext2D)
          },
          computeSize (...args) {
            return [128, 12] // a method to compute the current size of the widget
          },
          async serializeValue (nodeId, widgetIndex) {
            return window._mixlab_screen_prompt || ''
          }
        }
        // console.log('###widget', widget)
        node.addCustomWidget(widget) // adds it to the node
        return widget // and returns it.
      },
      SLIDE (node, inputName, inputData, app) {
        // We return an object containing a field CHEESE which has a function (taking node, name, data, app)
        const widget = {
          type: inputData[0], // the type, CHEESE
          name: inputName, // the name, slice
          size: [128, 12], // a default size
          draw (ctx, node, width, y) {
            // a method to draw the widget (ctx is a CanvasRenderingContext2D)
          },
          computeSize (...args) {
            return [128, 12] // a method to compute the current size of the widget
          },
          async serializeValue (nodeId, widgetIndex) {
            return window._mixlab_screen_slide_input || 0.5
          }
        }
        // console.log('###widget', widget)
        node.addCustomWidget(widget) // adds it to the node
        return widget // and returns it.
      },
      SEED (node, inputName, inputData, app) {
        // We return an object containing a field CHEESE which has a function (taking node, name, data, app)
        const widget = {
          type: inputData[0], // the type, CHEESE
          name: inputName, // the name, slice
          size: [128, 12], // a default size
          draw (ctx, node, width, y) {
            // a method to draw the widget (ctx is a CanvasRenderingContext2D)
          },
          computeSize (...args) {
            return [128, 12] // a method to compute the current size of the widget
          },
          async serializeValue (nodeId, widgetIndex) {
            return window._mixlab_screen_seed_input || 0
          }
        }
        // console.log('###widget', widget)
        node.addCustomWidget(widget) // adds it to the node
        return widget // and returns it.
      }
    }
  },
  async beforeRegisterNodeDef (nodeType, nodeData, app) {
    if (nodeType.comfyClass == 'ScreenShare') {
      const orig_nodeCreated = nodeType.prototype.onNodeCreated
      nodeType.prototype.onNodeCreated = function () {
        orig_nodeCreated?.apply(this, arguments)

        const widget = {
          type: 'HTML', // whatever
          name: 'sreen_share', // whatever
          draw (ctx, node, widget_width, y, widget_height) {
            // console.log('ScreenSHare', node)
            Object.assign(
              this.card.style,
              get_position_style(ctx, widget_width, y, node.size[1], 40)
            )
 
          }
        }

        widget.card = $el('div', {
          color: 'var(--descrip-text)',
          backgroundColor: 'var(--comfy-input-bg)'
        })

        widget.previewCard = $el('div', {
          color: 'var(--descrip-text)',
          backgroundColor: 'var(--comfy-input-bg)'
        })

        widget.preview = $el('video', {
          style: {
            width: '100%'
          },
          controls: true,
          poster:
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAALZJREFUKFOFkLERwjAQBPdbgBkInECGaMLUQDsE0AkRVRAYWqAByxldPPOWHwnw4OBGye1p50UDSoA+W2ABLPN7i+C5dyC6R/uiAUXRQCs0bXoNIu4QPQzAxDKxHoALOrZcqtiyR/T6CXw7+3IGHhkYcy6BOR2izwT8LptG8rbMiCRAUb+CQ6WzQVb0SNOi5Z2/nX35DRyb/ENazhpWKoGwrpD6nICp5c2qogc4of+c7QcrhgF4Aa/aoAFHiL+RAAAAAElFTkSuQmCC'
        })

        widget.previewArea = $el('div', {
          style: {
            color: 'var(--descrip-text)',
            backgroundColor: 'var(--comfy-input-bg)'
          }
        })

        widget.shareDiv = $el('div', {
          // innerText: 'Share Screen',
          style: {
            cursor: 'pointer',
            fontWeight: '300',
            display: 'flex',
            color: 'var(--descrip-text)',
            backgroundColor: 'var(--comfy-input-bg)'
          }
        })

        widget.shareBtn = $el('button', {
          innerText: 'Share Screen',
          style: {
            cursor: 'pointer',
            padding: '8px 0',
            fontWeight: '300',
            margin: '2px',
            width: '100%',
            color: 'var(--descrip-text)',
            backgroundColor: 'var(--comfy-input-bg)',
            borderRadius: '8px',
            borderColor: 'var(--border-color)',
            borderStyle: 'solid'
          }
        })

        widget.shareOfWebCamBtn = $el('button', {
          innerText: 'Camera',
          style: {
            cursor: 'pointer',
            padding: '8px 0',
            fontWeight: '300',
            margin: '2px',
            width: '100%',
            color: 'var(--descrip-text)',
            backgroundColor: 'var(--comfy-input-bg)',
            borderRadius: '8px',
            borderColor: 'var(--border-color)',
            borderStyle: 'solid'
          }
        })

        widget.openFloatingWinBtn = $el('button', {
          innerText: 'Set Area',
          style: {
            cursor: 'pointer',
            padding: '8px 0',
            fontWeight: '300',
            margin: '2px',
            color: 'var(--descrip-text)',
            backgroundColor: 'var(--comfy-input-bg)',
            borderRadius: '8px',
            borderColor: 'var(--border-color)',
            borderStyle: 'solid'
          }
        })

        // widget.refreshInput = $el('input', {
        //   placeholder: '  Refresh rate:200 ms',
        //   type: 'number',
        //   min: 100,
        //   step: 100,
        //   style: {
        //     cursor: 'pointer',
        //     padding: '8px 24px',
        //     fontWeight: '300',
        //     margin: '2px',
        //     color: 'var(--descrip-text)',
        //     backgroundColor: 'var(--comfy-input-bg)'
        //   }
        // });
        // widget.refreshInput.className='comfy-multiline-input'

        widget.liveBtn = $el('button', {
          innerText: 'Live Run',
          style: {
            cursor: 'pointer',
            padding: '8px 0',
            fontWeight: '300',
            margin: '2px',
            color: 'var(--descrip-text)',
            backgroundColor: 'var(--comfy-input-bg)',
            borderRadius: '8px',
            borderColor: 'var(--border-color)',
            borderStyle: 'solid'
          }
        })

        document.body.appendChild(widget.card)

        widget.card.appendChild(widget.previewCard)
        widget.previewCard.appendChild(widget.preview)
        widget.previewCard.appendChild(widget.previewArea)

        widget.card.appendChild(widget.shareDiv)
        widget.shareDiv.appendChild(widget.shareBtn)
        widget.shareDiv.appendChild(widget.shareOfWebCamBtn)
        widget.card.appendChild(widget.openFloatingWinBtn)
        // widget.card.appendChild(widget.refreshInput)
        widget.card.appendChild(widget.liveBtn)

        const toggleShare = async (isCamera = false) => {
          if (widget.preview.paused) {
            window._mixlab_stopVideo = await shareScreen(
              isCamera,
              widget.preview,
              widget.shareBtn,
              widget.liveBtn,
              widget.previewArea
            )

            if (isCamera) {
              widget.shareOfWebCamBtn.innerText = 'Stop Share'
              widget.shareBtn.innerText = 'Stop'
            } else {
              widget.shareOfWebCamBtn.innerText = 'Stop'
              widget.shareBtn.innerText = 'Stop Share'
            }

            console.log('视频已暂停')
            if (window._mixlab_stopLive) {
              window._mixlab_stopLive()
              window._mixlab_stopLive = null
              widget.liveBtn.innerText = 'Live Run'
            }

            setTimeout(() => updateSetAreaDisplay(), 2000)
          } else {
            console.log('视频正在播放')
            if (window._mixlab_stopVideo) {
              window._mixlab_stopVideo()
              window._mixlab_stopVideo = null
              widget.shareBtn.innerText = 'Share Screen'
              widget.shareOfWebCamBtn.innerText = 'Camera'
            }
            if (window._mixlab_stopLive) {
              window._mixlab_stopLive()
              window._mixlab_stopLive = null
              widget.liveBtn.innerText = 'Live Run'
            }
          }
        }

        // updateSetAreaDisplay(widget.previewArea, 200, 200)
        widget.shareOfWebCamBtn.addEventListener('click', async () => {
          if (!widget.preview.paused) {
            if (window._mixlab_stopVideo) {
              window._mixlab_stopVideo()
              window._mixlab_stopVideo = null
              widget.shareBtn.innerText = 'Share Screen'
              widget.shareOfWebCamBtn.innerText = 'Camera'
            }
            if (window._mixlab_stopLive) {
              window._mixlab_stopLive()
              window._mixlab_stopLive = null
              widget.liveBtn.innerText = 'Live Run'
            }
            return
          }

          let r = await requestCamera()
          if (r === false) return
          const devices = await navigator.mediaDevices.enumerateDevices()

          // 查找摄像头设备
          var cameras = devices.filter(function (device) {
            // console.log(device)
            return device.kind === 'videoinput'
          })

          // 创建 <select> 元素
          var select = document.createElement('select')

          // 创建默认选项
          // var defaultOption = document.createElement('option')
          // defaultOption.text = '请选择摄像头'
          // defaultOption.disabled = true
          // defaultOption.selected = true
          // select.appendChild(defaultOption)

          // 创建每个摄像头设备的选项
          Array.from(cameras, (camera, i) => {
            var option = document.createElement('option')
            option.value = camera.deviceId
            option.text = camera.label || 'Camera ' + (select.length - 1)
            if (i === 0) option.selected = true
            select.appendChild(option)
          })

          let modal = document.createElement('div')
          modal.className = 'comfy-modal'
          modal.style.display = 'flex'

          let modalContent = document.createElement('div')
          modalContent.className = 'comfy-modal-content'

          let title = document.createElement('p')
          title.innerText = 'Please select a camera'

          modalContent.appendChild(title)
          modalContent.appendChild(select)

          let btns = document.createElement('div')
          btns.style = `display: flex;
          justify-content: space-between;
          margin: 24px 0;`

          let btn = document.createElement('button')
          btn.innerText = 'OK'
          btn.style = `width: 112px;`

          let closeBtn = document.createElement('button')
          closeBtn.innerText = 'Cancel'
          closeBtn.style = `width: 112px;`

          modalContent.appendChild(btns)
          btns.appendChild(btn)
          btns.appendChild(closeBtn)

          modal.appendChild(modalContent)
          document.body.appendChild(modal)

          btn.addEventListener('click', () => {
            // 获取所选择的选项的索引
            var selectedIndex = select.selectedIndex

            // 获取所选择的选项的值
            var selectedValue = select.options[selectedIndex].value
            if (selectedValue) {
              const constraints = {
                audio: false,
                video: {
                  width: { ideal: 1920, max: 1920 },
                  height: { ideal: 1080, max: 1080 },
                  deviceId: selectedValue
                }
              }

              localStorage.setItem(
                '_mixlab_webcamera_select',
                JSON.stringify(constraints)
              )

              toggleShare(true)
            }

            modal.remove()
          })

          closeBtn.addEventListener('click', () => {
            modal.remove()
          })
        })

        widget.shareBtn.addEventListener('click', async () => {
          toggleShare()
        })

        // widget.refreshInput.addEventListener('change', async () => {
        //   window._mixlab_screen_refresh_rate = Math.round(
        //     widget.refreshInput.value
        //   )
        // })

        widget.liveBtn.addEventListener('click', async () => {
          if (window._mixlab_stopLive) {
            window._mixlab_stopLive()
            window._mixlab_stopLive = null
            widget.liveBtn.innerText = 'Live Run'
          } else {
            window._mixlab_stopLive = await startLive(widget.liveBtn)
            console.log('window._mixlab_stopLive', window._mixlab_stopLive)
          }
        })

        widget.openFloatingWinBtn.addEventListener('click', async () => {
          // if (window._mixlab_stopLive) {
          //   window._mixlab_stopLive()
          //   window._mixlab_stopLive = null
          //   widget.liveBtn.innerText = 'Live Run'
          // }
          let blob = await createBlobFromVideoForArea(
            window._mixlab_screen_webcamVideo
          )

          setArea(await blobToBase64(blob))
        })
        // console.log('widget.inputEl',widget.inputEl)

        this.setSize([this.size[0], this.size[1] + 450])
        app.canvas.draw(true, true)

        /*
              Add the widget, make sure we clean up nicely, and we do not want to be serialized!
              */
        this.addCustomWidget(widget)
        this.onRemoved = function () {
          widget.preview.remove()
          widget.shareDiv.remove()
          widget.shareOfWebCamBtn.remove()
          widget.shareBtn.remove()
          widget.liveBtn.remove()
          widget.card.remove()
          // widget.refreshInput.remove()
          widget.previewArea.remove()
          widget.previewCard.remove()
        }
        this.serialize_widgets = true
      }

      const onExecuted = nodeType.prototype.onExecuted
      nodeType.prototype.onExecuted = function (message) {
        onExecuted?.apply(this, arguments)
        // console.log('###ScreenShare', this, message.refresh_rate)
        window._mixlab_screen_refresh_rate = Math.round(
          message.refresh_rate[0] || 500
        )
      }
    }
  }
})

function updateSetAreaDisplay () {
  try {
    let canvas = document.createElement('canvas')
    canvas.width = window._mixlab_screen_webcamVideo.videoWidth
    canvas.height = window._mixlab_screen_webcamVideo.videoHeight
    let ctx = canvas.getContext('2d')
    const lineWidth = 2 // Width of the stroke line
    const strokeColor = 'red' // Color of the stroke

    // Draw the rectangle
    ctx.strokeStyle = strokeColor // Set the stroke color
    ctx.lineWidth = lineWidth // Set the stroke line width

    ctx.fillStyle = 'rgba(255,0,0,0.35)'

    let x = 0,
      y = 0,
      width = canvas.width,
      height = canvas.height

    if (!window._mixlab_share_screen) {
      let d = getSetAreaData()
      if (d) {
        window._mixlab_share_screen = d
      }
    }

    if (window._mixlab_share_screen) {
      x = window._mixlab_share_screen.x
      y = window._mixlab_share_screen.y
      width = window._mixlab_share_screen.width
      height = window._mixlab_share_screen.height
    }

    ctx.strokeRect(x, y, width, height) // Draw the stroked rectangle
    ctx.fillRect(x, y, width, height)

    canvas.style.width = '100%'

    let area = graph._nodes
      .filter(n => n.type === 'ScreenShare')[0]
      .widgets.filter(w => w.name == 'sreen_share')[0].previewArea

    area.innerHTML = ''
    area.appendChild(canvas)
    area.style = `
    position: absolute;
    width:100%%;
    left:0;
    top:0;
    `
  } catch (error) {
    console.log(error)
  }
}

function updateSetAreaData (left, top, width, height, imgWidth, imgHeight) {
  window._mixlab_share_screen = {
    x: left,
    y: top,
    width,
    height,
    imgWidth,
    imgHeight
  }
  localStorage.setItem(
    '_mixlab_share_screen',
    JSON.stringify(window._mixlab_share_screen)
  )
}

function getSetAreaData () {
  try {
    let data = JSON.parse(localStorage.getItem('_mixlab_share_screen')) || {}
    if (data.width === 0 || data.height === 0 || data.width === undefined)
      return
    return data
  } catch (error) {}
  return
}

async function setArea (src) {
  let displayHeight = Math.round(window.screen.availHeight * 0.6)
  let div = document.createElement('div')
  div.innerHTML = `
    <div id='ml_overlay' style='position: absolute;top:0;background: #251f1fc4;
    height: 100vh;
    z-index:99999999999999;
    width: 100%;'>
      <img id='ml_video' style='position: absolute; 
      height: ${displayHeight}px;user-select: none; 
      -webkit-user-drag: none;
      outline: 2px solid #eaeaea;
      left: 0;
      box-shadow: 8px 9px 17px #575757;' />
      <div id='ml_selection' style='position: absolute; 
      border: 2px dashed red; 
      pointer-events: none;'></div>
    </div>`
  // document.body.querySelector('#ml_overlay')
  document.body.appendChild(div)

  let im = await createImage(src)

  let img = div.querySelector('#ml_video')
  let overlay = div.querySelector('#ml_overlay')
  let selection = div.querySelector('#ml_selection')
  let startX, startY, endX, endY
  let start = false
  // Set video source
  img.src = src

  // init area
  const data = getSetAreaData()
  let x = 0,
    y = 0,
    width = (im.naturalWidth * displayHeight) / im.naturalHeight,
    height = displayHeight
  let imgWidth = im.naturalWidth
  let imgHeight = im.naturalHeight

  if (
    data &&
    data.width > 0 &&
    data.height > 0 &&
    data.imgWidth === imgWidth &&
    data.imgHeight === imgHeight &&
    data.imgHeight > 0
  ) {
    // 相同尺寸窗口，恢复选区
    x = (img.width * data.x) / data.imgWidth
    y = (img.height * data.y) / data.imgHeight
    width = (img.width * data.width) / data.imgWidth
    height = (img.height * data.height) / data.imgHeight
  }

  selection.style.left = x + 'px'
  selection.style.top = y + 'px'
  selection.style.width = width + 'px'
  selection.style.height = height + 'px'

  // Add mouse events
  img.addEventListener('mousedown', startSelection)
  img.addEventListener('mousemove', updateSelection)
  img.addEventListener('mouseup', endSelection)
  overlay.addEventListener('click', remove)

  function remove () {
    overlay.removeEventListener('click', remove)
    img.removeEventListener('mousedown', startSelection)
    img.removeEventListener('mousemove', updateSelection)
    img.removeEventListener('mouseup', endSelection)
    div.remove()
  }

  function startSelection (event) {
    if (start == false) {
      startX = event.clientX
      startY = event.clientY
      updateSelection(event)
      start = true
    } else {
    }
  }

  function updateSelection (event) {
    endX = event.clientX
    endY = event.clientY

    // Calculate width, height, and coordinates
    let width = Math.abs(endX - startX)
    let height = Math.abs(endY - startY)
    let left = Math.min(startX, endX)
    let top = Math.min(startY, endY)

    // Set selection style
    selection.style.left = left + 'px'
    selection.style.top = top + 'px'
    selection.style.width = width + 'px'
    selection.style.height = height + 'px'
  }

  function endSelection (event) {
    endX = event.clientX
    endY = event.clientY

    // 获取img元素的真实宽度和高度
    let imgWidth = img.naturalWidth
    let imgHeight = img.naturalHeight

    // 换算起始坐标
    let realStartX = (startX / img.offsetWidth) * imgWidth
    let realStartY = (startY / img.offsetHeight) * imgHeight

    // 换算起始坐标
    let realEndX = (endX / img.offsetWidth) * imgWidth
    let realEndY = (endY / img.offsetHeight) * imgHeight

    startX = realStartX
    startY = realStartY
    endX = realEndX
    endY = realEndY
    // Calculate width, height, and coordinates
    let width = Math.abs(endX - startX)
    let height = Math.abs(endY - startY)
    let left = Math.min(startX, endX)
    let top = Math.min(startY, endY)

    if (width <= 0 && height <= 0) return remove()

    updateSetAreaData(left, top, width, height, imgWidth, imgHeight)

    updateSetAreaDisplay()

    createBlobFromVideo(
      window._mixlab_screen_webcamVideo,
      !window._mixlab_screen_live
    )
    remove()
  }
}

async function save_workflow (json) {
  let api_host = `${window.location.hostname}:${window.location.port}`
  let api_base = ''
  let url = `${window.location.protocol}//${api_host}${api_base}`

  const res = await fetch(`${url}/mixlab/workflow`, {
    method: 'POST',
    body: JSON.stringify({
      data: json,
      task: 'save'
    })
  })
  return await res.json()
}

async function get_my_workflow () {
  let api_host = `${window.location.hostname}:${window.location.port}`
  let api_base = ''
  let url = `${window.location.protocol}//${api_host}${api_base}`

  const res = await fetch(`${url}/mixlab/workflow`, {
    method: 'POST',
    body: JSON.stringify({
      task: 'list'
    })
  })
  let result = await res.json()
  return result.data
}

app.registerExtension({
  name: 'Mixlab.image.FloatingVideo',
  async beforeRegisterNodeDef (nodeType, nodeData, app) {
    if (nodeType.comfyClass == 'FloatingVideo') {
      const orig_nodeCreated = nodeType.prototype.onNodeCreated
      nodeType.prototype.onNodeCreated = function () {
        orig_nodeCreated?.apply(this, arguments)

        const widget = {
          type: 'video',
          name: 'FloatingVideo',
          draw (ctx, node, widget_width, y, widget_height) {
            Object.assign(this.card.style, {
              ...get_position_style(ctx, widget_width, y, node.size[1], 40),
              top: `${widget_height}px`
            })
          }
        }

        widget.card = $el('div', {})

        widget.preview = $el('video', {
          controls: true,
          draggable: true,
          style: {
            width: '100%'
          },
          poster:
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAALZJREFUKFOFkLERwjAQBPdbgBkInECGaMLUQDsE0AkRVRAYWqAByxldPPOWHwnw4OBGye1p50UDSoA+W2ABLPN7i+C5dyC6R/uiAUXRQCs0bXoNIu4QPQzAxDKxHoALOrZcqtiyR/T6CXw7+3IGHhkYcy6BOR2izwT8LptG8rbMiCRAUb+CQ6WzQVb0SNOi5Z2/nX35DRyb/ENazhpWKoGwrpD6nICp5c2qogc4of+c7QcrhgF4Aa/aoAFHiL+RAAAAAElFTkSuQmCC'
        })

        // })
        // const dropTarget = document.getElementById('your-drop-target-id')

        // dropTarget.addEventListener('dragover', event => {
        //   event.preventDefault()
        // })

        // dropTarget.addEventListener('drop', event => {
        //   event.preventDefault()

        //   const imageUrl = event.dataTransfer.getData('text/plain')

        //   navigator.clipboard
        //     .writeText(imageUrl)
        //     .then(() => {
        //       console.log('Image URL copied to clipboard')
        //     })
        //     .catch(error => {
        //       console.error('Failed to copy image URL to clipboard:', error)
        //     })
        // })

        widget.canvas = $el('canvas', {
          style: {
            display: 'none'
          }
        })

        widget.PictureInPicture = $el('button', {
          innerText: 'Picture In Picture',
          style: {
            display: 'pictureInPictureEnabled' in document ? 'block' : 'none',
            cursor: 'pointer',
            padding: '8px 0',
            fontWeight: '300',
            margin: '2px',
            color: 'var(--descrip-text)',
            backgroundColor: 'var(--comfy-input-bg)',
            borderRadius: '8px',
            borderColor: 'var(--border-color)',
            borderStyle: 'solid'
          }
        })

        document.body.appendChild(widget.card)
        widget.card.appendChild(widget.PictureInPicture)
        widget.card.appendChild(widget.preview)
        widget.card.appendChild(widget.canvas)

        widget.preview.addEventListener('click', event => {
          const imageUrl = window._mixlab_screen_result || ''
          // console.log(imageUrl)
          try {
            if (imageUrl) clipboardWriteImage(pipWindow, imageUrl)
          } catch (error) {
            console.log(error)
            if (imageUrl) clipboardWriteImage(window, imageUrl)
          }

          // pipWindow.navigator.permissions
          //   .query({ name: 'clipboard-write' })
          //   .then(result => {
          //     if (result.state === 'granted' || result.state === 'prompt') {
          //       // 执行复制操作

          //     } else {
          //       console.error('Clipboard write permission denied')
          //     }
          //   })
        })

        // widget.card.appendChild(widget.PictureInPicture)

        widget.PictureInPicture.addEventListener('click', async () => {
          if (window.location.protocol != 'https:') {
            let http_workflow = app.graph.serialize()
            await save_workflow(http_workflow)

            window.alert(
              `Redirecting to HTTPS access due to the requirement of the floating window. https://${
                window.location.hostname
              }:${~~window.location.port + 1}`
            )
            window.open(
              `https://${window.location.hostname}:${
                ~~window.location.port + 1
              }?workflow=1`
            )
          }
          // Open a Picture-in-Picture window.
          let w = 360,
            s = widget.preview.videoWidth / widget.preview.videoHeight,
            h = w / s || w
          // console.log(h)

          if (!window.documentPictureInPicture) {
            window.alert(
              'This feature is available only in secure contexts (HTTPS), in some or all supporting browsers. https://developer.mozilla.org/en-US/docs/Web/API/Document_Picture-in-Picture_API'
            )
          }

          const pipWindow = await documentPictureInPicture.requestWindow({
            width: w,
            height: Math.round(h) + 120
          })

          pipWindow.document.body.style = `margin: 0px;
          overflow: hidden;
          background: #2a2c34;
          border: 4px solid #878787;
          outline: none;background: black;`

          let div = document.createElement('div')
          div.style = `display:flex;position: fixed;flex-direction: column;
          bottom: 0px;
          z-index: 9999;
          left: 0px;
          width: calc(100% - 24px);
          margin: 12px;`

          let inputDiv = document.createElement('div')
          inputDiv.style = `width: 100%;`

          // inputDiv.style = ``
          let infoDiv = document.createElement('div')
          infoDiv.style = `width: 100%;
          display: flex;
          justify-content: space-between;
          height: 16px;
          color: white;
          margin-bottom: 4px;
          font-size: 12px;
          text-shadow: gray 1px 1px;
          align-items: center;`

          let infoText = document.createElement('div')
          infoText.id = 'info'

          let hideBtn = document.createElement('button')
          hideBtn.innerText = '🤖'
          hideBtn.style = `
          border: none;
          background: none;
          cursor: pointer; height: 24px; margin: 4px; color: red;`

          hideBtn.addEventListener('click', () => {
            if (fnDiv.style.display == 'none') {
              fnDiv.style.display = 'flex'
            } else {
              fnDiv.style.display = 'none'
            }
            try {
              pipWindow.document.querySelector('#info').innerText = ''
            } catch (error) {
              console.log(error)
            }
          })

          // Move the player to the Picture-in-Picture window.
          let input = document.createElement('textarea')
          input.style = `
          min-width:90%;
          max-width:100%;
          background: #24283db3;
          color: white;
          font-size: 14px;
          padding: 8px;
          font-weight: 300;
          letter-spacing: 1px;
          outline: none;
          min-height: 98px;
          border-radius: 8px;
          border: 1px solid rgb(91, 91, 91);
          font-family: sans-serif;
          `
          // Create a style element
          const style = document.createElement('style')
          // Define the CSS rule for scrollbar width
          const cssRule = `::-webkit-scrollbar { width: 2px;}`
          // Add the CSS rule to the style element
          style.appendChild(document.createTextNode(cssRule))

          // Append the style element to the document head
          pipWindow.document.head.appendChild(style)

          window._mixlab_screen_prompt =
            window._mixlab_screen_prompt ||
            'beautiful scenery nature glass bottle landscape,under water'
          input.value = window._mixlab_screen_prompt

          let btnDiv = document.createElement('div')

          btnDiv.style = `cursor: pointer;
          display: flex;
          flex-direction: column;
          justify-content: start;
          align-items: center;
          width: 24px;
          font-size: 16px;
          margin-right: 6px;user-select: none;`

          let seedBtn = document.createElement('butotn')
          seedBtn.innerText = '🎲'
          seedBtn.style = `cursor: pointer;height: 24px;margin:4px;
          color: red;`

          seedBtn.addEventListener('click', () => {
            window._mixlab_screen_seed_input = Math.round(
              Math.floor(Math.random() * 0xffffffffffffffff)
            )

            try {
              pipWindow.document.querySelector('#info').innerText =
                window._mixlab_screen_seed_input
            } catch (error) {
              console.log(error)
            }

            // console.log(window._mixlab_screen_seed_input)
            if (window._mixlab_screen_imagePath)
              document.querySelector('#queue-button').click()
          })

          // TODO 需要判断是否有screenshare节点，没有的话，不需要添加
          let pauseBtn = document.createElement('butotn')
          pauseBtn.innerText = '⏸'
          pauseBtn.style = `cursor: pointer;height: 24px;margin:4px;
          color: #03A9F4;`

          pauseBtn.addEventListener('click', async () => {
            if (window._mixlab_stopLive) {
              pauseBtn.innerText = '▶'

              window._mixlab_stopLive()
              window._mixlab_stopLive = null

              let node = this.graph._nodes.filter(
                n => n.type === 'ScreenShare'
              )[0]

              var w = node.widgets?.filter(w => w.name === 'sreen_share')[0] // see if it already exists
              if (w) {
                w.liveBtn.innerText = 'Live Run'
              }

              try {
                pipWindow.document.querySelector('#info').innerText =
                  'Stop Live'
              } catch (error) {
                console.log(error)
              }
            } else {
              pauseBtn.innerText = '⏸'
              let node = this.graph._nodes.filter(
                n => n.type === 'ScreenShare'
              )[0]
              var w = node.widgets?.filter(w => w.name === 'sreen_share')[0] // see if it already exists
              if (w) {
                w.liveBtn.innerText = 'Stop Live'
                window._mixlab_stopLive = await startLive(w.liveBtn)
                console.log('window._mixlab_stopLive', window._mixlab_stopLive)
              }

              try {
                pipWindow.document.querySelector('#info').innerText = 'Live'
              } catch (error) {
                console.log(error)
              }
            }
          })

          let promptFinishBtn = document.createElement('butotn')
          promptFinishBtn.innerText = '🚀'
          promptFinishBtn.style = `cursor: pointer;height: 24px;margin:4px;`
          promptFinishBtn.addEventListener('click', () => {
            console.log('##更新Prompt')
            window._mixlab_screen_prompt =
              window._mixlab_screen_prompt_input || window._mixlab_screen_prompt

            if (window._mixlab_screen_imagePath)
              document.querySelector('#queue-button').click()

            try {
              pipWindow.document.querySelector('#info').innerText =
                'Update Prompt'
            } catch (error) {
              console.log(error)
            }
          })

          widget.preview.addEventListener('click', event => {
            const imageUrl = window._mixlab_screen_result || ''
            // console.log(imageUrl)
            try {
              if (imageUrl) clipboardWriteImage(pipWindow, imageUrl)
            } catch (error) {
              console.log(error)
              if (imageUrl) clipboardWriteImage(window, imageUrl)
            }

            try {
              pipWindow.document.querySelector('#info').innerText =
                'Image copied to clipboard'
              setTimeout(
                () =>
                  (pipWindow.document.querySelector('#info').innerText = ''),
                8000
              )
            } catch (error) {
              console.log(error)
            }
            // pipWindow.navigator.permissions
            //   .query({ name: 'clipboard-write' })
            //   .then(result => {
            //     if (result.state === 'granted' || result.state === 'prompt') {
            //       // 执行复制操作

            //     } else {
            //       console.error('Clipboard write permission denied')
            //     }
            //   })
          })

          pipWindow.document.body.append(widget.preview)
          pipWindow.document.body.append(div)

          // 滑动条
          const createSlide = () => {
            let d = document.createElement('div')
            d.style = `width: 100%;margin-bottom: 12px;`
            let range = document.createElement('input')
            range.type = 'range'
            d.appendChild(range)
            return range
          }

          let slideInp = createSlide()
          slideInp.addEventListener('change', () => {
            console.log(~~slideInp.value / 100)
            window._mixlab_screen_slide_input = ~~slideInp.value / 100
            try {
              pipWindow.document.querySelector('#info').innerText =
                window._mixlab_screen_slide_input
              if (window._mixlab_screen_imagePath)
                document.querySelector('#queue-button').click()
            } catch (error) {
              console.log(error)
            }
          })

          // console.log(pipWindow)

          let fnDiv = document.createElement('div')
          fnDiv.style = `display: flex;`

          div.appendChild(infoDiv)
          div.appendChild(fnDiv)

          infoDiv.appendChild(infoText)
          infoDiv.appendChild(hideBtn)

          fnDiv.appendChild(btnDiv)
          // 按钮区域
          btnDiv.appendChild(seedBtn)
          btnDiv.appendChild(pauseBtn)
          btnDiv.appendChild(promptFinishBtn)

          // 输入框
          fnDiv.appendChild(inputDiv)
          inputDiv.appendChild(slideInp)

          inputDiv.appendChild(input)

          input.addEventListener('input', () => {
            window._mixlab_screen_prompt_input = input.value
            try {
              pipWindow.document.querySelector('#info').innerText = ''
            } catch (error) {
              console.log(error)
            }
          })

          input.addEventListener('keydown', handleKeyDown)

          function handleKeyDown (event) {
            if (event.key === 'Enter') {
              if (!event.shiftKey) {
                // 回车键被按下且未同时按下Shift键，执行你的操作
                event.preventDefault() // 阻止默认行为（如提交表单）
                // 在这里添加你的代码
                console.log('##更新Prompt')
                window._mixlab_screen_prompt =
                  window._mixlab_screen_prompt_input ||
                  window._mixlab_screen_prompt

                if (window._mixlab_screen_imagePath)
                  document.querySelector('#queue-button').click()

                try {
                  pipWindow.document.querySelector('#info').innerText =
                    'Update Prompt'
                } catch (error) {
                  console.log(error)
                }
              }
            }
          }

          // Move the player back when the Picture-in-Picture window closes.
          pipWindow.addEventListener('pagehide', event => {
            widget.card.appendChild(widget.preview)
            // pipWindow.remove()
            pipWindow.close()
          })
        })

        this.addCustomWidget(widget)
        this.onRemoved = function () {
          widget.preview.remove()
          widget.canvas.remove()
          widget.card.remove()
          widget.PictureInPicture.remove()
        }
        this.serialize_widgets = true
      }

      const onExecuted = nodeType.prototype.onExecuted
      nodeType.prototype.onExecuted = function (message) {
        const r = onExecuted ? onExecuted.apply(this, message) : undefined

        if (this.widgets) {
          const video = this.widgets.filter(w => w.type === `video`)[0]
          const canvas = video.canvas

          if (video.preview.paused) {
            const stream = canvas.captureStream()
            const videoTrack = stream.getVideoTracks()[0]

            video.preview.srcObject = new MediaStream([videoTrack])
            try {
              video.preview.play()
            } catch (error) {
              console.log(error)
            }

            // 检查浏览器是否支持画中画模式
            if ('pictureInPictureEnabled' in document) {
              // 检查video元素是否可以进入画中画模式
              // if (video.preview !== document.pictureInPictureElement) {
              //   // 请求进入画中画模式
              //   video.preview.addEventListener('loadedmetadata', () => {
              //     // 请求进入画中画模式
              //     video.preview
              //       .requestPictureInPicture()
              //       .then(() => {
              //         // 进入画中画模式成功
              //         console.log('进入画中画模式成功')
              //       })
              //       .catch(error => {
              //         // 进入画中画模式失败
              //         console.error('进入画中画模式失败', error)
              //       })
              //   })
              // } else {
              //   // 已经在画中画模式中
              //   console.log('已经在画中画模式中')
              // }
            } else {
              // 浏览器不支持画中画模式
              console.error('浏览器不支持画中画模式')
            }
          }

          const context = canvas.getContext('2d')

          if (message?.images_) {
            window._mixlab_screen_result = `data:image/png;base64,${message.images_[0]}`
            const image = new Image()
            image.onload = function () {
              canvas.width = image.width
              canvas.height = image.height
              context.drawImage(image, 0, 0)
            }
            // console.log(`data:image/jpeg;base64,${base64}`)
            image.src = window._mixlab_screen_result
          }
          const onRemoved = this.onRemoved
          this.onRemoved = () => {
            // cleanupNode(this)
            return onRemoved?.()
          }
        }
        this.setSize([
          this.size[0],
          this.computeSize([this.size[0], this.size[1]])[1]
        ])
        return r
      }
    }
  }
})

// ;(() => {
//   // 创建一个新的style元素
//   var styleElement = document.createElement('style')

//   // 设置样式内容
//   var cssCode = `
//   :picture-in-picture {
//     box-shadow: 0 0 0 5px red;
//   }
// `
//   // 将样式内容添加到style元素中
//   styleElement.appendChild(document.createTextNode(cssCode))

//   // 将style元素添加到文档头部
//   document.head.appendChild(styleElement)
// })()

// 和python实现一样
function run (mutable_prompt, immutable_prompt) {
  // Split the text into an array of words
  const words1 = mutable_prompt.split('\n')

  // Split the text into an array of words
  const words2 = immutable_prompt.split('\n')

  const prompts = []
  for (let i = 0; i < words1.length; i++) {
    words1[i] = words1[i].trim()
    for (let j = 0; j < words2.length; j++) {
      words2[j] = words2[j].trim()
      if (words2[j] && words1[i]) {
        prompts.push(words2[j].replaceAll('``', words1[i]))
      }
    }
  }

  return prompts
}

// 更新ui，计算prompt的组合结果
const updateUI = node => {
  const mutable_prompt_w = node.widgets.filter(
    w => w.name === 'mutable_prompt'
  )[0]
  mutable_prompt_w.inputEl.title = 'Enter keywords, one per line'
  const immutable_prompt_w = node.widgets.filter(
    w => w.name === 'immutable_prompt'
  )[0]
  immutable_prompt_w.inputEl.title =
    'Enter prompts, one per line, variables represented by ``'

  const max_count = node.widgets.filter(w => w.name === 'max_count')[0]
  let prompts = run(mutable_prompt_w.value, immutable_prompt_w.value)

  prompts = prompts.slice(0, max_count.value)

  max_count.value = prompts.length

  // 如果已经存在,删除
  const pw = node.widgets.filter(w => w.name === 'prompts')[0]
  if (pw) {
    // node.widgets[pos].onRemove?.();
    pw.value = prompts.join('\n\n')
    pw.inputEl.title = `Total of ${prompts.length} prompts`
  } else {
    // 动态添加
    // console.log('ComfyWidgets',ComfyWidgets.STRING(
    //   node,
    //   'prompts',
    //   ['STRING', { multiline: true }]
    // ))

    // ComfyWidgets.STRING(this, "", ["", {default:this.properties.text, multiline: true}], app)

    const w = ComfyWidgets.STRING(
      node,
      'prompts',
      ['STRING', { multiline: true }],
      app
    ).widget
    w.inputEl.readOnly = true
    w.inputEl.style.opacity = 0.6
    w.value = prompts.join('\n\n')
    w.inputEl.title = `Total of ${prompts.length} prompts`
  }

  // 移除无关的widget
  //  for (let i = 0; i < node.widgets.length; i++) {
  //   console.log(node.widgets[i]?.name)
  //   if(node.widgets[i]&&!['mutable_prompt','immutable_prompt','max_count','prompts'].includes(node.widgets[i].name)) node.widgets[i].onRemove?.();
  // }

  // console.log(node.widgets.length,node.size);

  node.widgets.length = 5
  node.onResize?.(node.size)
}

const exportGraph = () => {
  const graph = app.graph

  var clipboard_info = {
    nodes: [],
    links: []
  }
  var index = 0
  var selected_nodes_array = []
  for (var i in graph._nodes_in_order) {
    var node = graph._nodes_in_order[i]
    if (node.clonable === false) continue
    node._relative_id = index
    selected_nodes_array.push(node)
    index += 1
  }

  for (var i = 0; i < selected_nodes_array.length; ++i) {
    var node = selected_nodes_array[i]
    var cloned = node.clone()
    if (!cloned) {
      console.warn('node type not found: ' + node.type)
      continue
    }

    let nc = {}
    let n = cloned.serialize()
    for (const key in n) {
      if (
        [
          'type',
          'pos',
          'size',
          'flags',
          'order',
          'mode',
          'inputs',
          'outputs',
          'properties',
          'widgets_values'
        ].includes(key)
      ) {
        nc[key] = n[key]
      }
    }

    clipboard_info.nodes.push(nc)

    if (node.inputs && node.inputs.length) {
      for (var j = 0; j < node.inputs.length; ++j) {
        var input = node.inputs[j]
        if (!input || input.link == null) {
          continue
        }
        var link_info = graph.links[input.link]
        if (!link_info) {
          continue
        }
        var target_node = graph.getNodeById(link_info.origin_id)
        if (!target_node) {
          continue
        }
        clipboard_info.links.push([
          target_node._relative_id,
          link_info.origin_slot, //j,
          node._relative_id,
          link_info.target_slot,
          target_node.id
        ])
      }
    }
  }
  localStorage.setItem('_Mixlab_clipboard', JSON.stringify(clipboard_info))

  return clipboard_info
}

const my = {
  nodes: [
    {
      type: 'LoadImage',
      pos: [719.5130480797907, 172.9437092123179],
      size: { 0: 315, 1: 314 },
      flags: {},
      order: 0,
      mode: 0,
      outputs: [
        { name: 'IMAGE', type: 'IMAGE', links: [], shape: 3 },
        { name: 'MASK', type: 'MASK', links: null, shape: 3 }
      ],
      properties: { 'Node name for S&R': 'LoadImage' },
      widgets_values: ['00204211b3c71288c12ed66516a1a20a.jpg', 'image']
    },
    {
      type: 'ControlNetLoader',
      pos: [1199.5130480797907, -331.0562907876821],
      size: { 0: 415.221923828125, 1: 58.84859848022461 },
      flags: {},
      order: 1,
      mode: 0,
      outputs: [
        { name: 'CONTROL_NET', type: 'CONTROL_NET', links: [], shape: 3 }
      ],
      properties: { 'Node name for S&R': 'ControlNetLoader' },
      widgets_values: ['control_v11p_sd15_canny.pth']
    },
    {
      type: 'ControlNetLoader',
      pos: [1204.5130480797907, -169.0562907876821],
      size: { 0: 415.221923828125, 1: 58.84859848022461 },
      flags: {},
      order: 2,
      mode: 0,
      outputs: [
        { name: 'CONTROL_NET', type: 'CONTROL_NET', links: [], shape: 3 }
      ],
      properties: { 'Node name for S&R': 'ControlNetLoader' },
      widgets_values: ['control_v11f1p_sd15_depth.pth']
    },
    {
      type: 'ControlNetLoader',
      pos: [1206.5130480797907, -20.056290787682087],
      size: { 0: 415.221923828125, 1: 58.84859848022461 },
      flags: {},
      order: 3,
      mode: 0,
      outputs: [
        { name: 'CONTROL_NET', type: 'CONTROL_NET', links: [], shape: 3 }
      ],
      properties: { 'Node name for S&R': 'ControlNetLoader' },
      widgets_values: ['t2iadapter_seg-fp16.safetensors']
    },
    {
      type: 'ControlNetLoader',
      pos: [1209.5130480797907, 125.94370921231791],
      size: { 0: 415.221923828125, 1: 58.84859848022461 },
      flags: {},
      order: 4,
      mode: 0,
      outputs: [
        { name: 'CONTROL_NET', type: 'CONTROL_NET', links: [], shape: 3 }
      ],
      properties: { 'Node name for S&R': 'ControlNetLoader' },
      widgets_values: ['control_v11p_sd15_openpose.pth']
    },
    {
      type: 'ControlNetLoader',
      pos: [1214.5130480797907, 293.9437092123179],
      size: { 0: 415.221923828125, 1: 58.84859848022461 },
      flags: {},
      order: 5,
      mode: 0,
      outputs: [
        { name: 'CONTROL_NET', type: 'CONTROL_NET', links: [], shape: 3 }
      ],
      properties: { 'Node name for S&R': 'ControlNetLoader' },
      widgets_values: ['control_v11e_sd15_ip2p.safetensors']
    },
    {
      type: 'ControlNetLoader',
      pos: [1212.5130480797907, 461.9437092123179],
      size: { 0: 415.221923828125, 1: 58.84859848022461 },
      flags: {},
      order: 6,
      mode: 0,
      outputs: [
        { name: 'CONTROL_NET', type: 'CONTROL_NET', links: [], shape: 3 }
      ],
      properties: { 'Node name for S&R': 'ControlNetLoader' },
      widgets_values: ['control_v11p_sd15_inpaint.pth']
    },
    {
      type: 'ControlNetLoader',
      pos: [1216.5130480797907, 636.9437092123179],
      size: { 0: 415.221923828125, 1: 58.84859848022461 },
      flags: {},
      order: 7,
      mode: 0,
      outputs: [
        { name: 'CONTROL_NET', type: 'CONTROL_NET', links: [], shape: 3 }
      ],
      properties: { 'Node name for S&R': 'ControlNetLoader' },
      widgets_values: ['control_v11f1e_sd15_tile.bin']
    },
    {
      type: 'ControlNetLoader',
      pos: [1227.5130480797907, 804.9437092123179],
      size: { 0: 415.221923828125, 1: 58.84859848022461 },
      flags: {},
      order: 8,
      mode: 0,
      outputs: [
        { name: 'CONTROL_NET', type: 'CONTROL_NET', links: [], shape: 3 }
      ],
      properties: { 'Node name for S&R': 'ControlNetLoader' },
      widgets_values: ['control_v11f1e_sd15_tile.bin']
    },
    {
      type: 'ControlNetApplyAdvanced',
      pos: [1816, 94],
      size: { 0: 315, 1: 166 },
      flags: {},
      order: 9,
      mode: 0,
      inputs: [
        { name: 'positive', type: 'CONDITIONING', link: null },
        { name: 'negative', type: 'CONDITIONING', link: null },
        { name: 'control_net', type: 'CONTROL_NET', link: null, slot_index: 2 },
        { name: 'image', type: 'IMAGE', link: null, slot_index: 3 }
      ],
      outputs: [
        { name: 'positive', type: 'CONDITIONING', links: null, shape: 3 },
        { name: 'negative', type: 'CONDITIONING', links: null, shape: 3 }
      ],
      properties: { 'Node name for S&R': 'ControlNetApplyAdvanced' },
      widgets_values: [1, 0, 1]
    }
  ],
  links: [
    [1, 0, 9, 2, 2],
    [0, 0, 9, 3, 1]
  ]
}

// 添加workflow
const importWorkflow = my => {
  localStorage.setItem('litegrapheditor_clipboard', JSON.stringify(my))
  app.canvas.pasteFromClipboard()
}

function getURLParameters (url) {
  var params = {}
  var paramStr = url.split('?')[1]
  if (paramStr) {
    var paramArr = paramStr.split('&')
    for (var i = 0; i < paramArr.length; i++) {
      var param = paramArr[i].split('=')
      var paramName = decodeURIComponent(param[0])
      var paramValue = decodeURIComponent(param[1] || '')
      if (paramName) {
        if (params[paramName]) {
          params[paramName] = Array.isArray(params[paramName])
            ? [...params[paramName], paramValue]
            : [params[paramName], paramValue]
        } else {
          params[paramName] = paramValue
        }
      }
    }
  }
  return params
}

// var url = "http://example.com/page?param1=value1&param2=value2&param3=value3";
// var params = getURLParameters(url);
// console.log(params);

const node = {
  name: 'RandomPrompt',
  async init (app) {
    // Any initial setup to run as soon as the page loads
    // console.log('[logging]', 'extension init')

    if (window.location.href.match('/?')) {
      const { workflow } = getURLParameters(window.location.href)
      if (workflow)
        get_my_workflow().then(data => {
          // console.log('#get_my_workflow', data)
          let my_workflow = data.filter(
            d => d.filename == 'my_workflow.json'
          )[0]
          if (my_workflow?.data) {
            // app.loadGraphData(my_workflow.data)
            localStorage.setItem('workflow', JSON.stringify(my_workflow.data))
          }
        })
    }
  },
  async setup (a) {
    for (const node of app.graph._nodes) {
      // console.log('#setup', node)
      if (node.type === 'RandomPrompt') {
        updateUI(node)
      }
    }
    // console.log('[logging]', 'loaded graph node: ', exportGraph(app.graph))
  },
  addCustomNodeDefs (defs, app) {
    // console.log(
    //   '[logging]',
    //   'add custom node definitions',
    //   'current nodes:',
    //   defs
    // )
    // 在这里进行 语言切换
    // for (const nodeName in defs) {
    //   if (nodeName === 'RandomPrompt') {
    //     // defs[nodeName].category
    //     // defs[nodeName].display_name
    //   }
    // }
  },
  loadedGraphNode (node, app) {
    if (node.type === 'RandomPrompt') {
      try {
        let max_count = node.widgets.filter(w => w.name === 'max_count')[0]
        max_count.value = node.widgets_values[0]
        // console.log('RandomPrompt',max_count,node.widgets_values[0])
      } catch (error) {
        console.log(error)
      }
    }
  },
  async nodeCreated (node) {
    if (node.type === 'RandomPrompt') {
      updateUI(node)
    }

    if (node.type === 'RunWorkflow') {
      const pw = node.widgets.filter(w => w.name === 'workflow')[0]
      console.log('nodeCreated', pw)
      // if (pw) {
      // 	// node.widgets[pos].onRemove?.();
      //   pw.value = prompts.join('\n\n');
      //   // pw.inputEl=document.createElement('input');
      // }

      //  node.widgets.length = 1;
      node.onResize?.(node.size)
    }
  },
  async beforeRegisterNodeDef (nodeType, nodeData, app) {
    // 注册节点前，可以修改节点的数据
    // 可以获取得到其他节点数据

    // 汉化
    // app.graph._nodes // title ='123'

    // if (nodeData.name === 'SaveTransparentImage') {
    //   const onExecuted = nodeType.prototype.onExecuted
    //   nodeType.prototype.onExecuted = function (message) {
    //     const r = onExecuted?.apply?.(this, arguments)
    //     console.log('executed', message)
    //     const { image_path } = message
    //     if (image_path) {
    //     }
    //     return r
    //   }
    // }

    if (nodeData.name === 'WSServer') {
      // Create the button widget for selecting the files
      // node.addWidget(
      //   'button',
      //   'choose file to upload',
      //   'video',
      //   () => {
      //     console.log('click')
      //   }
      // )
      // uploadWidget.serialize = false
      // const onExecuted = nodeType.prototype.onExecuted
      // nodeType.prototype.onExecuted = function (message) {
      //   const r = onExecuted?.apply?.(this, arguments)
      //   console.log('executed', message)
      //   const upload = this.widgets.filter(w => w.name === 'upload')[0]
      //   console.log('executed', this.widgets)
      //   // navigator.mediaDevices
      //   //   .getDisplayMedia({ video: true })
      //   //   .then(stream => {
      //   //     const videoElement = document.createElement('video')
      //   //     videoElement.srcObject = stream
      //   //     videoElement.autoplay = true
      //   //     const canvasElement = document.createElement('canvas')
      //   //     const context = canvasElement.getContext('2d')
      //   //     videoElement.addEventListener('loadedmetadata', () => {
      //   //       canvasElement.width = videoElement.videoWidth
      //   //       canvasElement.height = videoElement.videoHeight
      //   //       setInterval(async () => {
      //   //         context.drawImage(
      //   //           videoElement,
      //   //           0,
      //   //           0,
      //   //           canvasElement.width,
      //   //           canvasElement.height
      //   //         )
      //   //         const imageData = canvasElement.toDataURL()
      //   //         upload.value = await uploadScreenshot(imageData)
      //   //       }, 200)
      //   //     })
      //   //   })
      //   //   .catch(error => {
      //   //     console.error('Error getting screen share:', error)
      //   //   })
      //   return r
      // }
    }

    if (nodeData.name === 'RandomPrompt') {
      const onExecuted = nodeType.prototype.onExecuted
      nodeType.prototype.onExecuted = function (message) {
        const r = onExecuted?.apply?.(this, arguments)

        let prompts = message.prompts
        // console.log('executed', message)
        // console.log('#RandomPrompt', this.widgets)
        const pw = this.widgets.filter(w => w.name === 'prompts')[0]

        if (pw) {
          // node.widgets[pos].onRemove?.();
          pw.value = prompts.join('\n\n')
          pw.inputEl.title = `Total of ${prompts.length} prompts`
        } else {
          // 动态添加
          const w = ComfyWidgets.STRING(
            this,
            'prompts',
            ['STRING', { multiline: true }],
            app
          ).widget
          w.inputEl.readOnly = true
          w.inputEl.style.opacity = 0.6
          w.value = prompts.join('\n\n')
          w.inputEl.title = `Total of ${prompts.length} prompts`
        }

        this.widgets.length = 5

        this.onResize?.(this.size)

        return r
      }
    }
  }
}

app.registerExtension(node)
