function $(a) {
  return document.getElementById(a);
}

let pdat = {
  monners: 0,
  monnerperclick: 1,
  globalMPSx: 1,
  ownedItems: {},
  ownedUpgrades: {},
  can: 0,
  stats: {
    game_start: new Date().getTime(),
    total_monners: 0,
    cans_unlocked: 1,
  },
};

if (localStorage.autosave && localStorage.autosave !== 'undefined') pdat = JSON.parse(localStorage.autosave);

// Auto save
setInterval(() => {
  localStorage.autosave = JSON.stringify(pdat);
}, 5000);

const monnerButton = $('monner');

function click(pos) {
  pdat.monners += pdat.monnerperclick;
  pdat.stats.total_monners += pdat.monnerperclick;
  einup(pdat.monnerperclick, pos);
  monnerButton.style.transition = '0s';
  monnerButton.style.transform = `scale(1.2) rotate(${
    ((Math.random() - 0.5) * Math.PI) / 4
  }rad)`;
  setTimeout(() => {
    monnerButton.style.transform = 'scale(1)';
    monnerButton.style.transition = '0.15s ease-out';
  }, 10);
  launchMonnerParticle();
  update();
}

monnerButton.addEventListener('click', e => {
  e.preventDefault();
  click({
    x: e.x,
    y: e.y,
  });
});

// ### Main Update ###

function update() {
  // Main Text
  $('stats-can').innerText =
    'Dåse ' + (pdat.can + 1) + ' - ' + data.cans[pdat.can].title;
  $('stats-monners').innerText =
    numToString(Math.floor(pdat.monners)) + ' monners';
  $('stats-monnerps').innerText = numToString(getTotalMPS()) + ' mps';
  // Can
  $('monner').style.backgroundImage = `url(assets/${
    data.cans[pdat.can].can
  }.png)`;
  monnerzImg.src = `assets/${data.cans[pdat.can].bglogo}.png`;
  // Can Selector
  displayCans();
  // Shop
  displayShop();
}

// Update Title
setInterval(() => {
  $('title').innerText = `${numToString(
    Math.floor(pdat.monners)
  )} monners - Monner Clicker`;
}, 1000);

// ### MPS ###

let pms = 0;
let nextParticle = 0;

function mpsTick(nms) {
  let s = (nms - pms) / 1000;
  pms = nms;

  let newMonners = getTotalMPS() * s;

  pdat.monners += newMonners;
  pdat.stats.total_monners += newMonners;
  // Particle Limit
  nextParticle += newMonners > 2 ? 2 : newMonners;

  while (nextParticle > 1) {
    launchMonnerParticle();
    nextParticle -= 1;
    monnerButton.style.transition = '0s';
    monnerButton.style.transform = `scale(1.02) rotate(${
      ((Math.random() - 0.5) * Math.PI) / 32
    }rad)`;
    setTimeout(() => {
      monnerButton.style.transform = 'scale(1)';
      monnerButton.style.transition = '0.15s ease-out';
    }, 10);
  }

  update();

  requestAnimationFrame(mpsTick);
}
requestAnimationFrame(mpsTick);

// ### Can Selector ###

let canElements = [];

function displayCans() {
  if (pdat.stats.cans_unlocked > 1)
    for (let i = 0; i < pdat.stats.cans_unlocked; i++) {
      if (!canElements[i]) {
        canElements[i] = $('monners').q('.selectable-monner');
        canElements[i].addEventListener('click', () => selectCan(i));
      }
      canElements[
        i
      ].style.backgroundImage = `url(assets/${data.cans[i].can}.png)`;
      canElements[i].style.left =
        window.innerWidth / 2 -
        60 -
        45 * (pdat.stats.cans_unlocked - 1) +
        90 * i +
        'px';
    }
}

function selectCan(cI) {
  if (pdat.stats.cans_unlocked >= cI) pdat.can = cI;
}

// ### Shop ###

let shopElements = {};
let upgradeElements = {};

