import { reactive, ref, computed, onMounted, onUnmounted, toRaw } from "vue";
import { getQuery, setKirby } from "../../kirby.js";
import { handleActive, ArrowVerticalKeyHandler, ArrowHorizontalKeyHandler, NumberKeyHandler } from "../../handlers.js";
import { overlayAndGet, powerStateMachine, overlayAndPop, popLastElem } from "../../componentPromise.js";
import { autodartsState } from '../../websocket.js'
import { getGameProps, initGame, initStats, storeVisit, formatDate, removeLastVisit, extension, getFinalWinner, getVal } from "./logic.js";
import { playSound } from '../../sound.js';

const html = (v) => { return v[0] };

////////////////////////////////////////////////////////////////////////////////
// Components
//////////////////////////in//////////////////////////////////////////////////////

const pregame = {
  props: ['page','active', 'stack'],
  setup(props, context) {
    handleActive(props, [ArrowVerticalKeyHandler]);

    const selectPlayer = async (i) => {
      if (props.active) {
        let [result, error] = await overlayAndPop("d-select", { type: "player", options: props.page.participants, class:"overlay"}, props.stack);
        if (error === undefined) {
          props.page.players[i] = result;
        }
      }
    }

    const selectScorer = async (i) => {
      if (props.active) {
        const [scorer, error] = await overlayAndPop("d-select", { type: "scorer", options: props.page.participants, class:"overlay"}, props.stack);
        if (error === undefined) {
          if (scorer.member.length > 1){ 
            const [captain, err] = await overlayAndPop("d-select", { type: "player", title: "Team Captain", options: [scorer.member[0],scorer.member[1]], class:"overlay"}, props.stack);
            if (err !== undefined){
              return selectScorer(i);
            }
            scorer.member = [captain, scorer.member[0] == captain ? scorer.member[1] : scorer.member[0]];
          }
          props.page.scorers[i] = scorer;
        }
      }
    }

    const selectX01 = async () => {
      if (props.active) {
        const [result, error] = await overlayAndPop("d-select", { options: [ "301", "501" ], class:"overlay"}, props.stack);
        if (error === undefined) {
          props.page.modus = result;
        }
      }
    }

    const selectIn = async () => {
      if (props.active) {
        const [result, error] = await overlayAndPop("d-select", { options: [ "Straight", "Double" ], class:"overlay"}, props.stack);
        if (error === undefined) {
          props.page.in = result;
        }
      }
    }

    return { selectX01, selectIn, selectScorer }
  },
  template: html`
  <nav class="gamesetup" :disabled="!active">
    <h1>Game Setup</h1>
    <div class="menu">
      <!-- <div><span class="label">Name:</span><d-inputElem v-index="active" v-model="game.title"></d-inputElem></div> -->
      <div><span class="label">Scorer 1:</span><d-scorerElem v-index="active" @click="selectScorer(0)" :scorer="page.scorers[0]"></d-scorerElem></div>
      <div><span class="label">Scorer 2:</span><d-scorerElem v-index="active" @click="selectScorer(1)" :scorer="page.scorers[1]"></d-scorerElem></div>

      <!-- <div><span class="label">X01:</span><d-plainElem v-index="active" @click="selectX01()" :text="page.modus"></d-plainElem></div> -->
      <!-- <div><span class="label">In:</span><d-plainElem v-index="active" @click="selectIn()" :text="page.in"></d-plainElem></div> -->
      <div><span class="label">Best of Sets:</span><d-inputElem v-index="active" v-model="page.sets"></d-inputElem></div>
      <div><span class="label">Best of Legs (per Set):</span><d-inputElem v-index="active" v-model="page.legs"></d-inputElem></div>
      <div><d-plainElem text="Start" v-index="active" @click="$emit('resolve', page)"></d-plainElem></div>
    </div>
  </nav>
  `
}

export const bullselect = {
  props: ['scorers', 'active', 'stack'],
  setup(props, { emit }) {
    handleActive(props, [ArrowHorizontalKeyHandler, NumberKeyHandler]);

    const children = computed(() => {
      const items = [];
      for (let i in props.scorers){
        const player = props.scorers[i].member[0];
        
        items.push({
          component: "d-squareElem",
          props: {
            text: player.forename + " " + player.surname,
            icon: player.img ? player.img : '/assets/img/placeholder_person.png'
          },
          onClick: () => {
            emit("resolve", props.scorers[i]);
          }
        })
      }
      return items;
    });

    return { children }
  },
  template: html`
  <div class="gamesetup">
    <h1 class="bull">Who won bull?</h1>
    <d-list :withshortkey="true" :elements="children" type="horizontal" />
  </div>
  `
}



