import ImageLayer from 'ol/layer/Image.js';
import ImageCanvasSource from 'ol/source/ImageCanvas.js';
import { transformExtent } from 'ol/proj.js';
/**
 * WebGL을 사용하여 날씨 데이터 이미지를 지도에 오버레이하는 레이어
 * 이미지의 RGBA 채널에는 온도, 습도, 바람, 강수량 데이터가 인코딩되어 있음
 */
class WebGLCanvasLayer extends ImageLayer {
    /**
     * WebGL 캔버스 레이어 생성자
     * @param imageUrl 날씨 데이터가 인코딩된 이미지 URL
     * @param dataType 시각화할 데이터 유형 (temperature, humidity, wind, precipitation)
     */
    constructor(imageUrl, dataType = 'temperature') {
        super({
            source: new ImageCanvasSource({
                canvasFunction: (extent, resolution, pixelRatio, size, projection) => {
                    this._initializeWebGL(size, pixelRatio);
                    if (this.gl && this.program) {
                        // 현재 지도 뷰의 extent를 지리 좌표(EPSG:4326)로 변환
                        const worldExtent = transformExtent(extent, projection.getCode(), 'EPSG:4326');
                        // 이미지가 로드된 경우 렌더링
                        if (this.imageLoaded && this.texture) {
                            this._updateWebGLUniforms(worldExtent, resolution, size, pixelRatio);
                            this._renderTexture(worldExtent, size, resolution, pixelRatio);
                        }
                        else if (!this.pendingRender) {
                            this.pendingRender = true;
                            this._loadImage(this.imageUrl, worldExtent, size, resolution, pixelRatio);
                        }
                    }
                    return this.canvas;
                },
                projection: 'EPSG:3857'
            })
        });
        this.canvas = null;
        this.gl = null;
        this.program = null;
        this.texture = null;
        this.buffer = null;
        this.imageLoaded = false;
        this.pendingRender = false;
        this.imageUrl = imageUrl;
        this.dataType = dataType;
        this.on('change:visible', () => this.changed());
    }
    /**
     * 데이터 시각화 유형 변경
     */
    updateDataType(type) {
        this.dataType = type;
        this.changed();
    }
    /**
     * 리소스 해제
     */
    dispose() {
        if (this.gl) {
            if (this.buffer)
                this.gl.deleteBuffer(this.buffer);
            if (this.texture)
                this.gl.deleteTexture(this.texture);
            if (this.program)
                this.gl.deleteProgram(this.program);
        }
        super.dispose();
    }
    /**
     * 이미지 URL 업데이트
     */
    updateImageUrl(newUrl) {
        if (this.imageUrl === newUrl)
            return;
        this.imageUrl = newUrl;
        this.imageLoaded = false;
        this.pendingRender = false;
        if (this.texture && this.gl) {
            this.gl.deleteTexture(this.texture);
            this.texture = null;
        }
        this.changed();
    }
    /**
     * WebGL 컨텍스트 초기화
     */
    _initializeWebGL(size, pixelRatio = 1) {
        const width = Math.round(size[0] * pixelRatio);
        const height = Math.round(size[1] * pixelRatio);
        if (!this.canvas) {
            // 캔버스 생성 및 WebGL 초기화
            this.canvas = document.createElement('canvas');
            this.canvas.width = width;
            this.canvas.height = height;
            this.gl = this.canvas.getContext('webgl', {
                alpha: true,
                antialias: false,
                depth: false
            });
            if (!this.gl)
                return;
            this.program = this._createShaderProgram();
            if (this.program) {
                this._initializeBuffers();
            }
        }
        else if (this.canvas.width !== width || this.canvas.height !== height) {
            // 캔버스 크기 조정
            this.canvas.width = width;
            this.canvas.height = height;
            if (this.gl)
                this.gl.viewport(0, 0, width, height);
        }
    }
    /**
     * 버텍스 버퍼 초기화
     */
    _initializeBuffers() {
        if (!this.gl)
            return;
        // 사각형 버텍스 (위치(x,y) + 텍스처 좌표(s,t))
        const vertices = new Float32Array([
            -1,
            -1,
            0,
            1, // 좌하단
            1,
            -1,
            1,
            1, // 우하단
            -1,
            1,
            0,
            0, // 좌상단
            1,
            1,
            1,
            0 // 우상단
        ]);
        this.buffer = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, vertices, this.gl.STATIC_DRAW);
    }
    /**
     * 이미지 로드 및 텍스처 생성
     */
    _loadImage(url, worldExtent, size, resolution, pixelRatio) {
        const img = new Image();
        img.crossOrigin = 'anonymous';
        img.onload = () => {
            if (!this.gl)
                return;
            try {
                // 텍스처 생성 및 설정
                this.texture = this.gl.createTexture();
                this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
                // 경도 방향으로 래핑 (날짜 변경선 처리)
                // this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.REPEAT)
                this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
                this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
                this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
                this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
                // 텍스처 데이터 업로드
                this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); // 알파값 손실 방지
                this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, img);
                this.imageLoaded = true;
                this.pendingRender = false;
                // 이미지 로드 후 렌더링
                this._renderTexture(worldExtent, size, resolution, pixelRatio);
                this.changed();
            }
            catch (err) {
                console.error('텍스처 생성 실패:', err);
                this.pendingRender = false;
            }
        };
        img.onerror = () => {
            console.error('이미지 로드 실패:', url);
            this.pendingRender = false;
        };
        img.src = url;
    }
    /**
     * 텍스처 렌더링
     */
    _renderTexture(worldExtent, size, resolution, pixelRatio) {
        if (!this.gl || !this.program || !this.texture || !this.buffer)
            return;
        const gl = this.gl;
        const width = Math.round(size[0] * pixelRatio);
        const height = Math.round(size[1] * pixelRatio);
        this._updateWebGLUniforms(worldExtent, resolution, size, pixelRatio);
        // 기본 WebGL 상태 설정
        gl.viewport(0, 0, width, height);
        gl.clearColor(0, 0, 0, 0);
        gl.enable(gl.BLEND);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
        gl.clear(gl.COLOR_BUFFER_BIT);
        // 셰이더 프로그램 활성화
        gl.useProgram(this.program);
        // 텍스처 바인딩
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, this.texture);
        // 유니폼 설정
        const dataTypeVal = ['temperature', 'humidity', 'wind', 'precipitation'].indexOf(this.dataType);
        gl.uniform1i(gl.getUniformLocation(this.program, 'u_texture'), 0);
        gl.uniform1f(gl.getUniformLocation(this.program, 'u_opacity'), 0.8 * pixelRatio);
        gl.uniform1i(gl.getUniformLocation(this.program, 'u_dataType'), dataTypeVal >= 0 ? dataTypeVal : 0);
        gl.uniform4fv(gl.getUniformLocation(this.program, 'u_worldExtent'), new Float32Array([worldExtent[0], worldExtent[1], worldExtent[2], worldExtent[3]]));
        // 버퍼 바인딩 및 속성 설정
        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
        const a_position = gl.getAttribLocation(this.program, 'a_position');
        const a_texCoord = gl.getAttribLocation(this.program, 'a_texCoord');
        gl.enableVertexAttribArray(a_position);
        gl.enableVertexAttribArray(a_texCoord);
        gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 16, 0);
        gl.vertexAttribPointer(a_texCoord, 2, gl.FLOAT, false, 16, 8);
        // 사각형 렌더링
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    }
    /**
     * 셰이더 프로그램 생성
     */
    _createShaderProgram() {
        if (!this.gl)
            return null;
        // 버텍스 셰이더: 지리 좌표와 텍스처 좌표 간 변환
        const vertexShaderSource = `
precision highp float;

attribute vec4 a_position; // Rectangle coordinates
attribute vec2 a_texCoord; // Texture coordinates
varying vec2 v_texCoord;

uniform vec2 u_resolution; // Resolution (meters per pixel)
uniform vec2 u_offset; // Offset in map coordinates
uniform float u_zoomResolution; // 🚀 This will store the resolution passed from JS

void main() {
  float x = ((a_position.x - u_offset.x * u_zoomResolution) / u_resolution.x) * 2.0 - 1.0;
  float y = ((a_position.y - u_offset.y * u_zoomResolution) / u_resolution.y) * 2.0 - 1.0;

  gl_Position = vec4(x, y, 0.0, 1.0);
  v_texCoord = a_texCoord;
}
    `;
        // 프래그먼트 셰이더: 데이터 시각화
        const fragmentShaderSource = `
      precision highp float;
      
      uniform sampler2D u_texture;
      uniform float u_opacity;
      uniform int u_dataType;
      varying vec2 v_texCoord;
      
      // 색상 그라데이션 생성
      vec3 getColorForData(float value) {
        // 값 범위 확장
        value = clamp(value * 1.2, 0.0, 1.0);
        
        if (u_dataType == 0) {  // 온도
          // 파랑(저온) -> 빨강(고온)
          return mix(vec3(0.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), value);
        } 
        else if (u_dataType == 1) {  // 습도
          // 하늘색(건조) -> 진한 파랑(습함)
          return mix(vec3(0.5, 0.5, 1.0), vec3(0.0, 0.0, 0.8), value);
        }
        else if (u_dataType == 2) {  // 바람
          // 녹색(약함) -> 노랑(강함)
          return mix(vec3(0.0, 0.5, 0.0), vec3(1.0, 1.0, 0.0), value);
        }
        else {  // 강수량
          // 투명(없음) -> 보라(많음)
          float intensity = value * 1.5;
          return vec3(0.5, 0.0, 0.5) * intensity;
        }
      }
      
      void main() {
        // 텍스처에서 데이터 가져오기
        vec4 texColor = texture2D(u_texture, v_texCoord);
        
        // 데이터가 없는 경우 무시
        if (texColor.a < 0.01) {
          discard;
        }
        
        // 데이터 채널 선택
        float value = 0.0;
        if (u_dataType == 0) value = texColor.r;        // 온도
        else if (u_dataType == 1) value = texColor.g;   // 습도
        else if (u_dataType == 2) value = texColor.b;   // 바람
        else value = texColor.a;                        // 강수량
        
        // 데이터 시각화
        vec3 color = getColorForData(value);
        float alpha = u_dataType == 3 ? value * u_opacity : u_opacity;
        
        gl_FragColor = vec4(color, alpha);
      }
    `;
        // 셰이더 컴파일
        const vertexShader = this._compileShader(this.gl.VERTEX_SHADER, vertexShaderSource);
        const fragmentShader = this._compileShader(this.gl.FRAGMENT_SHADER, fragmentShaderSource);
        if (!vertexShader || !fragmentShader)
            return null;
        // 프로그램 생성 및 링크
        const program = this.gl.createProgram();
        this.gl.attachShader(program, vertexShader);
        this.gl.attachShader(program, fragmentShader);
        this.gl.linkProgram(program);
        if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
            console.error('WebGL 프로그램 링크 실패:', this.gl.getProgramInfoLog(program));
            return null;
        }
        return program;
    }
    /**
     * 셰이더 컴파일
     */
    _compileShader(type, source) {
        if (!this.gl)
            return null;
        const shader = this.gl.createShader(type);
        this.gl.shaderSource(shader, source);
        this.gl.compileShader(shader);
        if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
            console.error('셰이더 컴파일 오류:', this.gl.getShaderInfoLog(shader));
            this.gl.deleteShader(shader);
            return null;
        }
        return shader;
    }
    _updateWebGLUniforms(worldExtent, resolution, size, pixelRatio) {
        if (!this.gl || !this.program)
            return;
        const gl = this.gl;
        gl.useProgram(this.program);
        const textureWidth = size[0] * pixelRatio;
        const textureHeight = size[1] * pixelRatio;
        // ✅ Apply resolution correctly (fixing zoom scaling issue)
        const u_resolution = [
            (worldExtent[2] - worldExtent[0]) / textureWidth,
            (worldExtent[3] - worldExtent[1]) / textureHeight
        ];
        // ✅ Convert u_offset to OpenGL coordinates
        const u_offset = [
            (worldExtent[0] - (worldExtent[2] + worldExtent[0]) / 2) / (worldExtent[2] - worldExtent[0]),
            (worldExtent[1] - (worldExtent[3] + worldExtent[1]) / 2) / (worldExtent[3] - worldExtent[1])
        ];
        console.log('📌 _updateWebGLUniforms called');
        console.log('🟢 resolution:', resolution);
        console.log('🟢 pixelRatio:', pixelRatio);
        console.log('🟢 textureWidth:', textureWidth, 'textureHeight:', textureHeight);
        console.log('🟢 u_resolution:', u_resolution);
        console.log('🟢 u_offset:', u_offset);
        gl.uniform2fv(gl.getUniformLocation(this.program, 'u_resolution'), u_resolution);
        gl.uniform2fv(gl.getUniformLocation(this.program, 'u_offset'), u_offset);
        gl.uniform1f(gl.getUniformLocation(this.program, 'u_zoomResolution'), resolution); // 🚀 Pass resolution!
    }
}
export default WebGLCanvasLayer;
//# sourceMappingURL=webgl-canvas-layer.js.map