import { FILE } from 'dns';
import { SpeedCompetitionCategoryResult } from '../models/Competition';
import { Participant } from '../models/Participant';

export interface SpeedRoundPair {
  laneA?: Participant;
  laneB?: Participant;
}

export interface SpeedRound {
  pairs: SpeedRoundPair[];
  roundIndex: number;
  roundName?: string;
}

/**
 * Class for use with speed flowcharts
 */
export class SpeedFlowchart {
  private _result: SpeedCompetitionCategoryResult;

  /**
   *
   * @param {SpeedCompetitionCategoryResult} result
   */
  constructor(result: SpeedCompetitionCategoryResult) {
    this._result = result;
  }

  /**
   *
   * @return {SpeedRound[]} list of speed rounds
   */
  public _convert() {
    const allData: SpeedRound[] = [];

    const rounds =
      Object.keys(this._result.route_names).length > 2
        ? this._result.route_names['2']?.includes('8')
          ? 2
          : 1
        : 0;

    const setAorB = (pair: SpeedRoundPair, participant: Participant) => {
      if (pair.laneA === undefined) pair.laneA = participant;
      else pair.laneB = participant;
    };
    // console.log(this._result.route_names);

    for (const round in this._result.route_names) {
      if (
        Object.keys(this._result.route_names).indexOf(round) >= 0 &&
        parseInt(round) >= 0
      ) {
        // console.log(round);

        if (parseInt(round) === 0) {
          // this is the first round

          // find pairs (always worse vs. best (1-16; 1-15; ...)) (they are sorted by the rank of the better athlete (1-2-3-4-5-6-7-8)

          const qualificationResults: Participant[] = [];

          for (let x = 0; x < this._result.participants.length; x++) {
            qualificationResults.push(this._result.participants[x]);
          }

          qualificationResults.sort(function (a, b) {
            return (
              parseInt(a.result_rank0 ?? '') - parseInt(b.result_rank0 ?? '')
            );
          });

          const nextRoundPairs: SpeedRoundPair[] = [];
          const totalMatches =
            (Object.keys(this._result.route_names).length > 2
              ? this._result.route_names['2']?.includes('8')
                ? 2
                : 1
              : 0) + 2;
          const nextRoundMatches = Math.pow(2, totalMatches - 1);

          for (let i = 0; i < nextRoundMatches; i++) {
            nextRoundPairs.push({
              laneA: qualificationResults[i],
              laneB: qualificationResults[nextRoundMatches * 2 - i - 1],
            });
          }

          // build second round pairs (sorted by the rank of the better athlete and worst vs. best (1-8; 2-7; ... ))

          const sortedFirstRoundPairs: [SpeedRoundPair?, SpeedRoundPair?][] =
            [];

          for (let i = 0; i < nextRoundMatches; i += 1) {
            sortedFirstRoundPairs.push([
              nextRoundPairs.shift(),
              nextRoundPairs.pop(),
            ]);
          }

          // sort these pairs (containing two pairs of athletes) by the rank of the better athlete (1-4;2-3)

          const finalSortedFirstRoundPairs = [];

          for (let i = 0; i < nextRoundMatches / 4; i++) {
            finalSortedFirstRoundPairs.push(sortedFirstRoundPairs[i]);
            finalSortedFirstRoundPairs.push(
              sortedFirstRoundPairs[nextRoundMatches / 2 - i - 1],
            );
          }

          // convert the list of pairs of pairs of athletes back to a single list of pairs of athletes

          const finalFirstRoundPairs: SpeedRoundPair[] = [];

          for (let i = 0; i < finalSortedFirstRoundPairs.length; i++) {
            if (finalSortedFirstRoundPairs[i][0] !== undefined)
              finalFirstRoundPairs.push(finalSortedFirstRoundPairs[i][0]!);
            if (finalSortedFirstRoundPairs[i][1] !== undefined)
              finalFirstRoundPairs.push(finalSortedFirstRoundPairs[i][1]!);
          }

          // push the first round to all data
          allData.push({
            pairs: finalFirstRoundPairs,
            roundIndex: 2,
            roundName: this._result.route_names[2] ?? '',
          });

          // console.log(allData);
        } else if (parseInt(round) > 0) {
          // this is not the first round
          const nextRound: SpeedRound = {
            pairs: [],
            roundIndex: -1,
            roundName: '',
          };

          // only used when  the current round is the 1/2 final
          const smallFinal: SpeedRoundPair = {};
          const Final: SpeedRoundPair = {};

          for (
            let i = 0;
            i < allData[allData.length - 1].pairs.length;
            i += 1
          ) {
            const thisPair: SpeedRoundPair =
              allData[allData.length - 1].pairs[i];
            let thisWinner;
            let thisLooser;
            const thisWinnerIsFirstOfNewPair = i % 2 === 0;

            if (thisPair.laneA === undefined || thisPair.laneB === undefined) {
              continue;
            }

            if (thisWinnerIsFirstOfNewPair) {
              nextRound.pairs.push({});
            }

            if (
              Object.keys(thisPair.laneA).indexOf('result_rank' + round) < 0 ||
              Object.keys(thisPair.laneB).indexOf('result_rank' + round) < 0
            ) {
              continue;
            }

            if (
              parseInt(thisPair.laneA['result_rank' + round] as string) <
              parseInt(thisPair.laneB['result_rank' + round] as string)
            ) {
              thisWinner = thisPair.laneA;
              thisLooser = thisPair.laneB;
            } else if (
              parseInt(thisPair.laneA['result_rank' + round] as string) >
              parseInt(thisPair.laneB['result_rank' + round] as string)
            ) {
              thisWinner = thisPair.laneB;
              thisLooser = thisPair.laneA;
            } else {
              // no result yet!!
              /* console.log(
                'got no winner yet, rank 0: ' +
                  thisPair.laneA['result_rank' + round] +
                  ' rank 1: ' +
                  thisPair.laneB['result_rank' + round],
              );*/
              continue;
            }

            // console.log(thisWinner['firstname'] + ' has won in round ' + round);

            if (parseInt(round) - rounds === 2) {
              // if we are in the 1/2 final
              setAorB(Final, thisWinner);
              setAorB(smallFinal, thisLooser);
            } else {
              setAorB(nextRound.pairs[nextRound.pairs.length - 1], thisWinner);
            }
          }

          if (smallFinal.laneA !== undefined && Final.laneA !== undefined) {
            // Final
            allData.push({
              pairs: [Final],
              roundIndex: parseInt(round) + 2,
              roundName:
                this._result.route_names[String(parseInt(round) + 2)] +
                ' / ' +
                this._result.route_names[String(parseInt(round) + 1)],
            });
            // small Final
            allData.push({
              pairs: [smallFinal],
              roundIndex: parseInt(round) + 1,
            });
          } else {
            nextRound.roundIndex = parseInt(round) + 1;
            nextRound.roundName = this._result.route_names[parseInt(round) + 1];
            allData.push(nextRound);
          }
        }
      }
    }

    return allData;
  }
}