const score = {
  props: ['page', 'justlegs', 'current_set_points', 'current_leg_points'],
  setup(props) {
    return { }
  },
  template: html`
    <div class="score">
      <div v-if="!justlegs" class="sets">
        <h2 v-for="(idx) in [0,1]">{{ current_set_points[idx] }}</h2>
        <div class="info">
          <h3>Best of {{ page.sets }}</h3>
          <h2>Sets</h2>
        </div>
      </div>
      <div class="legs">
        <h2 v-for="(idx) in [0,1]">{{ current_leg_points[idx] }}</h2>
        <div class="info">
          <h3>Best of {{ page.legs }}</h3>
          <h2>Legs</h2>
        </div>
      </div>
    </div>
  `
}

const scorer = {
  props: ['page', 'id', 'current_stat', 'current_leg'],
  setup(props) {
    const addStats = (stat1, stat2) => {
      let res = {
        average: [stat1.average[0]+stat2.average[0], stat1.average[1]+stat2.average[1]],
        first9: [stat1.first9[0]+stat2.first9[0], stat1.first9[1]+stat2.first9[1]],
        "60+": stat1["60+"]+stat2["60+"],
        "100+": stat1["100+"]+stat2["100+"],
        "140+": stat1["140+"]+stat2["140+"],
        "180": stat1["180"]+stat2["180"],
        "checkouts": [stat1.checkouts[0]+stat2.checkouts[0], stat1.checkouts[1]+stat2.checkouts[1]],
        "checkoutPoints": [...stat1.checkoutPoints, ...stat2.checkoutPoints]
      }
      if ("checkins" in stat1 && "checkins" in stat2){
        res["checkins"] = [stat1.checkins[0]+stat2.checkins[0], stat1.checkins[1]+stat2.checkins[1]];
        res["checkinPoints"] = [...stat1.checkinPoints, ...stat2.checkinPoints];      
      }
      
      return res;
    }
    const inspect = !!props.page.enddate;
    const tourStats = computed(() => {
      if (inspect) {
       return props.page.tournamentStats;
      }
      return props.page.tournamentStats.map((s,i) => {
        if (s.length == 0){
          return props.page.stats.stats[i];
        }
        return addStats(s,props.page.stats.stats[i])
      });
    });

    const getAverage = (avg) => {
      return avg && avg[1] != 0 ? ((3*avg[0])/avg[1]).toFixed(1) : "-";
    }
    const getCheckout = (checkout) => {
      return checkout && checkout[1] != 0 ? Math.round(1000*checkout[0]/checkout[1])/10 : "- "
    }
    const getMax = (checkouts) => {
      if (checkouts && checkouts.length != 0) {
        return Math.max(...checkouts);
      }
      return "-"
    }
    
    const scorer = ref(props.page.scorers[props.id]);
    const allScorerIndices = computed(() => scorer.value.member.map((p) => props.page.stats.stats.map((s) => s.player).indexOf(p.uuid)));
    
    const currentScorerIdx = computed(() => {
      if (scorer.value.member.length == 1){
        return 0;
      }
      const idx = props.page.scorers[props.id].member.map((p) => p.uuid).indexOf(props.current_leg.visits.at(-1).player);      
      return idx;
    });
    
    const tnmStats = computed(() => {
      if (currentScorerIdx.value == -1){
        return addStats(tourStats.value[allScorerIndices.value[0]], tourStats.value[allScorerIndices.value[1]]);
      }
      return tourStats.value[allScorerIndices.value[currentScorerIdx.value]];
    });
    
    const matchStats = computed(() => {
      if (currentScorerIdx.value == -1){
        return addStats(props.page.stats.stats[allScorerIndices.value[0]], props.page.stats.stats[allScorerIndices.value[1]]);
      }
      return props.page.stats.stats[allScorerIndices.value[currentScorerIdx.value]];
    });

    const legStats = computed(() => {
      if (currentScorerIdx.value == -1){
        return addStats(props.current_stat.stats[allScorerIndices.value[0]], props.current_stat.stats[allScorerIndices.value[1]]);
      }
      return props.current_stat.stats[allScorerIndices.value[currentScorerIdx.value]];
    });

    
    return { tnmStats, matchStats, legStats, getAverage, getCheckout, getMax, scorer, currentScorerIdx }
  },
  template: html`
    <div class="player" :class="{ 'team': scorer.member.length == 2 }">
      <template v-if="scorer.member.length == 1">
        <img style="width: 100%" :src="scorer.member[0].img ? scorer.member[0].img : '/assets/img/placeholder_person.png'">
        <h2 class="name">{{ scorer.member[0].forename }} {{ scorer.member[0].surname }}</h2>
        <h3 class="nickname">{{ scorer.member[0].nickname }}</h3>
      </template>
      <template v-if="scorer.member.length == 2">
        <div class="team images">
          <img :class="{ 'active': currentScorerIdx == 0, 'inactive': currentScorerIdx == 1  }" :src="scorer.member[0].img ? scorer.member[0].img : '/assets/img/placeholder_person.png'">
          <img :class="{ 'active': currentScorerIdx == 1, 'inactive': currentScorerIdx == 0  }" :src="scorer.member[1].img ? scorer.member[1].img : '/assets/img/placeholder_person.png'">
        </div>
        <div class="team titles">
          <div class="title" :class="{ 'active': currentScorerIdx == -1 }">
            <h2 class="name">{{ scorer.member[0].forename }} + {{ scorer.member[1].forename }}</h2>
          </div>
          <div class="title" :class="{ 'active': currentScorerIdx == 0 }">
            <h2 class="name">{{ scorer.member[0].forename }} {{ scorer.member[0].surname }}</h2>
            <h3 class="nickname">{{ scorer.member[0].nickname }}</h3>
          </div>
          <div class="title" :class="{ 'active': currentScorerIdx == 1 }">
            <h2 class="name">{{ scorer.member[1].forename }} {{ scorer.member[1].surname }}</h2>
            <h3 class="nickname">{{ scorer.member[1].nickname }}</h3>
          </div>
        </div>
      </template>
      <div class="stats">
        <div class="row header">
          <div>Stat</div><div>Tnm</div><div>Match</div><div>Leg</div>
        </div>
        <div class="row">
          <div>Avg:</div><div>
            {{ getAverage(tnmStats.average) }}
          </div><div>
            {{ getAverage(matchStats.average) }}
          </div><div>
            {{ getAverage(legStats.average) }}
          </div>
        </div>
        <div class="row">
          <div>First 9:</div><div>
            {{ getAverage(tnmStats.first9) }}
          </div><div>
            {{ getAverage(matchStats.first9) }}
          </div><div>
            {{ getAverage(legStats.first9) }}
          </div>
        </div>
        <div class="row">
          <div>60+:</div><div>{{ tnmStats["60+"] }}</div><div>{{ matchStats["60+"] }}</div><div>{{ legStats["60+"] }}</div>
        </div>
        <div class="row">
          <div>100+:</div><div>{{ tnmStats["100+"] }}</div><div>{{ matchStats["100+"] }}</div><div>{{ legStats["100+"] }}</div>
        </div>
        <div class="row">
          <div>140+:</div><div>{{ tnmStats["140+"] }}</div><div>{{ matchStats["140+"] }}</div><div>{{ legStats["140+"] }}</div>
        </div>
        <div class="row">
          <div>180:</div><div>{{ tnmStats["180"] }}</div><div>{{ matchStats["180"] }}</div><div>{{ legStats["180"] }}</div>
        </div>
        <div class="row" v-if="page.in == 'Double'">
          <div>Ch. I. %:</div><div>{{ getCheckout(tnmStats.checkins) }}%</div><div>{{ getCheckout(matchStats.checkins) }}%</div><div>{{ getCheckout(legStats.checkins) }}%</div>
        </div>
        <div class="row" v-if="page.in == 'Double'">
          <div>Best Ch.:</div><div>{{ getMax(tnmStats.checkinPoints) }}</div><div>{{ getMax(matchStats.checkinPoints) }}</div><div>{{ getMax(legStats.checkinPoints) }}</div>
        </div>
        <div class="row">
          <div>Ch. O. %:</div><div>{{ getCheckout(tnmStats.checkouts) }}%</div><div>{{ getCheckout(matchStats.checkouts) }}%</div><div></div>
        </div>
        <div class="row">
          <div>Best Ch.:</div><div>{{ getMax(tnmStats.checkoutPoints) }}</div><div>{{ getMax(matchStats.checkoutPoints) }}</div><div></div>
        </div>
      </div>
    </div>
  `
}


