import './index.less'
import 'highlight.js/styles/atom-one-dark.css'
import $ from 'jquery'
import hljs from 'highlight.js/lib/core'
import javascript from 'highlight.js/lib/languages/javascript'
import vbscript from 'highlight.js/lib/languages/vbscript'
import python from 'highlight.js/lib/languages/python'
import matlab from 'highlight.js/lib/languages/matlab'
import csharp from 'highlight.js/lib/languages/csharp'
import shell from 'highlight.js/lib/languages/shell'
import vhdl from 'highlight.js/lib/languages/vhdl'
import java from 'highlight.js/lib/languages/java'
import css from 'highlight.js/lib/languages/css'
import xml from 'highlight.js/lib/languages/xml'
import sql from 'highlight.js/lib/languages/sql'
import cpp from 'highlight.js/lib/languages/cpp'
import c from 'highlight.js/lib/languages/c'
import ClipboardJS from 'clipboard'

hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('vbscript', vbscript)
hljs.registerLanguage('python', python)
hljs.registerLanguage('matlab', matlab)
hljs.registerLanguage('csharp', csharp)
hljs.registerLanguage('shell', shell)
hljs.registerLanguage('vhdl', vhdl)
hljs.registerLanguage('java', java)
hljs.registerLanguage('html', xml)
hljs.registerLanguage('xml', xml)
hljs.registerLanguage('css', css)
hljs.registerLanguage('sql', sql)
hljs.registerLanguage('cpp', cpp)
hljs.registerLanguage('c', c)

hljs.configure({ ignoreUnescapedHTML: true })

/**
 * 高亮代码块
 * @param {HTMLElement} element 包含 pre code 代码块的元素
 */
function highlightCode(element: HTMLElement): void {
  const codeEls = element.querySelectorAll('pre code')
  codeEls.forEach(el => {
    hljs.highlightElement(el as HTMLElement)
  })
}

/**
 * 给代码块添加复制按钮
 * @param {HTMLElement} element 包含 pre code 代码块的元素
 */
function buildCopyButton(element: HTMLElement): void {
  const $pres = $(element).find('pre')
  if (!$pres.length) return

  $pres.each(function (this: HTMLElement) {
    const t = $(this).children('code').text()

    // 创建按钮
    const btn = $('<span class="copy">复制</span>').attr('data-clipboard-text', t)

    $(this).prepend(btn)

    const c = new ClipboardJS(btn[0] as HTMLElement)
    c.on('success', function () {
      btn.addClass('copyed').text('复制成功')
      setTimeout(function () {
        btn.text('复制').removeClass('copyed')
      }, 1000)
    })
    c.on('error', function () {
      btn.text('复制失败')
    })
  })
}

/** 构建生成中的 markdown 的内容 */
export function buildCodeBlock(element: HTMLElement): void {
  highlightCode(element)
  buildCopyButton(element)
}

/** 核心函数, 对比节点的内容 实现动态更新 markdown 的 div 而不是用 innerHTML 的属性全部刷新 */
export function deepCloneAndUpdate(div1: HTMLElement, div2: HTMLElement): void {
  // 递归比较和更新 div1 和 div2 的子节点
  function compareAndUpdate(node1: Node, node2: Node): void {
    // 情况 1：node1 是文本节点，更新文本内容
    if (node1.nodeType === Node.TEXT_NODE && node2.nodeType === Node.TEXT_NODE) {
      if (node1.nodeValue !== node2.nodeValue) {
        // 更新文本内容
        node1.nodeValue = node2.nodeValue
      }
      return
    }

    // 情况 2：node1 和 node2 的标签名不同，替换整个节点
    if (node1.nodeType !== node2.nodeType || (node1 as HTMLElement).tagName !== (node2 as HTMLElement).tagName) {
      const newNode = node2.cloneNode(true)
      if (node1.parentNode) {
        node1.parentNode.replaceChild(newNode, node1)
      }
      return
    }

    const el1 = node1 as HTMLElement
    const el2 = node2 as HTMLElement

    // 情况 3：节点的 class 或其他属性更新, 注意对root节点的保护
    if (el1.className !== 'article-content' && el1.className !== el2.className) {
      el1.className = el2.className
    }

    if (el1.id !== 'article-content' && el1.id !== el2.id) {
      el1.id = el2.id
    }

    if (el1.style.cssText !== el2.style.cssText) {
      el1.style.cssText = el2.style.cssText
    }

    // 情况 4：递归对比和更新子节点
    const children1 = Array.from(el1.childNodes)
    const children2 = Array.from(el2.childNodes)

    children2.forEach((child2, index) => {
      const child1 = children1[index]
      if (!child1) {
        const newChild = child2.cloneNode(true)
        el1.appendChild(newChild)
      } else {
        compareAndUpdate(child1, child2)
      }
    })

    if (children1.length > children2.length) {
      for (let i = children2.length; i < children1.length; i++) {
        el1.removeChild(children1[i])
      }
    }
  }

  compareAndUpdate(div1, div2)
}