function displayShop() {
  // Items
  Object.entries(data.items).forEach(entry => {
    let key = entry[0],
      item = entry[1];

    if (!shopElements[key]) {
      shopElements[key] = $('shop-items').q('.shop-item');
      shopElements[key].owned = shopElements[key].q('.shop-item-owned');
      shopElements[key].q('.shop-item-name').innerText = item.name;
      let priceContainer = shopElements[key].q('.shop-item-price');
      shopElements[key].price = priceContainer.q('.shop-item-price-num');
      priceContainer.q('img.shop-item-price-icon').src =
        'assets/monner-16x24.png';
      shopElements[key].addEventListener('click', () => buyItem(key));
    }

    const el = shopElements[key];
    el.price.innerText = numToString(getItemPrice(key));
    el.owned.innerText = `x${pdat.ownedItems[key]}`;

    if (pdat.ownedItems[key]) el.owned.style.display = 'block';
    else el.owned.style.display = 'none';

    el.classList.toggle('cantAfford', !canAffordItem(key));

    el.tip = () => {
      return `<div class="monner-tip"><b>${item.name}</b><br><i>"${
        item.description
      }"</i><br>${listStats(item.stats, key)}${
        pdat.ownedItems[key] ? `<br><i>${listOutput(key)}</i>` : ''
      }</div>`;
    };
  });

  // Upgrades
  Object.entries(data.upgrades).forEach(entry => {
    let key = entry[0],
      upgrade = entry[1];

    if (!pdat.ownedUpgrades[key] &&
      upgrade.conditions &&
      conditionsMet(upgrade.conditions)
    ) {
      if (!shopElements[key]) {
        shopElements[key] = $('shop-upgrades').q('.shop-upgrade');
        shopElements[key].addEventListener('click', () => buyUpgrade(key));
        shopElements[key].style.backgroundImage = `url(assets/${upgrade.img})`;
      }

      shopElements[key].classList.toggle(
        'cantAfford',
        pdat.monners < upgrade.price
      );

      shopElements[key].tip = () => {
        return `<div class="monner-tip"><b>${
          upgrade.name
        }</b><br><div class="centerline">Pris: ${numToString(
          upgrade.price
        )}<img src="assets/monner-16x24.png"></div><br>${listStats(
          upgrade.stats
        )}</div>`;
      };
    } else {
      if (shopElements[key]) {
        $('shop-upgrades').removeChild(shopElements[key]);
        delete shopElements[key];
      }
    }
  });
}

// ### Item Details ###

function getItemPrice(key) {
  let item = data.items[key];
  return item ?
    Math.ceil(
      pdat.ownedItems[key] ?
      item.price * item.pricemultiplier ** pdat.ownedItems[key] :
      item.price
    ) :
    false;
}

function canAffordItem(key) {
  let price = getItemPrice(key);
  return price === false ? false : pdat.monners >= price;
}

function getTotalMPS() {
  let mps = 0;
  let mods = getItemModifiers(Object.keys(pdat.ownedItems));

  Object.entries(pdat.ownedItems).forEach(entry => {
    let item = entry[0];
    let amount = entry[1];

    if (data.items[item] && data.items[item].stats)
      data.items[item].stats.forEach(stat => {
        if (stat.type === 'monnerpersecond')
          mps +=
          stat.add *
          (mods[item] ? mods[item].multipliers.monnerpersecond : 1) *
          amount *
          Math.ceil((amount + 1) / 50);
      });
  });

  return mps;
}

function getItemModifiers(key) {
  let out = {};

  let keys = Array.isArray(key) ? key : [key];

  for (let i = 0; i < keys.length; i++)
    out[keys[i]] = {
      multipliers: {
        monnerpersecond: 1,
      },
    };

  let upgrades = Object.keys(pdat.ownedUpgrades);
  for (let i = 0; i < upgrades.length; i++) {
    const upgrade = data.upgrades[upgrades[i]];
    if (upgrade && upgrade.stats)
      for (let i = 0; i < upgrade.stats.length; i++) {
        const stat = upgrade.stats[i];
        if (stat.type === 'boost')
          if (stat.item === '*')
            for (let i = 0; i < keys.length; i++)
              out[keys[i]].multipliers[stat.stat] *= stat.x;
          else if (out[stat.item])
          out[stat.item].multipliers[stat.stat] *= stat.x;
      }
  }

  return Array.isArray(key) ? out : out[key];
}

// ### Buy Items & Upgrades ###

function buyItem(key) {
  if (!data.items[key]) return false;
  if (!pdat.ownedItems[key]) pdat.ownedItems[key] = 0;
  let item = data.items[key];
  if (canAffordItem(key)) {
    pdat.monners -= getItemPrice(key);
    pdat.ownedItems[key] += 1;
    // Apply stats
    applyStats(item.stats);
  }
  update();
}

function buyUpgrade(key) {
  if (!data.upgrades[key] || pdat.ownedUpgrades[key]) return false;
  let upgrade = data.upgrades[key];
  if (pdat.monners >= upgrade.price) {
    pdat.monners -= upgrade.price;
    pdat.ownedUpgrades[key] = true;
    // Apply stats
    applyStats(upgrade.stats);
  }
  update();
}

// ### Conditions ###