const game = {
  props: ['active', 'stack', 'scorers', 'current_leg', 'max'],
  setup(props, context) {
    const visits = computed(() => props.current_leg ? props.current_leg.visits : undefined );
    
    const getScorersVisits = (scorer) => {

      // const vs = visits.value.filter((v,i) => i % 2 == props.scorers.map((p) => p.uuid).indexOf(uuid) % 2);
      const vs = visits.value.filter((v,i) => scorer.member.map((p) => p.uuid).indexOf(v.player) != -1);
      
      if (vs.length < 9) {
        for (var i = vs.length; i < 9; i++) {
          vs.push({ "sum": "", "toGo":["",""]})
        }
      } else if (vs.length*2 < visits.value.length) {

        vs.push({ "sum": "", "toGo":["",""]});
      }
      return vs;
    }
    return { getScorersVisits }
  },
  template: html`
    <div class="game">
      <div class="headding">
        <div class="headding points scorer1">Points</div>
        <div class="headding toGo scorer1">ToGo</div>
        <div class="headding rounds">Round</div>
        <div class="headding points scorer2">Points</div>
        <div class="headding toGo scorer2">ToGo</div>
      </div>
      <div class="body">
        <div class="points scorer1"></div>
        <div class="toGo scorer1">{{ max }}</div>
        <div class="rounds">0</div>
        <div class="points scorer2"></div>
        <div class="toGo scorer2">{{ max }}</div>
        <template v-for="j in [1,2]" v-if="scorers">
          <template v-for="visit, i in getScorersVisits(scorers[j-1])">
            <template v-if="visit.toGo === undefined">
              <div :class="'points scorer'+j+' input'">
                <slot />
              </div>
              <div :class="'toGo scorer'+j"></div>
            </template>
            <template v-if="visit.toGo !== undefined">
              <div :class="'points scorer'+j">{{ visit.sum }}</div>
              <div :class="'toGo scorer'+j">{{ visit.toGo[j-1] }}</div>
            </template>
            <div class="rounds" v-if="j == 1">{{ (i+1)*3 }}</div>
          </template>
        </template>
      </div>
    </div>
  `
}

