import { computed } from "vue";

/////////////////////////////////////////
// Helper
/////////////////////////////////////////

function setPoints(page, set){
  let points = [0, 0]; // need to get updated for more player

  for (const i in page.game.sets) {
    const s = page.game.sets[i];
    const p = legPoints(s, s.legs[s.legs.length-1]);
    
    const winner = getWinner(p, page.legs);
    
    if (winner > -1)
      points[winner] += 1;
    else if (winner == -2) {
      points[0] += 0.5;
      points[1] += 0.5;
    } else
      return points;

    if (s == set){
      return points;
    }
  }
  return points;
}

function legPoints(set, leg){
  let points = [0, 0]; // need to get updated for more player
  for (const i in set.legs) {
    const l = set.legs[i];

    if (l.visits[l.visits.length-1].toGo === undefined) {      
      return points;
    }
    if (l.visits[l.visits.length-1].toGo[0] == 0)
      points[0] += 1;
    else if (l.visits[l.visits.length-1].toGo[1] == 0)
      points[1] += 1;
    if (l == leg){
      return points;
    }
  }
  return points;
}

function newPlayerStats(player) {
  return {
    player: player.uuid,
    average: [0, 0],
    first9: [0, 0],
    checkouts: [0, 0],
    checkins: [0, 0],
    checkinPoints: [],
    checkoutPoints: [],
    "60+": 0,
    "100+": 0,
    "140+": 0,
    "180": 0
  };
}

function addNewSetStats(stats, scorers) {
  const s = newStats(scorers)
  s["legs"] = [];
  stats["sets"].push(s);
  return stats;
}

function addNewLegStats(stats, scorers) {
  stats["sets"].at(-1)["legs"].push(newStats(scorers));
  return stats;
}

function newStats(scorers) {
  const stats = {
    stats: []
  }
  scorers.forEach((scorer) => {
    scorer.member.forEach((player) => {
      const n = newPlayerStats(player);
      stats.stats.push(n);
    });
  });
  return stats;
}

function newVisit(playerUUID, round) {
  return {
    player: playerUUID,
    throws: [],
    visit: round,
    checkoutTries: 0,
    checkinTries: 0,
    numDarts: 0,
  }
}

function clearLastVisit(page){
  const set = page.game.sets[page.game.sets.length-1];
  const leg = set.legs[set.legs.length-1];
  const visit = leg.visits[leg.visits.length-1];
  const ret = visit["throws"];
  visit["throws"] = [];
  visit["checkoutTries"] = 0;
  visit["checkinTries"] = 0;
  visit["numDarts"] = 0;
  delete visit["sum"];
  delete visit["toGo"];
  return ret;
}

function getWinner(points, mode) {
  const sorted = [...points].map((e,i) => [e,i]).sort((a, b) => b[0] - a[0]);
  const k = points.length;
  const sum = points.reduce((a,c) => a+c, 0);
  if (sum < mode && sorted[0][0] <= mode/k){
    // not over yet
    return -1;
  }
  if (sorted[0][0] == sorted[1][0]){
    // Draw
    return -2;
  }
  // winner id
  return sorted[0][1];
}

function addNewSet(page){
  const set = page.game.sets[page.game.sets.length-1];
  const k = player2scorer(page)[set.legs[0].visits[0].player];
  const p = page.scorers[(k+1)%2].member[0].uuid;
  page.game.sets.push({
    // points: [...points],
    legs: [{
        points: Array(2).fill(0),
        visits: [newVisit(p, 1)]
      }]
  });
}

function addNewLeg(page){
  const set = page.game.sets[page.game.sets.length-1];
  const leg = set.legs[set.legs.length-1];
  const k = player2scorer(page)[leg.visits[0].player];
  
  const p = page.scorers[(k+1)%2].member[0].uuid;
  page.game.sets[page.game.sets.length-1].legs.push({
    // points: [...points],
    visits: [newVisit(p, 1)]
  });
}


/////////////////////////////////////////
// Exports
/////////////////////////////////////////

export function playerFromUUID(uuid, players){
  return players[players.map((p) => p.uuid).indexOf(uuid)];
}