function conditionsMet(conditions) {
  let met = true;
  conditions.forEach(condition => {
    if (
      condition.type === 'pstat' &&
      (!condition.stat ||
        !pdat.stats[condition.stat] == undefined ||
        pdat.stats[condition.stat] < condition.min)
    )
      met = false;
    if (
      condition.type === 'own' &&
      (!condition.item ||
        pdat.ownedItems[condition.item] == undefined ||
        pdat.ownedItems[condition.item] < condition.min)
    )
      met = false;
  });
  return met;
}

// ### Stats ###

function listStats(stats, key) {
  let mods = getItemModifiers(key);
  let out = '';
  stats.forEach(stat => {
    if (stat.type === 'can') out += `<br>Unlock den næste dåse!`;
    else if (stat.type === 'monnerpersecond')
      out +=
      mods && mods.multipliers.monnerpersecond > 1 ?
      `<br>+ ${numToString(
              stat.add * Math.ceil((pdat.ownedItems[key] + 1 || 1) / 50)
            )} x ${numToString(
              mods.multipliers.monnerpersecond
            )} = ${numToString(
              stat.add * mods.multipliers.monnerpersecond
            )} mps` :
      `<br>+ ${numToString(
              stat.add * Math.ceil((pdat.ownedItems[key] + 1 || 1) / 50)
            )} mps`;
    else if (stat.type === 'monnerperclick')
      out += `<br>+ ${numToString(stat.add)} monner/klik`;
    else if (stat.type === 'boost' && data.items[stat.item])
      out += `<br>Gør ${data.items[stat.item].name} ${numToString(
        stat.x
      )}x så effektivt`;
    else if (stat.type === 'boost' && stat.item === '*')
      out += `<br>Gør alt i buttiken ${numToString(stat.x)}x så effektivt`;
  });
  return out;
}

function applyStats(stats) {
  stats.forEach(stat => {
    if (stat.type === 'can') {
      pdat.can += 1;
      pdat.stats.cans_unlocked += 1;
      launchLevelupParticles();
    } else if (stat.type === 'monnerperclick') pdat.monnerperclick += stat.add;
  });
}

function listOutput(key) {
  let mods = getItemModifiers(key);
  let out = '';

  if (pdat.ownedItems[key]) {
    let globmps = getTotalMPS();

    data.items[key].stats.forEach(stat => {
      if (stat.type === 'monnerpersecond') {
        let generating =
          pdat.ownedItems[key] *
          stat.add *
          mods.multipliers.monnerpersecond *
          Math.ceil((pdat.ownedItems[key] + 1) / 50);
        out += `... Generating ${numToString(generating)} mps (${(
          (generating / globmps) *
          100
        ).toFixed(1).replace('.', ',')}%)`;
      }
    });
  }

  return out;
}

// ### Tips ###

const tip = document.body.q('.tip');
document.addEventListener('mousemove', e => {
  const path = e.composedPath();
  for (let i = 0; i < path.length; i++) {
    let target = path[i];
    if (
      target &&
      target !== document &&
      target !== window &&
      (target.getAttribute('tip') || target.tip)
    ) {
      let tiptxt = target.tip ? target.tip : target.getAttribute('tip');
      if (typeof tiptxt === 'function') tiptxt = tiptxt();
      if (tiptxt) {
        tip.innerHTML = tiptxt;
        let pos = target.getBoundingClientRect();
        tip.style.left = pos.x + 'px';
        tip.style.top = pos.y + pos.height + 'px';
        tip.classList.add('show');
        return false;
      }
    }
  }
  tip.classList.remove('show');
});
document.addEventListener('mouseleave', () => tip.classList.remove('show'));
document.addEventListener('mousedown', () => tip.classList.remove('show'));
document.addEventListener('mouseup', () => tip.classList.remove('show'));

// ### Background Canvas ###
const bgCanvas = $('bg-canvas');
const bgCtx = bgCanvas.getContext('2d');

function setCanvas() {
  // Set canvas dimensions
  bgCanvas.width = window.innerWidth;
  bgCanvas.height = window.innerHeight;
}
setCanvas();
window.addEventListener('resize', setCanvas);

// Particles
const bgParticles = [];
const monnerzImg = new Image();
class Particle {
  constructor(x, y, vx, vy, img) {
    this.x = x || 0;
    this.y = y || 0;
    this.vx = vx || 0;
    this.vy = vy || 0;
    this.rotSpeed = (Math.random() - 0.5) / 20;
    this.rotation = 0;
    this.life = 1;
    this.img = img;
  }
  tick(ms) {
    this.x += this.vx * ms;
    this.y += this.vy * ms;
    this.rotation += this.rotSpeed;
    this.life -= 0.0015;
    if (this.life < 0) {
      this.kill();
    }
  }
  draw() {
    if (this.life > 0) {
      bgCtx.translate(this.x, this.y);
      bgCtx.rotate(this.rotation);
      bgCtx.globalAlpha = this.life;
      bgCtx.drawImage(this.img, -21, -26.75, 42, 53.5);
      bgCtx.rotate(-this.rotation);
      bgCtx.translate(-this.x, -this.y);
    }
  }
  kill() {
    bgParticles.splice(bgParticles.indexOf(this), 1);
  }
}