const gameinput = {
  props: ['input','active', 'stack', 'toGo'],
  setup(props, context) {
    handleActive(props, []);
    const check_remove = (event) => {
      if (!event.repeat && event.target.value.length < 1) {
        event.preventDefault();
        context.emit('reject', -2);
      }
    }
    let inputtext = ref(props.input || "")

    // if (websocket){
    let throwdetect = (event) => {
      if (event.detail.data.event == "Takeout started"){
        return
      }
      if (event.detail.data.event == "Takeout finished" && inputtext.value != "" && inputtext.value != undefined && inputtext.value != NaN){
        context.emit('resolve', inputtext.value);
        return
      }
      let throws = event.detail.data.throws; 
      if (!throws){
        return
      }
      const scored = throws.at(-1).segment.name
      if (throws.length == 1) {
        inputtext.value = scored;
      } else {
        inputtext.value += "," + scored
      }
      const tr = inputtext.value.split(",");
      let sum = 0;
      tr.forEach((t, i) => {
        sum += getVal(t);
      });

      if (props.toGo - sum == 0 && scored.startsWith("D")) {
        playSound("game_shot");
        return
      }
      if (props.toGo - sum <= 1) {
        playSound("0");
        return
      }
      if (throws.length == 3) {
        playSound(""+sum);
      }    
    };

    onMounted(() => {
      document.body.addEventListener("autodarts", throwdetect); 
      if (props.toGo < 170 && [169, 168, 166, 165, 163, 162, 159].indexOf(props.toGo) == -1) {      
        playSound("require_"+ props.toGo);
      }
    })

    onUnmounted(() => document.body.removeEventListener("autodarts", throwdetect))    
    // }
    const keyhandler = (e) => {
       if (e.key == "F1" || e.keyCode == 112) {
         e.preventDefault();
         context.emit('resolve', "60");
       } else if (e.key == "F2" || e.keyCode == 113) {
         e.preventDefault();
         context.emit('resolve', "45");
       } else if (e.key == "F3" || e.keyCode == 114) {
         e.preventDefault();
         context.emit('resolve', "41");
       } else if (e.key == "F4" || e.keyCode == 115) {
         e.preventDefault();
         context.emit('resolve', "26");
       } else if (e.key == "/" || e.keyCode == 111 || e.key == "r") {
        e.preventDefault();
        context.emit('resolve', -1*parseFloat(e.target.value));
      }
    }
    return { check_remove, keyhandler, inputtext }
  },
  template: html`
  <input v-index="active" v-model="inputtext" @keydown="keyhandler" @keydown.enter.stop="$emit('resolve', inputtext)" @keydown.backspace="check_remove($event)">
  `
}