export const getFinalWinner = (page) => {
  const points = setPoints(page, page.sets[page.sets.length-1]);
  return getWinner(points, page.sets);
};

export function initStats(page) {
  const stats = newStats(page.scorers);
  stats.sets = [];
  addNewSetStats(stats, page.scorers);
  addNewLegStats(stats, page.scorers);
  page.stats = stats;
}

export function initGame(page) {
  page.game = {
    sets: [{
      points: new Array(2).fill(0),
      legs: [{
        points: new Array(2).fill(0),
        visits: [newVisit(page.scorers[0].member[0].uuid, 1)]
      }]
    }]
  }
}

function updateStats(page, visit){
  const playerUUIDs = allPlayers(page).map((p) => p.uuid);
  const p = player2scorer(page)[visit.player];
  const k = playerUUIDs.indexOf(visit.player);

  const todos = [page.stats["stats"][k]];
  todos.push(page.stats["sets"].at(-1)["stats"][k]);
  todos.push(page.stats["sets"].at(-1)["legs"].at(-1)["stats"][k]);

  todos.forEach((value, i) => {
    todos[i]["average"][0] += visit["sum"];
    todos[i]["average"][1] += visit["numDarts"];
    if (visit["visit"] < 4) {
      todos[i]["first9"][0] += visit["sum"];
      todos[i]["first9"][1] += visit["numDarts"];
    }
    if (visit["toGo"][p] == 0) {
      todos[i]["checkouts"][0] += 1;
      todos[i]["checkoutPoints"].push(visit["sum"]);
    }
    if (visit["checkinTries"] != 0 && visit["sum"] != 0) {
      todos[i]["checkins"][0] += 1;
      todos[i]["checkinPoints"].push(visit["sum"]);
    }
    todos[i]["checkouts"][1] += visit["checkoutTries"];
    todos[i]["checkins"][1] += visit["checkinTries"];
    if (visit["sum"] == 180) {
      todos[i]["180"] += 1;
    } else if (visit["sum"] >= 140) {
      todos[i]["140+"] += 1;
    } else if (visit["sum"] >= 100) {
      todos[i]["100+"] += 1;
    } else if (visit["sum"] >= 60) {
      todos[i]["60+"] += 1;
    }
  });
}

function getNextPlayer(page, visit) {
  const set = page.game.sets[page.game.sets.length-1];
  const leg = set.legs[set.legs.length-1];

  const offset = 1*(leg.visits[0].player == page.scorers[0].member[0].uuid);
  const order = [];
  for (let index = 0; index < page.scorers[0].member.length; index++) {
    order.push(page.scorers[1-offset].member[index].uuid);
    order.push(page.scorers[offset].member[index].uuid);
  } 
  const nextid = (order.indexOf(visit.player)+1);
  return [order[nextid%order.length], (nextid == order.length)];
}

export function player2scorer(page){
  return Object.entries(page.scorers).reduce((result, [scorersId, scorer]) => {
    // Use the map method to create an array of key-value pairs for each "member"
    scorer.member.forEach(player => {
      result[player.uuid] = parseInt(scorersId);
    });
    return result;
  }, {});
}

export function allPlayers(page){
  return Object.entries(page.scorers).reduce((result, [scorersId, scorer]) => {
    return result.concat(scorer.member);
  }, []);
}

