<template>
  <div class="login-container">
    <canvas id="canvas"></canvas>
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on"
             label-position="left">
      <h3 class="title">
        <span><img style="width:48px;margin-bottom:-5px;margin-right:2px" src="../../assets/logo.svg"></span>RAWLAB
      </h3>
      <el-form-item prop="username" style="margin-bottom: 28px;">
        <el-input
          v-model="loginForm.username"
          name="username"
          type="text"
          auto-complete="on"
          :placeholder="$t('Username')"
        />
      </el-form-item>
      <el-form-item prop="password" style="margin-bottom: 28px;">
        <el-input
          :type="pwdType"
          v-model="loginForm.password"
          name="password"
          auto-complete="on"
          :placeholder="$t('Password')"
          @keyup.enter.native="onKeyEnter"/>
      </el-form-item>
      <el-form-item v-if="isSignUp" prop="confirmPassword" style="margin-bottom: 28px;">
        <el-input
          :type="pwdType"
          v-model="loginForm.confirmPassword"
          name="password"
          auto-complete="on"
          :placeholder="$t('Confirm Password')"
          @keyup.enter.native="onKeyEnter"
        />
      </el-form-item>
      <el-form-item v-if="isSignUp" prop="email" style="margin-bottom: 28px;">
        <el-input
          v-model="loginForm.email"
          name="email"
          :placeholder="$t('Email')"
          @keyup.enter.native="onKeyEnter"
        />
      </el-form-item>
      <el-form-item style="border: none">
        <el-button v-if="isSignUp" :loading="loading" type="primary" style="width:100%;"
                   @click.native.prevent="handleSignup">
          {{$t('Sign up')}}
        </el-button>
        <el-button v-if="!isSignUp" :loading="loading" type="primary" style="width:100%;"
                   @click.native.prevent="handleLogin">
          {{$t('Sign in')}}
        </el-button>
      </el-form-item>
      <div class="alternatives">
        <div class="left">
          <span v-if="!isSignUp" class="forgot-password">{{$t('Forgot Password')}}</span>
        </div>
        <div class="right" v-if="setting.allow_register === 'Y'">
          <span v-if="isSignUp">{{$t('Has Account')}}, </span>
          <span v-if="isSignUp" class="sign-in" @click="$router.push('/login')">{{$t('Sign-in')}} ></span>
          <span v-if="!isSignUp">{{$t('New to Crawlab')}}, </span>
          <span v-if="!isSignUp" class="sign-up" @click="$router.push('/signup')">{{$t('Sign-up')}} ></span>
        </div>
      </div>
      <div class="tips">
        <span>{{$t('Initial Username/Password')}}: admin/admin</span>
        <a href="https://github.com/crawlab-team/crawlab" target="_blank" style="float:right">
          <img src="https://img.shields.io/github/stars/crawlab-team/crawlab?logo=github">
        </a>
      </div>
      <div class="lang">
        <span @click="setLang('zh')" :class="lang==='zh'?'active':''">中文</span>
        |
        <span @click="setLang('en')" :class="lang==='en'?'active':''">English</span>
      </div>
      <div class="documentation">
        <a href="http://docs.crawlab.cn" target="_blank">{{$t('Documentation')}}</a>
      </div>
      <div v-if="isShowMobileWarning" class="mobile-warning">
        <el-alert type="error" :closable="false">
          {{$t('You are running on a mobile device, which is not optimized yet. Please try with a laptop or desktop.')}}
        </el-alert>
      </div>
    </el-form>
  </div>
</template>

<script>
import {
  mapState
} from 'vuex'
import { isValidUsername } from '../../utils/validate'