const xoi = {
  props: ['page', 'active', 'stack', 'inspect', 'watch'],
  components: {
    "d-scorer": scorer,
    "d-score": score,
    "d-game": game
  },
  setup(props, context) {
    const gamestack = reactive([]);
    const inspectstack = reactive([]);
    let current_set, current_leg;
    const set_id = ref(props.page.game.sets.length - 1);
    const leg_id = ref(props.page.game.sets[set_id.value].legs.length - 1);
    let current_stat;
    if (props.inspect) {
      handleActive(props, [ArrowHorizontalKeyHandler]);
      current_set = computed(() => props.page.game.sets[set_id.value]);
      current_leg = computed(() => current_set.value.legs[leg_id.value]);
      current_stat = computed(() => props.page.stats?.sets[set_id.value].legs[leg_id.value]);
    }
    if (props.watch) {
      setInterval(async () => {
        let gs = await refreshGame(props.page.id);
        props.page.game = gs.game;
        props.page.stats = gs.stats;
      }, 2000);
    }
    const computedProps = getGameProps(props.page, current_set, current_leg);
    
    if (!props.inspect) {
      current_stat = computed( () => {
        return props.page.stats?.sets[props.page.stats.sets.length-1].legs[computedProps.current_set.value.legs.length-1];
      });
    }

    const mounted = onMounted(async () => {
      if (props.inspect || props.watch) {
      } else {
        const winner = await gameHandler(gamestack, props.stack, props.page, computedProps)
        if (winner === undefined){
          context.emit('reject', winner);
        } else {
          context.emit('resolve', winner);
        }
      }
    })
    return { ...computedProps, set_id, leg_id, gamestack, inspectstack, current_stat, autodartsState}
  },
  template: html`
    <div class="xoi" :class="autodartsState.status">
      <div class="bigToGo one" :class="{'active' : current_scorer_idx==0 }">{{ current_toGo[0] }}</div>
      <d-score :page="page" :justlegs="page && page.sets == 1" :current_set_points="current_set_points" :current_leg_points="current_leg_points"></d-score>
      <div class="bigToGo two" :class="{'active' : current_scorer_idx==1 }">{{ current_toGo[1] }}</div>
      <d-scorer class="scorer1" :page="page" :id="0" :current_stat="current_stat" :current_leg="current_leg"></d-scorer>
      <d-game :active="active" :stack="stack" :scorers="page.scorers" :max="page.modus" :current_leg="current_leg" >
        <d-renderer :stack="gamestack"></d-renderer>
      </d-game>
      <d-scorer class="scorer2" :page="page" :id="1" :current_stat="current_stat" :current_leg="current_leg"></d-scorer>
      <div class="nav" v-if="inspect">
        <span class="label" v-if="page.sets != 1">Set:</span><d-plainElem style="padding:0.2em 0.5em" :text="set_id+1"  v-if="page.sets != 1" v-index="active" @click="set_id = (set_id+1)%page.game.sets.length; leg_id=0"></d-plainElem><span class="label">Leg:</span><d-plainElem style="padding:0.2em 0.5em" :text="leg_id+1" v-index="active"  v-autofocus="true" @click="leg_id = (leg_id+1)%current_set.legs.length"></d-plainElem>
      </div>
      <div class="nav" v-if="!!!inspect">
        <span>Shortkeys:</span><span>60 (F1)</span><span>45 (F2)</span><span>41 (F3)</span><span>26 (F4)</span><span>{{ autodartsState.status}}</span>
      </div>
    </div>
  `
}

////////////////////////////////////////////////////////////////////////////////
// Dialogs
////////////////////////////////////////////////////////////////////////////////

