<template>
  <view class="w-qr-code">
    <view :class="{ 'show': !isPainted }" class="w-qr-code-loading"></view>
    <image :class="{ 'show': imageUrl && isPainted }" :src="imageUrl" class="w-qr-code-image" />
    <canvas :id="canvasId" :canvas-id="canvasId" class="w-qr-code-canvas"
      :style="{ width: size + 'px', height: size + 'px' }" />
  </view>
</template>

<script setup>
import {qrcode } from "./qrcode"
import { ref, computed, getCurrentInstance, nextTick } from 'vue';
import { onLoad } from '@dcloudio/uni-app'



const { proxy } = getCurrentInstance();

const emit = defineEmits(['end','error'])

let _ctx = undefined;
let qrCode = undefined;
const canvasId = `w_${Math.round(Math.random() * 1000000).toString(36)}`;

const imageUrl = ref("");
const isPainted = ref(false);

const props = defineProps({
  text: {
    type: String,
    default: "hello world"
  },
  typeNumber:{
    type:Number,
    default: 0
  },
  size: {
    type: Number,
    default: 512
  },
  bgColor: {
    type: String,
    default: "#FFFFFF"
  },
  color: {
    type: String,
    default: "#000000"
  },
  type: {
    type: String,
    default: "png"
  },
  level: {
    type: String,
    default: "H"
  },
  logo: String
});


const qrLevel = computed(() => {
  return props.level.includes('LMQH') ? props.level : 'H';
});


const clear = () => {
  _ctx.clearRect(0, 0, props.size, props.size)
  imageUrl.value = "";
}

const getImagePath = (src) => {
  return new Promise((resolve, reject) => {
    uni.getImageInfo({
      src,
      success(res) {
        resolve(res.path);
      },
      fail(e) {
        reject(e);
      }
    })
  })
}

const drawLogo = async () => {
  if (!props.logo) {
    return;
  }
  try {
    const img = await getImagePath(props.logo)
    const _size = props.size;
    const logoSize = _size / 4;
    const logoX = _size / 2 - logoSize / 2;
    const logoY = _size / 2 - logoSize / 2;
    const logoBorderSize = logoSize + _size / 20;
    const logoBorderX = _size / 2 - logoBorderSize / 2;
    const logoBorderY = _size / 2 - logoBorderSize / 2;
    const borderRadius = _size / 40;
    fillRoundRect(borderRadius, logoBorderX, logoBorderY, logoBorderSize, logoBorderSize)
    _ctx.drawImage(img, logoX, logoY, logoSize, logoSize)
  }catch(e){
    emit('error',e);
  }
}

const fillRoundRect = (r, x, y, w, h) => {
  _ctx.save()
  _ctx.translate(x, y)
  _ctx.beginPath()
  _ctx.arc(w - r, h - r, r, 0, Math.PI / 2)
  _ctx.lineTo(r, h)
  _ctx.arc(r, h - r, r, Math.PI / 2, Math.PI)
  _ctx.lineTo(0, r)
  _ctx.arc(r, r, r, Math.PI, Math.PI * 3 / 2)
  _ctx.lineTo(w - r, 0)
  _ctx.arc(w - r, r, r, Math.PI * 3 / 2, Math.PI * 2)
  _ctx.lineTo(w, h - r)
  _ctx.closePath()
  _ctx.setFillStyle(props.bgColor)
  _ctx.fill()
  _ctx.restore()
}


const draw = async () => {
  const nCount = qrCode.getModuleCount();
  const _size = props.size;
  const nWidth = _size / nCount;
  const nHeight = _size / nCount;
  const nRoundedWidth = Math.round(nWidth);
  const nRoundedHeight = Math.round(nHeight);
  let color = props.color
  let bgColor = props.bgColor
  clear();
  _ctx.setFillStyle(bgColor);
  _ctx.fillRect(0, 0, props.size, props.size);
  for (let row = 0; row < nCount; row++) {
    for (let col = 0; col < nCount; col++) {
      let bIsDark = qrCode.isDark(row, col);
      let nLeft = col * nWidth;
      let nTop = row * nHeight;
      _ctx.setStrokeStyle(bIsDark ? color : bgColor)
      _ctx.lineWidth = 1
      _ctx.setFillStyle(bIsDark ? color : bgColor)
      _ctx.fillRect(nLeft, nTop, nWidth, nHeight)
      _ctx.strokeRect(
        Math.floor(nLeft) + 0.5,
        Math.floor(nTop) + 0.5,
        nRoundedWidth,
        nRoundedHeight
      )
      _ctx.strokeRect(
        Math.ceil(nLeft) - 0.5,
        Math.ceil(nTop) - 0.5,
        nRoundedWidth,
        nRoundedHeight
      )
    }
  }

  await drawLogo();

  _ctx.draw(true, () => {
    setTimeout(() => { isPainted.value = true; }, 1000);
    // #ifndef MP-ALIPAY
    uni.canvasToTempFilePath({
      canvasId: canvasId,
      fileType: props.type,
      width: props.size,
      height: props.size,
      destWidth: props.size,
      destHeight: props.size,
      success: res => {
        imageUrl.value = res.tempFilePath
        emit('end', res.tempFilePath);
      }
    }, proxy);
    // #endif
    // #ifdef MP-ALIPAY
    _ctx.toTempFilePath({
      fileType: props.type,
      width: props.size,
      height: props.size,
      destWidth: props.size,
      destHeight: props.size,
      success: res => {
        this.imageUrl = res.apFilePath
        imageUrl.value = res.apFilePath
        emit('end', res.apFilePath);
      }
    })
    // #endif
  });

}


const make = async () => {
  qrCode = new qrcode(props.typeNumber, qrLevel.value);
  qrCode.addData(props.text);
  qrCode.make();
  draw();
}


const init = async () => {
  await nextTick();
  _ctx = uni.createCanvasContext(canvasId, proxy);
  make();
}


onLoad(init);

const repaint = async() => {
  isPainted.value = false;
  nextTick();
  init();
}

defineExpose({ repaint });

</script>

<style lang="scss" scoped>
.w-qr-code {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-size: var(--font-size, 1rem);
  color: var(--color, currentColor);
  width: 10em;
  height: 10em;
  position: relative;

  &>view,
  &-image {
    width: 100%;
    height: 100%;
    position: absolute;
    transition: opacity 1s;
    opacity: 0;

    &.show {
      opacity: 1;
    }
  }

  &-loading {
    display: flex;
    justify-content: center;
    align-items: center;

    &::before {
      content: '';
      display: block;
      width: 2.5em;
      height: 2.5em;
      box-sizing: border-box;
      border: .2em solid var(--loading-bg, #0001);
      border-top-color: currentColor;
      border-radius: 4em;
      margin-bottom: .3em;
      animation: am-qr-code-loading 1s ease-in-out .1s infinite;
    }

    @keyframes am-qr-code-loading {
      from {
        transform: rotate(0);
      }

      to {
        transform: rotate(360deg);
      }
    }
  }

  &-canvas {
    position: fixed;
    visibility: hidden;
    z-index: -999;
  }
}
</style>