export default {
  name: 'Login',
  data () {
    const validateUsername = (rule, value, callback) => {
      if (!isValidUsername(value)) {
        callback(new Error(this.$t('Please enter the correct username')))
      } else {
        callback()
      }
    }
    const validatePass = (rule, value, callback) => {
      if (value.length < 5) {
        callback(new Error(this.$t('Password length should be no shorter than 5')))
      } else {
        callback()
      }
    }
    const validateConfirmPass = (rule, value, callback) => {
      if (!this.isSignUp) return callback()
      if (value !== this.loginForm.password) {
        callback(new Error(this.$t('Two passwords must be the same')))
      } else {
        callback()
      }
    }
    return {
      loginForm: {
        username: '',
        password: '',
        confirmPassword: '',
        email: ''
      },
      loginRules: {
        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
        password: [{ required: true, trigger: 'blur', validator: validatePass }],
        confirmPassword: [{ required: true, trigger: 'blur', validator: validateConfirmPass }]
      },
      loading: false,
      pwdType: 'password',
      isShowMobileWarning: false
    }
  },
  computed: {
    ...mapState('setting', [
      'setting'
    ]),
    ...mapState('lang', [
      'lang'
    ]),
    isSignUp () {
      return this.$route.path === '/signup'
    },
    redirect () {
      return this.$route.query.redirect
    }
  },
  methods: {
    handleLogin () {
      this.$refs.loginForm.validate(async valid => {
        if (!valid) return
        this.loading = true
        const res = await this.$store.dispatch('user/login', this.loginForm)
        if (res.status === 200) {
          // success
          this.$router.push({ path: this.redirect || '/' })
          this.$st.sendEv('全局', '登录', '成功')
          await this.$store.dispatch('user/getInfo')
        } else if (res.message === 'Network Error' || !res.response) {
          // no response
          this.$message({
            type: 'error',
            message: this.$t('No response from the server. Please make sure your server is running correctly. You can also refer to the documentation to solve this issue.'),
            customClass: 'message-error',
            duration: 5000
          })
          this.$st.sendEv('全局', '登录', '服务器无响应')
        } else if (res.response.status === 401) {
          // incorrect username or password
          this.$message({
            type: 'error',
            message: '[401] ' + this.$t('Incorrect username or password')
          })
          this.$st.sendEv('全局', '登录', '用户名密码错误')
        } else {
          // other error
          this.$message({
            type: 'error',
            message: `[${res.response.status}] ${res.response.data.error}`,
            customClass: 'message-error'
          })
          this.$st.sendEv('全局', '登录', '其他错误')
        }
        this.loading = false
      })
    },
    handleSignup () {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          this.$store.dispatch('user/register', this.loginForm).then(() => {
            this.handleLogin()
            this.loading = false
            this.$st.sendEv('全局', '注册', '成功')
          }).catch(err => {
            this.$message.error(this.$t(err))
            this.loading = false
            this.$st.sendEv('全局', '注册', '失败')
          })
        }
      })
    },
    onKeyEnter () {
      const func = this.isSignUp ? this.handleSignup : this.handleLogin
      func()
    },
    setLang (lang) {
      window.localStorage.setItem('lang', lang)
      this.$set(this.$i18n, 'locale', lang)
      this.$store.commit('lang/SET_LANG', lang)
    }
  },
  mounted () {
    if (window.innerWidth >= 1024) {
      initCanvas()
    } else {
      this.isShowMobileWarning = true
    }
  }
}

const initCanvas = () => {
  var canvas = document.getElementById('canvas')
  var ctx = canvas.getContext('2d')

  resize()
  window.onresize = resize

  function resize () {
    canvas.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
    canvas.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
  }

  var RAF = (function () {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
      window.setTimeout(callback, 1000 / 60)
    }
  })()

  // 鼠标活动时，获取鼠标坐标
  var warea = { x: null, y: null, max: 20000 }
  // window.onmousemove = function (e) {
  //   e = e || window.event
  //
  //   warea.x = e.clientX
  //   warea.y = e.clientY
  // }
  // window.onmouseout = function (e) {
  //   warea.x = null
  //   warea.y = null
  // }

  // 添加粒子
  // x，y为粒子坐标，xa, ya为粒子xy轴加速度，max为连线的最大距离
  var dots = []
  for (var i = 0; i < 300; i++) {
    var x = Math.random() * canvas.width
    var y = Math.random() * canvas.height
    var xa = Math.random() * 2 - 1
    var ya = Math.random() * 2 - 1

    dots.push({
      x: x,
      y: y,
      xa: xa,
      ya: ya,
      max: 6000
    })
  }

  // 延迟100秒开始执行动画，如果立即执行有时位置计算会出错
  setTimeout(function () {
    animate()
  }, 100)

  // 每一帧循环的逻辑
  function animate () {
    ctx.clearRect(0, 0, canvas.width, canvas.height)

    // 将鼠标坐标添加进去，产生一个用于比对距离的点数组
    var ndots = [warea].concat(dots)

    dots.forEach(function (dot) {
      // 粒子位移
      dot.x += dot.xa
      dot.y += dot.ya

      // 遇到边界将加速度反向
      dot.xa *= (dot.x > canvas.width || dot.x < 0) ? -1 : 1
      dot.ya *= (dot.y > canvas.height || dot.y < 0) ? -1 : 1

      // 绘制点
      ctx.fillRect(dot.x - 0.5, dot.y - 0.5, 1, 1)

      // 循环比对粒子间的距离
      for (var i = 0; i < ndots.length; i++) {
        var d2 = ndots[i]

        if (dot === d2 || d2.x === null || d2.y === null) continue

        var xc = dot.x - d2.x
        var yc = dot.y - d2.y

        // 两个粒子之间的距离
        var dis = xc * xc + yc * yc

        // 距离比
        var ratio

        // 如果两个粒子之间的距离小于粒子对象的max值，则在两个粒子间画线
        if (dis < d2.max) {
          // 如果是鼠标，则让粒子向鼠标的位置移动
          if (d2 === warea && dis > (d2.max / 2)) {
            dot.x -= xc * 0.03
            dot.y -= yc * 0.03
          }

          // 计算距离比
          ratio = (d2.max - dis) / d2.max

          // 画线
          ctx.beginPath()
          ctx.lineWidth = ratio / 2
          // 线条颜色
          ctx.strokeStyle = 'rgba(64,158,255,' + (ratio + 0.1) + ')'
          ctx.moveTo(dot.x, dot.y)
          ctx.lineTo(d2.x, d2.y)
          ctx.stroke()
        }
      }

      // 将已经计算过的粒子从数组中删除
      ndots.splice(ndots.indexOf(dot), 1)
    })

    RAF(animate)
  }
}
</script>

