import { type CameraState } from '../camera/camera'
import { calibratedLonLatToWorldTile } from '../../utils/projection'
import { worldToScreen } from '../camera/camera'
import { type MapLayer, type LayerRenderContext } from './mapLayer'
import { type MapScene } from '../renderer/mapRenderer'

export interface ScreenPoint {
  x: number
  y: number
}

function smoothTrackScreenPoints(points: ScreenPoint[]): ScreenPoint[] {
  if (points.length < 3) {
    return points
  }

  const smoothed: ScreenPoint[] = [points[0]]
  for (let index = 1; index < points.length - 1; index += 1) {
    const prev = points[index - 1]
    const current = points[index]
    const next = points[index + 1]
    smoothed.push({
      x: prev.x * 0.2 + current.x * 0.6 + next.x * 0.2,
      y: prev.y * 0.2 + current.y * 0.6 + next.y * 0.2,
    })
  }
  smoothed.push(points[points.length - 1])
  return smoothed
}

function buildVectorCamera(scene: MapScene): CameraState {
  return {
    centerWorldX: scene.exactCenterWorldX,
    centerWorldY: scene.exactCenterWorldY,
    viewportWidth: scene.viewportWidth,
    viewportHeight: scene.viewportHeight,
    visibleColumns: scene.visibleColumns,
    rotationRad: scene.rotationRad,
  }
}

export class TrackLayer implements MapLayer {
  projectPoints(scene: MapScene): ScreenPoint[] {
    const camera = buildVectorCamera(scene)
    return scene.track.map((point) => {
      const worldPoint = calibratedLonLatToWorldTile(point, scene.zoom, scene.gpsCalibration, scene.gpsCalibrationOrigin)
      return worldToScreen(camera, worldPoint, false)
    })
  }

  draw(context: LayerRenderContext): void {
    const { ctx, scene } = context
    if (scene.trackMode === 'none') {
      return
    }
    const points = smoothTrackScreenPoints(this.projectPoints(scene))
    if (!points.length) {
      return
    }

    ctx.save()
    ctx.lineCap = 'round'
    ctx.lineJoin = 'round'
    if (scene.trackMode === 'tail') {
      const baseAlpha = 0.12 + scene.trackStyleConfig.glowStrength * 0.08
      points.forEach((screenPoint, index) => {
        if (index === 0) {
          return
        }
        const progress = index / Math.max(1, points.length - 1)
        ctx.strokeStyle = `rgba(84, 243, 216, ${baseAlpha + progress * 0.58})`
        ctx.lineWidth = 1.4 + progress * 4.2
        ctx.beginPath()
        ctx.moveTo(points[index - 1].x, points[index - 1].y)
        ctx.lineTo(screenPoint.x, screenPoint.y)
        ctx.stroke()
      })
      const head = points[points.length - 1]
      ctx.fillStyle = 'rgba(84, 243, 216, 0.24)'
      ctx.beginPath()
      ctx.arc(head.x, head.y, 11, 0, Math.PI * 2)
      ctx.fill()
      ctx.fillStyle = '#54f3d8'
      ctx.beginPath()
      ctx.arc(head.x, head.y, 5.2, 0, Math.PI * 2)
      ctx.fill()
    } else {
      ctx.strokeStyle = 'rgba(23, 109, 93, 0.96)'
      ctx.lineWidth = 4.2
      ctx.beginPath()
      points.forEach((screenPoint, index) => {
        if (index === 0) {
          ctx.moveTo(screenPoint.x, screenPoint.y)
          return
        }
        ctx.lineTo(screenPoint.x, screenPoint.y)
      })
      ctx.stroke()
    }
    ctx.restore()
  }
}
