import { Pose } from "../pose";
import { angleTo4Direction } from "../../../utils/dpad";

const zeroVec2 = [0, 0];
export const xforms = {
  noop: function() {},
  copy: function(frame, src, dest) {
    frame[dest.value] = frame[src.value];
  },
  scale: function(scalar) {
    return function scale(frame, src, dest) {
      if (frame[src.value] !== undefined) {
        frame[dest.value] = frame[src.value] * scalar;
      }
    };
  },
  split_vec2: function(frame, src, dest) {
    if (frame[src.value] !== undefined) {
      frame[dest.x] = frame[src.value][0];
      frame[dest.y] = frame[src.value][1];
    }
  },
  compose_vec2: function(frame, src, dest) {
    if (frame[src.x] !== undefined && frame[src.y] !== undefined) {
      frame[dest.value] = [frame[src.x], frame[src.y]];
    }
  },
  negate: function(frame, src, dest) {
    frame[dest.value] = -frame[src.value];
  },
  copyIfFalse: function(frame, src, dest) {
    frame[dest.value] = frame[src.bool] ? undefined : frame[src.value];
  },
  copyIfTrue: function(frame, src, dest) {
    frame[dest.value] = frame[src.bool] ? frame[src.value] : undefined;
  },
  zeroIfDefined: function(frame, src, dest) {
    frame[dest.value] = frame[src.bool] !== undefined ? 0 : frame[src.value];
  },
  true: function(frame, src, dest) {
    frame[dest.value] = true;
  },
  rising: function rising(frame, src, dest, prevState) {
    frame[dest.value] = frame[src.value] && prevState === false;
    return !!frame[src.value];
  },
  risingWithFrameDelay: function(n) {
    return function risingWithFrameDelay(frame, src, dest, state = { values: new Array(n) }) {
      frame[dest.value] = state.values.shift();
      state.values.push(frame[src.value] && !state.prev);
      state.prev = frame[src.value];
      return state;
    };
  },
  falling: function falling(frame, src, dest, prevState) {
    frame[dest.value] = !frame[src.value] && prevState;
    return !!frame[src.value];
  },
  vec2Zero: function(frame, _, dest) {
    frame[dest.value] = zeroVec2;
  },
  poseFromCameraProjection: function() {
    let camera;
    const pose = new Pose();
    return function poseFromCameraProjection(frame, src, dest) {
      if (!camera) {
        camera = document.querySelector("#player-camera").components.camera.camera;
      }
      frame[dest.value] = pose.fromCameraProjection(camera, frame[src.value][0], frame[src.value][1]);
    };
  },
  vec2dpad: function(deadzoneRadius, invertX = false, invertY = false) {
    const deadzoneRadiusSquared = deadzoneRadius * deadzoneRadius;
    return function vec2dpad(frame, src, dest) {
      if (!frame[src.value]) return;
      const [x, y] = frame[src.value];
      const inCenter = x * x + y * y < deadzoneRadiusSquared;
      const direction = inCenter ? "center" : angleTo4Direction(Math.atan2(invertX ? -x : x, invertY ? -y : y));
      frame[dest[direction]] = true;
    };
  },
  always: function(constValue) {
    return function always(frame, _, dest) {
      frame[dest.value] = constValue;
    };
  },
  wasd_to_vec2: function(frame, { w, a, s, d }, { vec2 }) {
    let x = 0;
    let y = 0;
    if (frame[a]) x -= 1;
    if (frame[d]) x += 1;
    if (frame[w]) y += 1;
    if (frame[s]) y -= 1;
    frame[vec2] = [x, y];
  },
  add_vec2: function(frame, src, dest) {
    const first = frame[src.first];
    const second = frame[src.second];
    if (first && second) {
      frame[dest.value] = [first[0] + second[0], first[1] + second[1]];
    } else if (second) {
      frame[dest.value] = second;
    } else if (first) {
      frame[dest.value] = first;
    }
  },
  max_vec2: function(frame, src, dest) {
    const first = frame[src.first];
    const second = frame[src.second];
    if (first && second) {
      frame[dest.value] =
        first[0] * first[0] + first[1] * first[1] > second[0] * second[0] + second[1] * second[1] ? first : second;
    } else if (second) {
      frame[dest.value] = second;
    } else if (first) {
      frame[dest.value] = first;
    }
  },
  normalize_vec2: function(frame, src, dest) {
    const vec2 = frame[src.value];
    if (vec2) {
      if (vec2[0] === 0 && vec2[0] === 0) {
        frame[dest.value] = vec2;
      } else {
        const l = Math.sqrt(vec2[0] * vec2[0] + vec2[1] * vec2[1]);
        frame[dest.value] = [vec2[0] / l, vec2[1] / l];
      }
    }
  },
  any: function(frame, src, dest) {
    for (const path in src) {
      if (frame[src[path]]) {
        frame[dest.value] = true;
        return;
      }
    }
    frame[dest.value] = false;
  },
  touch_axis_scroll(scale = 1) {
    return function(frame, src, dest, state = { value: 0, touching: false }) {
      frame[dest.value] =
        !state.touching || !frame[src.touching] ? 0 : scale * (frame[src.value] + 1 - (state.value + 1));
      state.value = frame[src.value];
      state.touching = frame[src.touching];
      return state;
    };
  }
};