const numDartsDialog = () => {
  const buttons = [];
  for (var i = 1; i <= 3; i++) {
    buttons.push({
      "component": "d-plainElem",
      "props" : {
        "text": `${i}`
      },
      "result" : i
    })
  }
  return {
    "withshortkey": true,
    "title": "Congratulations!",
    "text": `How many darts did you need?`,
    "buttons": buttons
  };
}

const numCheckoutTriesDialog = (numDarts, start=1) => {
  const buttons = [];
  for (var i = start; i <= numDarts; i++) {
    buttons.push({
      "component": "d-plainElem",
      "props" : {
        "text": `${i}`,
        "autofocus": i == 0,
        "data-tabindex": i
      },
      "result" : i
    })
  }
  return {
    "withshortkey": true,
    "title": "Checkout Tries",
    "text": `How many tries on a Checkout?` ,
    "buttons": buttons
  };
}

const numCheckinTriesDialog = (numDarts, start=1) => {
  const buttons = [];
  for (var i = start; i <= numDarts; i++) {
    buttons.push({
      "component": "d-plainElem",
      "props" : {
        "text": `${i}`,
        "autofocus": i == 0,
        "data-tabindex": i
      },
      "result" : i
    })
  }
  return {
    "withshortkey": true,
    "title": "Checkin Tries",
    "text": `How many tries on a Checkin?` ,
    "buttons": buttons
  };
}

const impossibleDialog = (sum) => {
  return {
    "withshortkey": true,
    "title": "Impossible",
    "text": `A score of ${sum} is not possible`,
    "buttons": [{
      "component": "d-plainElem",
      "props" : {
        "text": "ok"
      },
      "result" : "ok"
    }]}
}

const gameOverDialog = (winner, points, breaks) => {
  return {
    "withshortkey": true,
    "title": `Game Over`,
    "text": `${winner != "DRAW" ? "The winner is": ""} ${winner} with ${points[0]}-${points[1]} (Breaks: ${breaks[0]}-${breaks[1]})`,
    "buttons": [
      {
        "component": "d-plainElem",
        "props" : {
          "text": "End Game"
        },
        "result" : "end"
      },{
      "component": "d-plainElem",
      "props" : {
        "text": "Continue"
      },
      "result" : "continue"
    }]
  }
}

////////////////////////////////////////////////////////////////////////////////
// API
////////////////////////////////////////////////////////////////////////////////

function refreshGame(id){
  return getQuery(`site.find('${id}')`, {
    select: {
      game: "page.rounds.parseJSON",
      stats: "page.stats.parseJSON",
      tournamentStats: "page.tournamentStats"
    }
  })
}

async function reloadGame(page){
  let gs = await refreshGame(page.id);
  page.game = gs.game;
  page.stats = gs.stats;
  page.tournamentStats = gs.tournamentStats;
}

function getGame(id){
  const playerInfo = {
    forename: "page.forename",
    surname: "page.surname",
    nickname: "page.nickname",
    uuid: "page.uuid",
    img: "page.pic.toFile?.thumbnail(350).url"
  };
  return getQuery(`site.find('${id}')`, {
    select: {
      title: "page.title",
      id: "page.id",
      modus: "page.max.toInt",
      in: "page.in",
      out: "page.out",
      game: "page.rounds.parseJSON",
      stats: "page.stats.parseJSON",
      sets: "page.sets.toInt",
      legs: "page.legs.toInt",
      startdate: "page.Startdate",
      enddate: "page.Enddate",
      tournamentStats: "page.tournamentStats",
      participants: {
        query: "page.parent.scorers",
        select: {
          member: {
            query: "structureItem.member.toPages",
            select: playerInfo
          }
        }
      },
      scorers: {
        query: "page.scorers.toStructure",
        select: {
          member: {
            query: "structureItem.member.toPages",
            select: playerInfo
          }
        }
      }
    }
  })
}

function savePregame(page){
  return setKirby(page.id, {
      sets: page.sets,
      legs: page.legs,
      max: page.modus,
      in: page.in,
      scorers: page.scorers.map((s) => {
        const copy = Object.assign({},s);
        copy.member = copy.member.map((p) => p.uuid)
        return copy
      }),
      startdate: page.startdate ? page.startdate : "",
      rounds: page.game ? JSON.stringify(page.game) : "",
      stats: page.stats ? JSON.stringify(page.stats) : ""
  });
}

