export type VetorXYType = [number, number];
export type VetorXYZType = [number, number, number];
export type VetorType = VetorXYType | VetorXYZType;

export class GA {
  static produtoVetorial(vetorA: VetorXYZType, vetorB: VetorXYZType, head?: VetorXYZType) {
    const [detAI, detAJ, detAK] = vetorA;
    const [detBI, detBJ, detBK] = vetorB;
    const hasHead = Boolean(head);

    const positivos = [(detAJ * detBK), (detAK * detBI), (detAI * detBJ)].map((value, index) =>
      hasHead ? value * (head as number[])[index] : value
    );
    const negativos = [(detAK * detBJ), (detAI * detBK), (detAJ * detBI)].map((value, index) =>
      hasHead ? value * (head as number[])[index] : value
    ).map(element => element * -1);

    return [(positivos[0] + negativos[0]), (positivos[1] + negativos[1]), (positivos[2] + negativos[2])];
  }

  static produtoEscalar(vetorA: VetorXYZType, vetorB: VetorXYZType) {
    return vetorA.map((value, index) => value * vetorB[index]).reduce((acc, val) => acc + val);
  }

  static moduloVetor(vetor: VetorXYZType) {
    return Math.sqrt(vetor.map(element => element ** 2).reduce((acc, val) => acc + val));
  }

  static calcularVetor(vetorA: VetorXYZType, vetorB: VetorXYZType) {
    return vetorB.map((value, index) => value - vetorA[index]) as VetorXYZType;
  }

  static equacaoParametrica(ponto: VetorXYZType, vetorDiretor: VetorXYZType) {
    return ponto.map((value, index) => `${value} + (${vetorDiretor[index]}).t`);
  }

  static equacaoGeralDoPlano(vetor: VetorXYZType, ponto: VetorXYZType) {
    const d = vetor.map((value, index) => value * ponto[index]).reduce((acc, val) => acc + val) * -1;
    return `${vetor[0]}x + ${vetor[1]}y + ${vetor[2]}z + ${d} = 0`;
  }

  static anguloEntreVetores(vetorA: VetorXYZType, vetorB: VetorXYZType) {
    const produtoEscalarResultado = GA.produtoEscalar(vetorA, vetorB);
    const moduloVetorA = GA.moduloVetor(vetorA);
    const moduloVetorB = GA.moduloVetor(vetorB);
    return produtoEscalarResultado / (moduloVetorA * moduloVetorB);
  }

  static produtoMisto(vetorA: VetorXYZType, vetorB: VetorXYZType, vetorC: VetorXYZType) {
    const produtoVetorial = GA.produtoVetorial(vetorB, vetorC) as VetorXYZType;
    return GA.produtoEscalar(vetorA, produtoVetorial);
  }

  static distanciaEntrePontos(vetorA: VetorXYZType, vetorB: VetorXYZType) {
    return GA.moduloVetor(GA.calcularVetor(vetorA, vetorB));
  }
}
