<template>
  <v-app>
    <v-app-bar app dark color="primary">
      <v-toolbar-title>{{i18n.title}}</v-toolbar-title>
      <v-spacer></v-spacer>
      <v-select style="max-width: 10rem" v-model="locale_name" @change="set_locale(locale_name)" :items="['English','Chinese']" prepend-icon="mdi-web" hide-details></v-select>
    </v-app-bar>
    <v-main>
      <v-row class="mx-3">
        <div class="col-12 offset-0 col-sm-8 offset-sm-2  col-md-8 offset-md-2 col-lg-6 offset-lg-3 col-xl-6 offset-xl-3 mt-8">
          <v-textarea
              v-model="prompt_title"
              :label="i18n.input_title_label"
              :disabled="loading"
              rows="1"
              prepend-icon="mdi-comment"
              :hint="i18n.input_title_hint"
              no-resize
          ></v-textarea>
          <v-textarea
              v-model="prompt_text"
              :label="i18n.input_text_label"
              :disabled="loading"
              rows="2"
              prepend-icon="mdi-comment"
              :hint="i18n.input_text_hint"
              no-resize
          ></v-textarea>
          <v-row>
            <v-checkbox class="col-6" v-model="show_romaji" :disabled="loading" @change="show_type=show_romaji?0:1"  :label="i18n.show_romaji"></v-checkbox>
            <v-scroll-x-transition>
              <v-select class="col-6" v-if="show_romaji" v-model="romaji_system" :disabled="loading" :items="romaji_systems" :hint="i18n.romaji_system"></v-select>
            </v-scroll-x-transition>
          </v-row>
          <v-row class="mt-n8">
            <v-checkbox class="col-6" v-model="use_trans" :disabled="loading" @change="show_type=use_trans?1:0"  :label="i18n.use_translate"></v-checkbox>
            <v-scroll-x-transition>
              <v-select class="col-6" v-if="use_trans" v-model="trans_target_lang" :disabled="loading" :items="google_supported_languages" :hint="i18n.target_lang"></v-select>
            </v-scroll-x-transition>
          </v-row>
          <v-btn color="primary" class="mb-10" elevation="5" @click="do_gen()" :loading="loading" text block large tile>{{i18n.generate}}</v-btn>
          <v-expand-transition>
            <v-simple-table v-if="lyric.length !== 0" class="py-5" dense>
              <template v-slot:default>
                <thead>
                <tr>
                  <th>{{i18n.lyric}}<v-btn class="ml-2" elevation="0" @click="copy_lyric('s')" fab x-small><v-icon>mdi-content-copy</v-icon></v-btn></th>
                  <v-scroll-x-transition>
                    <th v-if="use_trans||show_romaji">
                      <span v-if="show_romaji&&!use_trans">{{i18n.romaji}}</span>
                      <span v-if="!show_romaji&&use_trans">{{i18n.translation}}</span>
                      <v-btn v-if="use_trans&&show_romaji" class="mr-n2"  elevation="0" @click="show_type=show_type===0?1:0" text small>
                        <span v-if="show_romaji" :class="{'grey--text':show_type===1}">{{i18n.romaji_s}}</span>/<span v-if="use_trans" :class="{'grey--text':show_type===0}">{{i18n.translation_s}}</span>
                      </v-btn>
                      <v-btn class="ml-2" elevation="0" @click="copy_lyric(show_type===0?'r':'t')" fab x-small><v-icon>mdi-content-copy</v-icon></v-btn>
                    </th>
                  </v-scroll-x-transition>
                </tr>
                </thead>
                <tbody style="word-break: break-word; word-wrap:break-word;">
                  <tr v-for="(item, idx) in lyric" :key="idx" @mouseenter="hover=idx" @mouseleave="hover=-1">
                    <td :class="{'text-h6': idx===0, 'text-center': !item.is_lyric}">
                      {{item.s}}
                      <v-scroll-x-transition v-if="tts_support">
                        <v-btn v-if="!tts_loading&&item.s!==''&&(speaking===idx||(speaking===-1&&hover===idx))" :class="{'mr-n9':true,'blue--text':speaking===idx}" icon small @click="ja_tts(idx)">
                          <v-icon>mdi-volume-high</v-icon>
                        </v-btn>
                      </v-scroll-x-transition >
                      <v-progress-circular v-if="tts_support&&speaking===idx&&tts_loading"  class="mr-n9" indeterminate size="20" width="2" color="primary"></v-progress-circular>
                    </td>
                    <v-scroll-x-transition>
                      <td v-if="use_trans||show_romaji" :class="{'text-center': !item.is_lyric}">{{show_type===0?item.r:item.t}}</td>
                    </v-scroll-x-transition>
                  </tr>
                </tbody>
              </template>
            </v-simple-table>
          </v-expand-transition>
        </div>
      </v-row>
      <v-snackbar dark v-model="snackbar" :timeout="2000">
        {{ snackbar_msg }}
        <template v-slot:action="{ attrs }">
          <v-btn color="blue" text v-bind="attrs" @click="snackbar = false">
            Close
          </v-btn>
        </template>
      </v-snackbar>
    </v-main>
    <v-footer>
      <v-row class="ma-0">
        <div class="text-center caption col-12 pa-0">{{i18n.footer}}</div>
        <div class="text-center caption col-12 pa-0">© 2022 SkyTNT</div>
      </v-row>
    </v-footer>
  </v-app>