export const formatDate = (d) => {
  return `${d.getFullYear()}-${(d.getMonth()+1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')} ${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
};


export function recalcStats(page){
  // const set = page.game.sets[page.game.sets.length-1];
  // const leg = set.legs[set.legs.length-1];
  // const visit = leg.visits[leg.visits.length-1];
  initStats(page);
  const lastset = page.game["sets"].length-1;
  page.game["sets"].forEach((set, i) => {
    const lastleg = set.legs.length-1;
    set.legs.forEach((leg, j) => {
      const lastvisit = leg["visits"].length-1;
      leg.visits.forEach((visit, k) => {
        if (!(k == lastvisit && j == lastleg && i == lastset)) {
          updateStats(page, visit);
        }
        if (k == lastvisit && j != lastleg) {
          addNewLegStats(page.stats, page.scorers);
        }
      });
      if (j == lastleg && i != lastset) {
        addNewSetStats(page.stats, page.scorers);
        addNewLegStats(page.stats, page.scorers);
      }
    });
  });
}

export function removeLastVisit(page){
  const set = page.game.sets[page.game.sets.length-1];
  const leg = set.legs[set.legs.length-1];
  // const visit = leg.visits[leg.visits.length-1];

  // delete last visit.
  leg["visits"].pop();
  if (leg["visits"].length == 0){
    set["legs"].pop();
    if (set["legs"].length == 0){
      page.game["sets"].pop();
      if (page.game["sets"].length == 0){
        initGame(page);
        return;
      }
    }
  }
  // clearLastVisit
  const ret = clearLastVisit(page);
  recalcStats(page);
  return ret;
}

export function storeVisit(page, throws, sum, numDarts, outTries, inTries=0) {
  const set = page.game.sets[page.game.sets.length-1];
  const leg = set.legs[set.legs.length-1];
  const visit = leg.visits[leg.visits.length-1];

  visit["numDarts"] = numDarts;
  visit["throws"] = throws;
  visit["checkoutTries"] = outTries;
  visit["checkinTries"] = inTries;
  visit["sum"] = sum

  const k = player2scorer(page)[visit["player"]];
  let toGo;
  if (leg["visits"].length-2 < 0) {
    toGo = Array(2).fill(page.modus);
  } else {
    toGo = [...leg["visits"][leg["visits"].length-2]["toGo"]];
  }
  const rest = toGo[k] - visit["sum"];
  if (rest < 0 || (page.out == "Double" && rest == 1)) {
    visit["sum"] = 0;
  } else {
    toGo[k] = rest;
  }
  visit["toGo"] = toGo;
  if (page.in != "Straight" && page.modus == toGo[k] && visit["sum"] == 0) {
    visit["checkinTries"] = 3;
  }
  // update stats
  updateStats(page, visit);

  let update = [];
  if (rest != 0) {
    // Normal case...next players turn
    const [nextPlayer, newRound] = getNextPlayer(page, visit);
    const nVisit = newVisit(nextPlayer, visit["visit"]+1*newRound);
    leg.visits.push(nVisit);
  }
  else {
    // rest == 0 leg finished
    
    
    const newlegp = legPoints(set, leg);
    let winner = getWinner(newlegp, page.legs);
    console.log(winner);
    if (winner == -1) {
      // new Leg
      addNewLeg(page);
      addNewLegStats(page.stats, page.scorers);
    } else {
      // new set?
      const newsetp = setPoints(page, set);
      winner = getWinner(newsetp, page.sets);
      console.log("Set", winner, newsetp, page.sets);
      if (winner == -1) {
        // new Set
        addNewSet(page);
        addNewSetStats(page.stats, page.scorers);
        addNewLegStats(page.stats, page.scorers);
      } else {
        // ask for continue or game over
        if (winner == -2) {
          page.stats["winner"] = "DRAW";
        } else {
          page.stats["winner"] = visit["player"];
        }

        page.enddate = formatDate(new Date(Date.now()));
        return [newsetp, newlegp];
      }
    }
  }
  return false;
}

export function endGame(page){
  // const set = page.game.sets[page.game.sets.length-1];
  // const leg = set.legs[set.legs.length-1];
  // const visit = leg.visits[leg.visits.length-1];
  // const winner = getWinner(newsetp, page.sets);
  // set["points"] = newsetp;
  // leg["points"] = newlegp;
}

export function extension(page){
  const set = page.game.sets[page.game.sets.length-1];
  page.enddate = undefined;
  // leg["points"] = newlegp;
  // new Leg
  addNewLeg(page);
  addNewLegStats(page.stats, page.scorers);
}


export const getVal = (tr) => {
  const val = tr.trim().toLowerCase();
  if (val == "") {
    return 0;
  }
  if (val == "sb") {
    return 25;
  }
  if (val == "db" || val == "bull") {
    return 50;
  }
  if (val[0] == "s" || val[0] == "o" || val[0] == "i") {
    return parseFloat(val.substring(1));
  }
  if (val[0] == "d") {
    return 2 * parseFloat(val.substring(1));
  }
  if (val[0] == "t") {
    return 3 * parseFloat(val.substring(1));
  }
  if (val[0] == "m") {
    return 0;
  } else {
    // TODO: Check for Na
    return parseFloat(val);
  }
}

/////////////////////////////////////////
// GameProps
/////////////////////////////////////////

export function getGameProps(page, current_set, current_leg) {
  const ret = {};

  if (current_set === undefined) {
    current_set = computed(() => {
      if (page.game != undefined) {
        return page.game.sets[page.game.sets.length - 1]
      }
      return undefined;
    });
  }
  ret.current_set = current_set;


  if (current_leg === undefined) {
    current_leg = computed(() => {      
      if (current_set.value != undefined) {
        return current_set.value.legs[current_set.value.legs.length - 1]
      }
      return undefined;
    });
  }
  ret.current_leg = current_leg;

  const current_toGo = computed(() => {
    if (current_leg.value != undefined) {
      if (current_leg.value.visits.length < 2) {
        return [page.modus, page.modus]
      } else {
        const end = current_leg.value.visits[current_leg.value.visits.length - 1].toGo;
        if (end) return end;
        return current_leg.value.visits[current_leg.value.visits.length - 2].toGo;
      }
    }
    return [0, 0]; // need to get updated for more player
  });
  ret.current_toGo = current_toGo;

  const current_set_points = computed(() => {
    if (current_set.value != undefined) {
      return setPoints(page, current_set.value);
    }
    return undefined;
  });
  ret.current_set_points = current_set_points;

  const current_leg_points = computed(() => {
    if (current_set.value != undefined || current_leg.value != undefined) {
      return legPoints(current_set.value, current_leg.value);
    }
    return undefined;
  });
  ret.current_leg_points = current_leg_points;

  const current_scorer_idx = computed(() => {
    if (current_leg.value != undefined) {
      const len = current_leg.value.visits.length;
      return player2scorer(page)[current_leg.value.visits[len - 1].player];
    }
    return undefined;
  });
  ret.current_scorer_idx = current_scorer_idx;

  const breaks = computed(() => {
    let b = [0,0];
    for (const j in page.game.sets) {
      const set = page.game.sets[j];
      for (const i in set.legs) {
        const l = set.legs[i];
        if (l.visits[l.visits.length-1].toGo === undefined) {
          return b;
        }
        if (l.visits[l.visits.length-1].toGo[0] == 0 && i%2 == 1)
          b[0] += 1;
        else if (l.visits[l.visits.length-1].toGo[1] == 0 && i%2 == 0)
          b[1] += 1;
      }
    }
    return b;
  });
  ret.breaks = breaks;


  const verifySum = (sum) => {
    if (sum > 180 || [179, 178, 176, 175, 173, 172, 169, 166, 163].indexOf(sum) > -1) {
      return -1;
    } else if (current_toGo.value[current_scorer_idx.value] - sum < 0) {
      return -2;
    } if (current_toGo.value[current_scorer_idx.value] - sum == 0) {
      // bogey numbers
      if (page.out == "Double" && (sum > 170 || [169, 168, 166, 165, 163, 162, 159].indexOf(sum) > -1)) {
        return -1;
      }
      return 0;
    } else if (current_toGo.value[current_scorer_idx.value] - sum <= 50) {
      return 1;
    } else if (page.in != "Straight" && current_toGo.value[current_scorer_idx.value] == page.modus && sum != 0) {
      return 3;
    }
    return 2;
  }

  const checkVisit = (throws) => { 
    const tr = throws.split(",");
    let sum = 0;
    let numDarts = 0;
    let fifty = current_toGo.value[current_scorer_idx.value] == 50;
    let tries = 0;
    tr.forEach((t, i) => {
      const val = getVal(t);
      let rest = current_toGo.value[current_scorer_idx.value] - sum;
      if ((rest <= 40 && rest % 2 == 0) || (fifty && val != 18 && val != 10)){
        tries += 1
        fifty = false
      } else if (rest == 50) {
        fifty = true;
      }
      sum += val;
      numDarts += 1
    });
    const res = verifySum(sum);
    return [res, sum, [numDarts, tries]];
  }
  ret.checkVisit = checkVisit;

  return ret;
}
