// general methods
showToast (args, style = false) {
  this.snackbar.show = false;
  switch (style) {
    case 'success': 
      args = {
        timeout: 3000,
        color: 'success',
        icon: 'mdi-check',
        ...args,
      }
      break;
    case 'warning':
      args = {
        timeout: 3000,
        color: 'warning',
        icon: 'mdi-close',
        ...args,
      }
      break;
    case 'error':
      args = {
        timeout: 3000,
        color: 'error',
        icon: 'mdi-close',
        ...args,
      }
      break;
  }
  this.$nextTick().then(()=>{
    let opts = {
      text: '', 
      color: 'primary', 
      timeout: -1, 
      icon: false, 
      dark: true,
      zindex: 201,
      ...args
    };
    this.snackbar = {
      ...opts,
      show: true
    };
  });
},
sha256: function sha256(text) {
  function rightRotate(value, amount) {
    return (value>>>amount) | (value<<(32 - amount));
  };
  
  var mathPow = Math.pow;
  var maxWord = mathPow(2, 32);
  var lengthProperty = 'length'
  var i, j; // Used as a counter across the whole file
  var result = ''

  var words = [];
  var textBitLength = text[lengthProperty]*8;
  
  //* caching results is optional - remove/add slash from front of this line to toggle
  // Initial hash value: first 32 bits of the fractional parts of the square roots of the first 8 primes
  // (we actually calculate the first 64, but extra values are just ignored)
  var hash = sha256.h = sha256.h || [];
  // Round constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes
  var k = sha256.k = sha256.k || [];
  var primeCounter = k[lengthProperty];
  /*/
  var hash = [], k = [];
  var primeCounter = 0;
  //*/

  var isComposite = {};
  for (var candidate = 2; primeCounter < 64; candidate++) {
    if (!isComposite[candidate]) {
      for (i = 0; i < 313; i += candidate) {
        isComposite[i] = candidate;
      }
      hash[primeCounter] = (mathPow(candidate, .5)*maxWord)|0;
      k[primeCounter++] = (mathPow(candidate, 1/3)*maxWord)|0;
    }
  }
  
  text += '\x80' // Append Ƈ' bit (plus zero padding)
  while (text[lengthProperty]%64 - 56) text += '\x00' // More zero padding
  for (i = 0; i < text[lengthProperty]; i++) {
    j = text.charCodeAt(i);
    if (j>>8) return; // text check: only accept characters in range 0-255
    words[i>>2] |= j << ((3 - i)%4)*8;
  }
  words[words[lengthProperty]] = ((textBitLength/maxWord)|0);
  words[words[lengthProperty]] = (textBitLength)
  
  // process each chunk
  for (j = 0; j < words[lengthProperty];) {
    var w = words.slice(j, j += 16); // The message is expanded into 64 words as part of the iteration
    var oldHash = hash;
    // This is now the undefinedworking hash", often labelled as variables a...g
    // (we have to truncate as well, otherwise extra entries at the end accumulate
    hash = hash.slice(0, 8);
    
    for (i = 0; i < 64; i++) {
      var i2 = i + j;
      // Expand the message into 64 words
      // Used below if 
      var w15 = w[i - 15], w2 = w[i - 2];

      // Iterate
      var a = hash[0], e = hash[4];
      var temp1 = hash[7]
        + (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)) // S1
        + ((e&hash[5])^((~e)&hash[6])) // ch
        + k[i]
        // Expand the message schedule if needed
        + (w[i] = (i < 16) ? w[i] : (
            w[i - 16]
            + (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15>>>3)) // s0
            + w[i - 7]
            + (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2>>>10)) // s1
          )|0
        );
      // This is only used once, so *could* be moved below, but it only saves 4 bytes and makes things unreadble
      var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) // S0
        + ((a&hash[1])^(a&hash[2])^(hash[1]&hash[2])); // maj
      
      hash = [(temp1 + temp2)|0].concat(hash); // We don't bother trimming off the extra ones, they're harmless as long as we're truncating when we do the slice()
      hash[4] = (hash[4] + temp1)|0;
    }
    
    for (i = 0; i < 8; i++) {
      hash[i] = (hash[i] + oldHash[i])|0;
    }
  }
  
  for (i = 0; i < 8; i++) {
    for (j = 3; j + 1; j--) {
      var b = (hash[i]>>(j*8))&255;
      result += ((b < 16) ? 0 : '') + b.toString(16);
    }
  }
  return result;
},
handleError (err = {}) {
  if (err.status === 418) {
    return swal(this._get(err,'response.data.error.message',err.message), "", "error");
  } else {
    console.error(err);
    return swal(this.$t('error.general'), this._get(err,'response.data.error.message',err.message), "error");
  }
},
getUrlParam (param) {
  const params = new URLSearchParams(window.location.search);
  return params.get(param);
},
updateUrlParam (data) {
  const params = new URLSearchParams(window.location.search);
  if (Array.isArray(data)) {
    data.forEach(d => {
      params.set(d.name, d.value);
    });
  } else {
    params.set(data.name, data.value);
  }
  window.history.replaceState({}, document.title, decodeURIComponent(`${window.location.pathname}?${params}`));
},
params (a) {
  var s = [];
  var add = function (k, v) {
    v = typeof v === 'function' ? v() : v;
    v = v === null ? '' : v === undefined ? '' : v;
    s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v);
  };
  var buildParams = function (prefix, obj) {
    var i, len, key;
    if (prefix) {
      if (Array.isArray(obj)) {
        for (i = 0, len = obj.length; i < len; i++) {
          buildParams(
            prefix + '[' + (typeof obj[i] === 'object' && obj[i] ? i : '') + ']',
            obj[i]
          );
        }
      } else if (String(obj) === '[object Object]') {
        for (key in obj) {
          buildParams(prefix + '[' + key + ']', obj[key]);
        }
      } else {
        add(prefix, obj);
      }
    } else if (Array.isArray(obj)) {
      for (i = 0, len = obj.length; i < len; i++) {
        add(obj[i].name, obj[i].value);
      }
    } else {
      for (key in obj) {
        buildParams(key, obj[key]);
      }
    }
    return s;
  };
  return buildParams('', a).join('&');
},
getCookie(cname) {
  var name = cname + "=";
  var decodedCookie = decodeURIComponent(document.cookie);
  var ca = decodedCookie.split(';');
  for(var i = 0; i <ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0) == ' ') {
      c = c.substring(1);
    }
    if (c.indexOf(name) == 0) {
      return c.substring(name.length, c.length);
    }
  }
  return "";
},
getQuery () {
  var urlParams = new URLSearchParams(location.search);
  var entries = urlParams.entries();
  let result = {}
  for(let entry of entries) { // each 'entry' is a [key, value] tupple
    var [key, value] = entry;
    result[key] = value;
  }
  return result; 
},
// utils
generateUUID(){
  let d = new Date().getTime();
  if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
    d += performance.now(); // use high-precision timer if available
  }
  let newGuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    let r = (d + Math.random() * 16) % 16 | 0;
    d = Math.floor(d / 16);
    return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
  });
  return newGuid;
},
clone (o) {
  return JSON.parse(JSON.stringify(o));
},
_get (object, keys, defaultVal) {
  if (object) {
    if (keys) {
      keys = Array.isArray(keys) ? keys : keys.split('.');
    } else {
      keys = [];
    }
    object = object[keys[0]];
    if (object && keys.length > 1) {
      return this._get(object, keys.slice(1), defaultVal);
    }
    return object === undefined ? defaultVal : object;
  } else {
    return defaultVal;
  }
},
noscript(string) {
  return string.replace(/(\<script)\s*[^\>]*\>([^\<]*\<\/script>)?/gi,'')
},
validate (type, value) {
  switch (type) {
    case 'email':
      var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
      return re.test(String(value))
    case 'email_characters':
      var re = /^[a-zA-Z0-9\.\_\+\@]+$/
      return re.test(String(value))
    default:
      return !!value
  }
},
_f (data, attr, lang) {
  if (lang) {
    return this._get(data, lang + '.' + attr, this._get(data,'default.'+attr,''));
  } else {
    return this._get(data, this.$i18n.locale + '.' + attr, this._get(data,'default.'+attr,''));
  }
},
getDateDiff (start_date, end_date) {
  var date_diff = new Date(start_date) * 1 - new Date(end_date) * 1;
  var diff_day = Math.ceil(date_diff/(24*60*60*1000));
  return diff_day;
},
calculateDateTime (date, value, quantifier) {
  var dt = new Date(date)
  var result = dt
  switch (quantifier) {
    case 'year':
      result = dt.setFullYear(dt.getFullYear()+value)
      break
    case 'month':
      result = dt.setMonth(dt.getMonth()+value)
      break
    case 'day':
      result = dt.setDate(dt.getDate()+value)
      break
    case 'hour':
      result = dt.setHours(dt.getHours()+value)
      break
    case 'minute':
      result = dt.setMinutes(dt.getMinutes()+value)
      break
    case 'second':
      result = dt.setSeconds(dt.getSeconds()+value)
      break
  }
  return result;
},
calculateDate (date, value, quantifier) {
  return this.getISODateString(this.calculateDateTime(date,value,quantifier));
},
getISODateString (date) {
  // ISO 8601 TZ
  var iso_format_date;
  if (typeof date === "number" || typeof date === "object") {
    iso_format_date = new Date(date);
  } else {
    iso_format_date = new Date((date&&date.toString().split('+')[0]));
  }
  // locale datetime
  return  new Date(iso_format_date).toLocaleDateString("fr-CA");
},
copyText (text, toast = true) {
  let is_iOS = /iphone|ipad|ipod/i.test(navigator.userAgent.toLowerCase());
  let is_safari = /apple/i.test(navigator.vendor.toLowerCase());
  if (is_iOS || is_safari){
    var textArea = document.createElement('textArea');
    textArea.value = text;
    document.body.appendChild(textArea);
    var range = document.createRange();
    range.selectNodeContents(textArea);
    var selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
    textArea.setSelectionRange(0, 999999);
    document.execCommand('copy');
    document.body.removeChild(textArea);
  } else {
    navigator.clipboard.writeText(text);
  }
  
  if (toast) {
    this.showToast({text: "複製成功", zindex:301}, "success");
  }
},
// locale
changeLocale (lang, callback=function(){}) {
  let locale = lang;
  if (locale != this.getUrlParam("lang")) {
    this.updateUrlParam({"name":"lang", "value":locale})
  }
  // if (locale == 'zh-HK') {
  //   this.$i18n.locale = 'zh_TW'
  // } else {
    this.$i18n.locale = locale
  // }
  callback();
},
setLocale (lang) {
  // if (lang == 'zh-HK') {
  //   this.$i18n.locale = 'zh_TW'
  // } else {
    this.$i18n.locale = lang
  // }
},
getLocale (i18n_lang = false) {
  // if (!i18n_lang && this.$i18n.locale == 'zh_TW') {
  //   return 'zh-HK';
  // } else {
    return this.$i18n.locale;
  // }
}