class BGFlash {
  constructor() {
    this.life = 1;
  }
  tick(ms) {
    this.life -= 0.005;
    if (this.life < 0) this.kill();
  }
  draw() {
    if (this.life > 0) {
      bgCtx.globalAlpha = this.life;
      bgCtx.fillStyle = '#888';
      bgCtx.fillRect(0, 0, bgCanvas.width, bgCanvas.height);
    }
  }
  kill() {
    bgParticles.splice(bgParticles.indexOf(this), 1);
  }
}

// Create Particle
function launchMonnerParticle() {
  let r = Math.random() * Math.PI * 2;
  let vx = Math.sin(r) * 0.2;
  let vy = Math.cos(r) * 0.2;
  bgParticles.push(
    new Particle(bgCanvas.width / 2, bgCanvas.height / 2, vx, vy, monnerzImg)
  );
}

function launchLevelupParticles() {
  let i = 0;
  bgParticles.push(new BGFlash());
  let spawner = setInterval(() => {
    let r = Math.random() * Math.PI * 2;
    let vx = Math.sin(r) * 0.2;
    let vy = Math.cos(r) * 0.2;
    bgParticles.push(
      new Particle(bgCanvas.width / 2, bgCanvas.height / 2, vx, vy, monnerzImg)
    );
    if (i++ > 150) clearInterval(spawner);
  }, 5);
}

// Canvas Loop
let cpms = 0;

function tickCanvas(ms) {
  let tms = ms - cpms;
  cpms = ms;
  bgCtx.clearRect(0, 0, bgCanvas.width, bgCanvas.height);
  bgParticles.forEach(particle => {
    particle.tick(tms);
    particle.draw();
  });
  window.requestAnimationFrame(tickCanvas);
}
window.requestAnimationFrame(tickCanvas);

// ### 1up TEXT ###
function einup(nr, pos) {
  let oneup = document.body.q('.oneup');
  oneup.innerText = '+' + nr;
  oneup.style.left =
    pos.x - oneup.offsetWidth / 2 + Math.random() * 50 - 25 + 'px';
  oneup.style.top = pos.y - oneup.offsetHeight + Math.random() * 50 - 25 + 'px';
  setTimeout(() => document.body.removeChild(oneup), 1000);
}

// ### Pages ###
function togglePage(id) {
  $(id + '-page').classList.toggle('show');
  Array.from(document.getElementsByClassName('page')).forEach(page => {
    if (page.id !== id + '-page') page.classList.remove('show');
  });
}

// ### Stats Page ###
setInterval(updateStats, 500)

function updateStats() {
  let sl = $('stats-list');

  sl.innerHTML = '';

  sl.q(`"Playtime: ${msToString(new Date().getTime() - pdat.stats.game_start)}"`);
  sl.q(`"Current Monners: ${numToString(Math.floor(pdat.monners))}"`);
  sl.q(`"Total Monners: ${numToString(Math.floor(pdat.stats.total_monners))}"`);
  sl.q(`"Total Cans: ${pdat.stats.cans_unlocked}"`);
  sl.q(`"Current MPS: ${numToString(getTotalMPS())}"`);
}

// ### Hjælpe Funktioner ###
// Number prettifier
let magnitudes = ['', '', 'M', 'B', 'T', 'Q', 'AB', 'AC', 'AD'];

function numToString(num) {
  let m = Math.ceil(num.toFixed(0).length / 3) - 1;

  if (m >= 2) num = num / 1000 ** m;

  let parts = parseFloat(num.toFixed(3)).toString().split('.');
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
  return parts.join(',') + ' ' + magnitudes[m];
}

function msToString(ms) {
  let s = ms / 1000;
  if (s < 60) return Math.floor(s) + ' second' + (Math.floor(s) === 1 ? '' : 's');
  if (s / 60 < 60) return Math.floor(s / 60) + ' minute' + (Math.floor(s / 60) === 1 ? '' : 's');
  if (s / 60 / 60 < 24) return Math.floor(s / 60 / 60) + ' hour' + (Math.floor(s / 60 / 60) === 1 ? '' : 's');
  else return Math.floor(s / 60 / 60 / 24) + ' day' + (Math.floor(s / 60 / 60 / 24) === 1 ? '' : 's');
}

// ResetAll
function resetAll() {
  if (confirm('Are you sure you want to erase you save? This action is irreversible.')) {
    pdat = undefined;
    localStorage.autosave = undefined;
    window.location.reload();
  }
}