<template>
  <el-container>
    <el-main>
      <el-row :gutter="5">
        <el-col :span="2">
          <div style="height: 30px"></div>
          <div style="padding: 4px">
            <el-checkbox v-model="clearWhenChangeType">
              <div>更换类型时</div>
              <div>清空文本</div>
            </el-checkbox>
          </div>
          <div style="height: 10px"></div>
          <div style="padding: 4px">
            <el-radio v-model="type" label="JSON" @change="onTypeChange">JSON</el-radio>
          </div>
          <div style="padding: 4px">
            <el-radio v-model="type" label="XML" @change="onTypeChange">XML</el-radio>
          </div>
        </el-col>
        <el-col :span="8">
          <pre :style="preStyle" class="formatted-body">
              <el-input v-model="originContent" type="textarea" rows="30" :placeholder="placeholder"
                        @input="executeFormat"></el-input>
          </pre>
        </el-col>
        <el-col :span="14">
          <pre :style="[preStyle, resultBackGround]" class="formatted-result">{{ formattedResult }}</pre>
        </el-col>
      </el-row>
    </el-main>
  </el-container>
</template>

<script>
import {computed, ref} from 'vue'
import {useStore} from 'vuex'

export default {
  setup() {
    const store = useStore()
    const windowSize = store.state.app.windowSize

    const clearWhenChangeType = ref(false)
    const type = ref('JSON')

    const placeholder = computed(() => {
      return `把要格式化的${type.value}字符串放在这里...`
    })

    const preStyle = {
      width: '92%',
      height: windowSize.h - 50 + 'px',
      overflowY: 'auto',
    }

    const onTypeChange = () => {
      if (clearWhenChangeType.value) {
        originContent.value = null
        formattedResult.value = null
      } else {
        executeFormat()
      }
    }

    const originContent = ref(null)
    const formattedResult = ref(null)
    const trueJson = ref(true)
    const resultBackGround = computed(() => {
      return {
        background: trueJson.value ? '#90da6b7a' : '#f3818186',
      }
    })

    const formatXml = () => {
      let text = originContent.value
      text =
          '\n' +
          text
              .replace(/(<\w+)(\s.*?>)/g, function ($0, name, props) {
                return name + ' ' + props.replace(/\s+(\w+=)/g, ' $1')
              })
              .replace(/>\s*?</g, '>\n<')
      //   text = text
      //     .replace(/\n/g, '\r')
      //     .replace(/<!--(.+?)-->/g, '<!--' + escape(text) + '-->')
      //     .replace(/\r/g, '\n')
      var rgx = /\n(<(([^\?]).+?)(?:\s|\s*?>|\s*?(\/)>)(?:.*?(?:(?:(\/)>)|(?:<(\/)\2>)))?)/gm
      var nodeStack = []
      var output = text.replace(rgx, function ($0, all, name, isBegin, isCloseFull1, isCloseFull2, isFull1, isFull2) {
        var isClosed = isCloseFull1 == '/' || isCloseFull2 == '/' || isFull1 == '/' || isFull2 == '/'
        var prefix = ''
        if (isBegin == '!') {
          //!开头
          prefix = setPrefix(nodeStack.length)
        } else {
          if (isBegin != '/') {
            ///开头
            prefix = setPrefix(nodeStack.length)
            if (!isClosed) {
              //非关闭标签
              nodeStack.push(name)
            }
          } else {
            nodeStack.pop() //弹栈
            prefix = setPrefix(nodeStack.length)
          }
        }
        var ret = '\n' + prefix + all
        return ret
      })
      var outputText = output.substring(1)
      //还原注释内容
      outputText = outputText.replace(/\n/g, '\r').replace(/(\s*)<!--(.+?)-->/g, function ($0, prefix, text) {
        if (prefix.charAt(0) == '\r') prefix = prefix.substring(1)
        text = unescape(text).replace(/\r/g, '\n')
        var ret = '\n' + prefix + '<!--' + text.replace(/^\s*/gm, prefix) + '-->'
        return ret
      })
      outputText = outputText.replace(/\s+$/g, '').replace(/\r/g, '\r\n')
      return outputText
    }

    //计算头函数 用来缩进
    const setPrefix = (prefixIndex) => {
      var result = ''
      var span = '   ' //缩进长度
      var output = []
      for (var i = 0; i < prefixIndex; ++i) {
        output.push(span)
      }
      result = output.join('')
      return result
    }

    const executeFormat = () => {
      if (!originContent.value) {
        return
      }
      try {
        if (type.value === 'JSON') {
          formattedResult.value = JSON.stringify(JSON.parse(originContent.value), null, 2)
          console.log(formattedResult.value)
          trueJson.value = true
        } else {
          if (originContent.value.indexOf('<') === -1 || originContent.value.indexOf('</') === -1 || originContent.value.indexOf('>') === -1) {
            formattedResult.value = '不正确的XML文本'
            trueJson.value = false
          } else {
            formattedResult.value = formatXml()
            trueJson.value = true
          }
        }
      } catch (error) {
        formattedResult.value = error
        trueJson.value = false
      }
    }

    return {
      type,
      preStyle,
      placeholder,
      clearWhenChangeType,
      originContent,
      formattedResult,
      resultBackGround,
      onTypeChange,
      executeFormat,
    }
  },
}
</script>

<style scoped>
pre {
  white-space: pre-wrap;
  word-wrap: break-word;
}

.formatted-body {
  background: #60a6ec8a;
  border-radius: 8px;
  padding: 10px;
}

.formatted-result {
  border-radius: 8px;
  padding: 10px;
}
</style>