<style rel="stylesheet/scss" lang="scss">
  $bg: #2d3a4b;
  $light_gray: #eee;

  /* reset element-ui css */
  .login-container {
    .el-input {
      display: inline-block;
      width: calc(100% - 44px);
      margin-left: 22px;

      input {
        background: transparent;
        border: 0;
        -webkit-appearance: none;
        border-radius: 0;
        padding: 12px 5px 12px 15px;
        color: #666;
        height: 44px;
        line-height: 44px;
      }
    }

    .el-form-item {
      border: 1px solid #ddd;
      background: #fff;
      border-radius: 22px;
      color: #454545;
      height: 44px;
      /*margin-bottom: 28px;*/

      .el-form-item__content {
        line-height: 44px;
      }
    }

    .el-button {
      height: 44px;
      border-radius: 22px;
    }

    #canvas {
      position: fixed;
      top: 0;
      left: 0;
    }
  }

  .message-error .el-message__content {
    width: 360px;
    line-height: 18px;
  }
</style>

<style rel="stylesheet/scss" lang="scss" scoped>
  $bg: white;
  $dark_gray: #889aa4;
  $light_gray: #aaa;
  .login-container {
    position: fixed;
    height: 100%;
    width: 100%;
    background-color: $bg;

    .login-form {
      background: transparent;
      position: absolute;
      left: 0;
      right: 0;
      width: 480px;
      max-width: 100%;
      padding: 35px 35px 15px 35px;
      margin: 120px auto;
    }

    .tips {
      font-size: 14px;
      color: #666;
      margin-bottom: 10px;
      background: transparent;

      span {
        &:first-of-type {
          margin-right: 22px;
        }
      }
    }

    .svg-container {
      padding: 6px 5px 6px 15px;
      color: $dark_gray;
      vertical-align: middle;
      width: 30px;
      display: inline-block;
    }

    .title {
      font-family: "Verdana", serif;
      /*font-style: italic;*/
      font-weight: 600;
      font-size: 48px;
      color: #409EFF;
      margin: 0px auto 20px auto;
      text-align: center;
      cursor: default;
    }

    .show-pwd {
      position: absolute;
      right: 10px;
      top: 7px;
      font-size: 16px;
      color: $dark_gray;
      cursor: pointer;
      user-select: none;
    }

    .alternatives {
      border-bottom: 1px solid #ccc;
      display: flex;
      justify-content: space-between;
      font-size: 14px;
      color: #666;
      font-weight: 400;
      margin-bottom: 10px;
      padding-bottom: 10px;

      .forgot-password {
        cursor: pointer;
      }

      .sign-in,
      .sign-up {
        cursor: pointer;
        color: #409EFF;
        font-weight: 600;
      }
    }

    .lang {
      margin-top: 20px;
      text-align: center;
      color: #666;

      span {
        cursor: pointer;
        margin: 10px;
        font-size: 14px;
      }

      span.active {
        font-weight: 600;
        text-decoration: underline;
      }

      span:hover {
        text-decoration: underline;
      }
    }

    .documentation {
      margin-top: 20px;
      text-align: center;
      font-size: 14px;
      color: #409eff;
      font-weight: bolder;

      &:hover {
        text-decoration: underline;
      }
    }

    .mobile-warning {
      margin-top: 20px;
    }

  }
</style>
<style scoped>
  .mobile-warning >>> .el-alert .el-alert__description {
    font-size: 1.2rem;
  }
</style>
