export interface CameraState {
  centerWorldX: number
  centerWorldY: number
  viewportWidth: number
  viewportHeight: number
  visibleColumns: number
  translateX?: number
  translateY?: number
  rotationRad?: number
}

export interface ScreenPoint {
  x: number
  y: number
}

export interface WorldPoint {
  x: number
  y: number
}

export function getTileSizePx(camera: CameraState): number {
  if (!camera.viewportWidth || !camera.visibleColumns) {
    return 0
  }

  return camera.viewportWidth / camera.visibleColumns
}

export function rotateScreenPoint(point: ScreenPoint, centerX: number, centerY: number, rotationRad: number): ScreenPoint {
  if (!rotationRad) {
    return point
  }

  const deltaX = point.x - centerX
  const deltaY = point.y - centerY
  const cos = Math.cos(rotationRad)
  const sin = Math.sin(rotationRad)

  return {
    x: centerX + deltaX * cos - deltaY * sin,
    y: centerY + deltaX * sin + deltaY * cos,
  }
}

export function worldToScreen(
  camera: CameraState,
  world: WorldPoint,
  includeTranslate = false,
): ScreenPoint {
  const tileSize = getTileSizePx(camera)
  const translateX = includeTranslate ? (camera.translateX || 0) : 0
  const translateY = includeTranslate ? (camera.translateY || 0) : 0
  const centerX = camera.viewportWidth / 2
  const centerY = camera.viewportHeight / 2

  const rotated = rotateScreenPoint(
    {
      x: centerX + (world.x - camera.centerWorldX) * tileSize,
      y: centerY + (world.y - camera.centerWorldY) * tileSize,
    },
    centerX,
    centerY,
    camera.rotationRad || 0,
  )

  return {
    x: rotated.x + translateX,
    y: rotated.y + translateY,
  }
}

export function screenToWorld(
  camera: CameraState,
  screen: ScreenPoint,
  includeTranslate = true,
): WorldPoint {
  const tileSize = getTileSizePx(camera)
  const translateX = includeTranslate ? (camera.translateX || 0) : 0
  const translateY = includeTranslate ? (camera.translateY || 0) : 0
  const centerX = camera.viewportWidth / 2
  const centerY = camera.viewportHeight / 2

  const unrotated = rotateScreenPoint(
    {
      x: screen.x - translateX,
      y: screen.y - translateY,
    },
    centerX,
    centerY,
    -(camera.rotationRad || 0),
  )

  return {
    x: camera.centerWorldX + (unrotated.x - centerX) / tileSize,
    y: camera.centerWorldY + (unrotated.y - centerY) / tileSize,
  }
}