function saveGame(page){
  return setKirby(page.id, {
      rounds: page.game ? JSON.stringify(page.game) : "",
      stats: page.stats ? JSON.stringify(page.stats) : "",
      enddate: page.enddate ? page.enddate : "",
  });
}


////////////////////////////////////////////////////////////////////////////////
// State Machine
////////////////////////////////////////////////////////////////////////////////

// Checkout Pipeline
const checkoutPipeline = (stack) => {
  return {
    0: async (sum, reGet) => {
      // Ask for num Darts
      const [numDarts, error] = await overlayAndGet("d-dialog", numDartsDialog(), stack, reGet);
      if (error != undefined) {
        popLastElem(stack);
        return [undefined, [-1, -1]];
      }
      return [1, [numDarts, 1]];
    },
    1: async ([numDarts, start], reGet) => {
      // Ask for checkoutTries
      if (numDarts <= start) {
        // Store with one checkout try
        popLastElem(stack);
        return [undefined, [1, 1]];
      }
      const [tries, error] = await overlayAndPop("d-dialog", numCheckoutTriesDialog(numDarts, start), stack);
      if (error != undefined) {
        if (start == 0) {
          return [undefined, [-1, -1]];
        }
        return [-1, undefined];
      }
      if (start == 1) {
        popLastElem(stack);
      }
      return [undefined, [numDarts, tries]];
    }
  };
};


// Game State Machine
const gameStateMachine = (gamestack, stack, page, computedProps) => {
  return {
    0: async (input, reGet) => {
      // Dispatcher
      if (page.stats.winner){
        return [undefined, page.stats.winner];
      }
      // Check for Game State
      return [1, undefined];
    },
    1: async (input, reGet) => {
      // Normal Game Loop
      // Get Game Input
      const toGo = computedProps.current_toGo.value[computedProps.current_scorer_idx.value * 1]
      let [visit, error] = await overlayAndPop("d-gameinput", { input: input, toGo: toGo}, gamestack);
      
      if (parseFloat(visit) < 0 ){
        visit = ""+(computedProps.current_toGo.value[computedProps.current_scorer_idx.value * 1] + parseFloat(visit));
      }
        // back/delete last throw
      if (error != undefined) {
        const val = removeLastVisit(page);
        if (val === undefined) {
          return [undefined, undefined];
        }
        saveGame(page);
        if (val == undefined) return [1, val];
        return [1, val.join(",")];
      }

      // Validate throw
      let numDarts, tries;
      const [ret, sum, extra] = computedProps.checkVisit(visit);
      [numDarts, tries] = extra;
      const manual = !(visit.split(",").length > 1) && !visit.toLowerCase().startsWith("s") && !visit.toLowerCase().startsWith("d") && !visit.toLowerCase().startsWith("t") && !visit.toLowerCase().startsWith("b")
      console.log(manual);
      
      if (ret == -1) {
        // Impossible
        const [_, error] = await overlayAndPop("d-dialog", impossibleDialog(sum), stack);
        return [1, visit];
      } else if (ret == -2) {
        // Bust TODO
        // const [_, error] = await overlayAndPop("d-dialog", impossibleDialog(sum), stack);
        storeVisit(page, visit.split(","), 0, 3, 0);
        saveGame(page);
        return [1, undefined];
      } else if (ret == 3) {
        // Checkins
        const [ret, error] = await overlayAndPop("d-dialog", numCheckinTriesDialog(3), stack);
        if (error) {
          return [1, visit];
        }
        storeVisit(page, visit.split(","), sum, 3, 0, ret);
        saveGame(page);
        return [1, undefined]
      } else if (ret == 2) {
        // Normal
        storeVisit(page, visit.split(","), sum, 3, 0);   
        saveGame(page);
        return [1, undefined]
      } else if (ret == 0 && manual) {
        // Checkout: Ask for num Darts
        [numDarts, tries] = await powerStateMachine(checkoutPipeline(stack), stack, /*initState=*/0, /*initInput=*/sum);
      } else if (ret == 1 && manual) {
        // <=50: Ask for checkout tries
        [numDarts, tries] = await powerStateMachine(checkoutPipeline(stack), stack, /*initState=*/1, /*initInput=*/[3,0]);
      }

      if (numDarts == -1) {
        // Error/Back
        return [1, visit];
      } else {
        const points = storeVisit(page, visit.split(","), sum, numDarts, tries);
        console.log(points);
        
        if (points){
          // Game Over
          const winner = getFinalWinner(page);
          
          let name = "DRAW"
          if (winner != -2){
            name = page.scorers[winner].member.map((p) => p.forename).join(" + ");
          }
          const [answer, error] = await overlayAndPop("d-dialog", gameOverDialog(name, points[1], computedProps.breaks.value), stack);
          if (error != undefined) {
            extension(page);
            const val = removeLastVisit(page);
            saveGame(page);
            if (val == undefined) return [1, val];
            return [1, val.join(",")];
          }
          if (answer == "end") {
            saveGame(page);
            return [undefined, page.stats.winner];
          } else {
            extension(page);
            saveGame(page);
          }
        } else {
          saveGame(page);
        }
        return [1, undefined];
      }
    },
  };
}

