import { Matrix4 } from "@shopify/react-native-skia";

export const identity4: Matrix4 = [
  1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
];

enum MatrixValues {
  ScaleX = 0,
  ScaleY = 5,
  TranslateX = 3,
  TranslateY = 7,
}

export const getTransformComponents = (m: Matrix4 | undefined) => {
  "worklet";

  return {
    scaleX: m?.[MatrixValues.ScaleX] || 1,
    scaleY: m?.[MatrixValues.ScaleY] || 1,
    translateX: m?.[MatrixValues.TranslateX] || 0,
    translateY: m?.[MatrixValues.TranslateY] || 0,
  };
};

export const setScale = (matrix: Matrix4, kx: number, ky?: number): Matrix4 => {
  "worklet";

  const m = matrix.slice(0);
  m[MatrixValues.ScaleX] = kx;
  m[MatrixValues.ScaleY] = ky ?? kx;

  return m as unknown as Matrix4;
};

export const setTranslate = (
  matrix: Matrix4,
  tx: number,
  ty: number,
): Matrix4 => {
  "worklet";

  const m = matrix.slice(0);
  m[MatrixValues.TranslateX] = tx;
  m[MatrixValues.TranslateY] = ty;

  return m as unknown as Matrix4;
};

/** taken from https://github.com/Shopify/react-native-skia/blob/main/packages/skia/src/skia/types/Matrix4.ts#L378 which was very recently added.
 *  This is a temporary workaround until the new version of react-native-skia is released and widely used.
 */
const det3x3 = (
  a00: number,
  a01: number,
  a02: number,
  a10: number,
  a11: number,
  a12: number,
  a20: number,
  a21: number,
  a22: number,
): number => {
  "worklet";
  return (
    a00 * (a11 * a22 - a12 * a21) +
    a01 * (a12 * a20 - a10 * a22) +
    a02 * (a10 * a21 - a11 * a20)
  );
};

/** taken from https://github.com/Shopify/react-native-skia/blob/main/packages/skia/src/skia/types/Matrix4.ts#L402 which was very recently added.
 *  This is a temporary workaround until the new version of react-native-skia is released and widely used.
 */
export const invert4 = (m: Matrix4): Matrix4 => {
  "worklet";

  const a00 = m[0],
    a01 = m[1],
    a02 = m[2],
    a03 = m[3];
  const a10 = m[4],
    a11 = m[5],
    a12 = m[6],
    a13 = m[7];
  const a20 = m[8],
    a21 = m[9],
    a22 = m[10],
    a23 = m[11];
  const a30 = m[12],
    a31 = m[13],
    a32 = m[14],
    a33 = m[15];

  // Calculate cofactors
  const b00 = det3x3(a11, a12, a13, a21, a22, a23, a31, a32, a33);
  const b01 = -det3x3(a10, a12, a13, a20, a22, a23, a30, a32, a33);
  const b02 = det3x3(a10, a11, a13, a20, a21, a23, a30, a31, a33);
  const b03 = -det3x3(a10, a11, a12, a20, a21, a22, a30, a31, a32);

  const b10 = -det3x3(a01, a02, a03, a21, a22, a23, a31, a32, a33);
  const b11 = det3x3(a00, a02, a03, a20, a22, a23, a30, a32, a33);
  const b12 = -det3x3(a00, a01, a03, a20, a21, a23, a30, a31, a33);
  const b13 = det3x3(a00, a01, a02, a20, a21, a22, a30, a31, a32);

  const b20 = det3x3(a01, a02, a03, a11, a12, a13, a31, a32, a33);
  const b21 = -det3x3(a00, a02, a03, a10, a12, a13, a30, a32, a33);
  const b22 = det3x3(a00, a01, a03, a10, a11, a13, a30, a31, a33);
  const b23 = -det3x3(a00, a01, a02, a10, a11, a12, a30, a31, a32);

  const b30 = -det3x3(a01, a02, a03, a11, a12, a13, a21, a22, a23);
  const b31 = det3x3(a00, a02, a03, a10, a12, a13, a20, a22, a23);
  const b32 = -det3x3(a00, a01, a03, a10, a11, a13, a20, a21, a23);
  const b33 = det3x3(a00, a01, a02, a10, a11, a12, a20, a21, a22);

  // Calculate determinant
  const det = a00 * b00 + a01 * b01 + a02 * b02 + a03 * b03;

  // Check if matrix is invertible
  if (Math.abs(det) < 1e-8) {
    // Return identity matrix if not invertible
    return Matrix4();
  }

  const invDet = 1.0 / det;

  // Calculate inverse matrix
  return [
    b00 * invDet,
    b10 * invDet,
    b20 * invDet,
    b30 * invDet,
    b01 * invDet,
    b11 * invDet,
    b21 * invDet,
    b31 * invDet,
    b02 * invDet,
    b12 * invDet,
    b22 * invDet,
    b32 * invDet,
    b03 * invDet,
    b13 * invDet,
    b23 * invDet,
    b33 * invDet,
  ] as Matrix4;
};