</template>

<style lang="scss">
</style>
<script>
import axios from 'axios'

if (!String.prototype.trim) {
  String.prototype.trim = function () {
    return this.triml().trimr();
  }
  String.prototype.triml = function () {
    return this.replace(/^[\s\n\t]+/g, "");
  }
  String.prototype.trimr = function () {
    return this.replace(/[\s\n\t]+$/g, "");
  }
}

function isMobile(){
  return /(iPhone|iPad|iPod|iOS|Android|Windows Phone)/i.test(navigator.userAgent);
}

export default {
  name: "App",
  data:()=>({
    locale_name: "English",
    i18n:{},
    use_trans: false,
    google_supported_languages:['af', 'sq', 'am', 'ar', 'hy', 'az', 'eu', 'be', 'bn', 'bs', 'bg', 'my', 'ca', 'ca', 'ceb', 'co', 'cs', 'da', 'nl', 'nl', 'en', 'eo', 'et', 'fi', 'fr', 'fy', 'ka', 'de', 'gd', 'gd', 'ga', 'gl', 'el', 'gu', 'ht', 'ht', 'ha', 'haw', 'he', 'hi', 'hr', 'hu', 'ig', 'is', 'id', 'it', 'jw', 'ja', 'kn', 'kk', 'km', 'ky', 'ky', 'ko', 'ku', 'lo', 'la', 'lv', 'lt', 'lb', 'lb', 'mk', 'ml', 'mi', 'mr', 'ms', 'mg', 'mt', 'mn', 'ne', 'no', 'ny', 'ny', 'ny', 'or', 'pa', 'pa', 'fa', 'pl', 'pt', 'ps', 'ps', 'ro', 'ro', 'ro', 'ru', 'si', 'si', 'sk', 'sl', 'sm', 'sn', 'sd', 'so', 'st', 'es', 'es', 'sr', 'su', 'sw', 'sv', 'ta', 'te', 'tg', 'tl', 'th', 'tr', 'ug', 'ug', 'uk', 'ur', 'uz', 'vi', 'cy', 'xh', 'yi', 'yo', 'zu', 'zh-CN', 'zh-TW'],
    trans_target_lang:"zh-CN",
    show_romaji:false,
    show_type:0,
    romaji_systems:["hepburn", "kunrei", "nippon"],
    romaji_system:"hepburn",
    prompt_title:"桜",
    prompt_text:"",
    loading:false,
    lyric:[],
    tts_cache:{},//tts缓存
    tts_loading:false,
    tts_support:false,
    speaking:-1,//正在tts的行数
    hover:-1,//鼠标悬停的行数
    snackbar:false,
    snackbar_msg:""
  }),
  mounted() {
    this.locale_name = (navigator.language ||navigator.userLanguage)==="zh-CN"?"Chinese":"English"
    if(this.locale_name==="English")
      this.trans_target_lang="en";
    this.set_locale(this.locale_name);
    this.loads();
  },
  methods:{
    set_locale(name){
      let locale = name==="English"?"en":"zh";
      let langs={
        en:{
          title:"Japanese Lyric Generator",
          loading:"Loading",
          input_title_label:"Title (can be empty)",
          input_text_label:"Beginning (can be empty)",
          input_title_hint:"Generate lyric for a given title",
          input_text_hint:"The generator continues with this text",
          show_romaji:"Show romaji",
          romaji_system:"Romaji system",
          use_translate:"Translate",
          target_lang:"Target language",
          generate:"Generate!",
          lyric:"Lyric",
          romaji:"Romaji",
          romaji_s:"R",
          translation:"Translation",
          translation_s:"T",
          footer:"The generated content can be used as you like, please share this site if you like.",
          copy_successful:"Copy successful",
          error:"Error",
          error_network:"Network error",
          error_failed_load_kuromoji:"Failed to load romaji converter",
          error_failed_covert_romaji:"Failed to convert lyric to romaji",
          error_failed_translate:"Failed to translate lyric",
          error_copy:"Copy failed, browser does not support"
        },
        zh:{
          title:"日语歌词生成器",
          loading:"加载中",
          input_title_label:"歌词标题(可不填)",
          input_text_label:"歌词开头(可不填)",
          input_title_hint:"给定标题生成歌词",
          input_text_hint:"生成器以这段文本进行续写",
          show_romaji:"显示罗马音",
          romaji_system:"罗马音系统",
          use_translate:"是否进行翻译",
          target_lang:"目标语言",
          generate:"生成!",
          lyric:"歌词",
          romaji:"罗马音",
          romaji_s:"音",
          translation:"翻译",
          translation_s:"译",
          footer:"生成的内容可以随意使用，喜欢的话就请分享本网站",
          copy_successful:"复制成功",
          error:"错误",
          error_network:"网络错误",
          error_failed_load_kuromoji:"罗马音转换器加载失败",
          error_failed_covert_romaji:"转换罗马音错误",
          error_failed_translate:"翻译出错",
          error_copy:"复制失败，浏览器不支持"
        }
      };
      this.i18n = langs[locale];
    },
    async loads(){
      if(!isMobile()){
        eval((await axios.get('https://l2d.fab.moe/js/autoload.js')).data);
      }
    },
    async do_gen(){
      let prompt_title = this.prompt_title.trim();
      let prompt_text = this.prompt_text.trim();
      this.loading = true;
      this.lyric = [];
      this.tts_cache = {};

      try {
        let result = await axios.post("gen",
            {title:prompt_title, text:prompt_text})
        let gen_text = `${result.data.title}\n\n${result.data.lyric}`
        let lines = gen_text.split('\n')
        for (let i in lines) {
          let line = lines[i].trim().replace(/ /g, "\u3000")
          this.lyric.push({s: line, r: "", t: "", is_lyric: line!=="---end---"})
        }
        this.lyric[0].is_lyric = false; //title

        try {
          let romajis = (await axios.post("romaji",{system:this.romaji_system, text:gen_text})).data.romaji
          romajis = romajis.split('\n')
          for (let i in romajis) {
            this.lyric[i].r = romajis[i]
          }
        }catch (e) {
          this.show_snackbar(this.i18n.error_failed_covert_romaji+"：" + e.message)
        }

        if (this.use_trans) {
          let trans = await this.google_translate(gen_text);
          if (trans !== "") {
            trans = trans.split("\n")
            for (let i in trans) {
              this.lyric[i].t = trans[i].trim().replace(/ /g, "\u3000")
            }
          }
        }
        this.loading = false;

      } catch (e) {
        console.log(e)
        this.show_snackbar(this.i18n.error+"：" + e.message)
        this.loading = false;
      }

    },
    async google_translate(text){
      let params = {client: "gtx", dt: "t", sl: "ja", tl: this.trans_target_lang, q: text};
      try {
        let result = await axios.get("https://translate.googleapis.com/translate_a/single",{
          params:params
        });
        let out = "";
        let lines = result.data[0];
        for(let i in lines)
          out += lines[i][0];
        return out;
      }catch(e){
        this.show_snackbar(this.i18n.error_failed_translate+":"+e.message);
      }
      return "";
    },
    async ja_tts(line){
      let text = this.lyric[line].s;
      if(this.speaking !== -1||text === ""){
        return;
      }

      this.speaking = line;
      try{
        let context = new AudioContext();
        let gain = context.createGain();
        gain.gain.value=2;
        if(this.tts_cache[text] === undefined){
          this.tts_loading=true;
          let audio_query = (await axios.post("https://voicevox-skytnt.cloud.okteto.net/audio_query",null,
              {params:{speaker:4,text:text}})).data;
          let wave = (await axios.post("https://voicevox-skytnt.cloud.okteto.net/synthesis",audio_query,
              {params:{speaker:4},responseType:"arraybuffer"})).data;
          this.tts_loading=false;
          this.tts_cache[text] = await context.decodeAudioData(wave);
        }
        let source = context.createBufferSource();
        source.buffer = this.tts_cache[text];
        source.loop = false;
        source.connect(gain);
        gain.connect(context.destination);
        await  new Promise((resolve) => {
          source.onended = () => {resolve()}
          source.start(0);//立即播放
        });
      }catch (e) {
        this.show_snackbar(this.i18n.error+"："+e.message)
      }
      this.speaking = -1;
      this.tts_loading=false;//防止网络出错导致tts_loading为true
    },
    copy_lyric(attr_name){
      let text = ""
      for(let i in this.lyric){
        text += this.lyric[i][attr_name] + "\n"
      }
      text = text.trim()
      this.copy(text)
    },
    async copy(text){
      try {
        await navigator.clipboard.writeText(text)
        this.show_snackbar(this.i18n.copy_successful)
      }catch (e) {
        let input = document.createElement('textarea')
        input.style.position = 'fixed'
        input.style.top = '-10000px'
        input.style.zIndex = '-999'
        document.body.appendChild(input)
        console.log('input', input)
        input.value = text
        input.focus()
        input.select()
        try {
          let result = document.execCommand('copy')
          if (!result || result === 'unsuccessful') {
            this.show_snackbar(this.i18n.error_copy)
          } else {
            this.show_snackbar(this.i18n.copy_successful)
          }
        } catch (e) {
          this.show_snackbar(this.i18n.error_copy)
        }finally {
          document.body.removeChild(input)
        }
      }
    },
    show_snackbar(msg){
      this.snackbar=true;
      this.snackbar_msg=msg;
    }
  }
}

</script>