export const gameHandler = async (gamestack, stack, page, computedProps) => {
  const sm = gameStateMachine(gamestack, stack, page, computedProps);
  return powerStateMachine(sm, gamestack);
}

function getUrlParam(name) {
  var url_string = window.location;
  let url = new URL(url_string);
  let params = new URLSearchParams(url.search);
  return params.get(name);
}

// General State Machine
const stateMachine = (stack, page) => {
  return {
    0: async (input, reGet) => {
        // Dispatcher
        // Check if in watchmode
        // Check Game State:        
        if ( page.scorers.length != 2 || page.startdate === undefined || page.startdate === ""){
          // Pre Game
          return [1, page];
        } else if (page.enddate === undefined || page.enddate === ""){
          if (page.game === "" || page.game === undefined || page.game === null) {
            // Who won Bull?
            return [2, page];
          } else {
            // In Game
            if (getUrlParam("w") != null){
              // Watch Game
              return [5, undefined];
            }
            return [3, page];
          }
        } else {
          // Post Game
          return [4, undefined];
        }
    },
    1: async (page, reGet) => {
        // Pre Game
        const [result, error] = await overlayAndPop("d-pregame", { page: page}, stack);
        if (error != undefined) {
          var re = /^https?:\/\/[^/]+/i;
          window.setTimeout(() => {
            window.location.href = re.exec(window.location.href)[0];
            return false;
          },1);
          return [undefined, undefined];
        }
        page.startdate = formatDate(new Date(Date.now()));
        // Update game in database
        const ret = await savePregame(page);
        if (ret.status != "ok") {
          console.error("Error save page:", ret.status, ret.error);
        }
        return [0, page];
    },
    2: async (page, reGet) => {
        // Ask for Bull
        const [scorer, error] = await overlayAndPop("d-bullselect", { scorers: page.scorers}, stack);
        if (error != undefined) {
          page.startdate = undefined;
          const ret = await savePregame(page);
          return [1, page];
        }
        // reorder Scorer
        if (scorer !== page.scorers[0]){
          page.scorers = [page.scorers[1], page.scorers[0]];
        }
        
        // Setup Game
        initGame(page);
        initStats(page);
        const ret = await savePregame(page);
        await reloadGame(page);
        
        return [3, page];
    },
    3: async (page, reGet) => {
        // In Game
        const [result, error] = await overlayAndPop("d-xoi", { page: page, inspect: false }, stack);
        if (error) {
          page.game = undefined;
          page.stats = undefined;
          const ret = await savePregame(page);
          return [2, page];
        }
        await reloadGame(page);
        return [4, result];
    },
    4: async (winnerUUID, reGet) => {
        const [res, e] = await overlayAndPop("d-xoi", { page: page, inspect: true }, stack);
        var re = /^https?:\/\/[^/]+/i;
        window.setTimeout(() => {
          window.location.href = re.exec(window.location.href)[0];
          return false;
        },1);
        return [4, res];
    },
    5: async (_, reGet) => {
        const [res, e] = await overlayAndPop("d-xoi", { page: page, watch: true }, stack);
        var re = /^https?:\/\/[^/]+/i;
        window.setTimeout(() => {
          window.location.href = re.exec(window.location.href)[0];
          return false;
        },1);
        return [5, res];
    }
  };
}


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

export const initXoiView = (app) => {
  app.component('d-pregame', pregame).component('d-xoi', xoi).component('d-bullselect', bullselect).component('d-gameinput', gameinput)
}

export const xoiHandler = async (stack, id) => {
  const page = reactive(await getGame(id));
  const sm = stateMachine(stack, page);
  await powerStateMachine(sm, stack);
}
