// src/core/ARRenderer.tsx

import React, { useRef, useEffect, useState } from 'react';
import { View, StyleSheet, TouchableOpacity, Text, Image } from 'react-native';
import * as GL from 'expo-gl';
import { Camera, CameraType } from 'expo-camera';
import { Renderer, TextureLoader, THREE } from 'expo-three';
import { PlaneDetection, Plane } from './PlaneDetection';

interface ARRendererState {
  snapshot?: GL.GLSnapshot;
  zoom: number;
  cameraType: CameraType;
}

const ARRenderer: React.FC = () => {
  const requestRef = useRef<number | null>(null);
  const glViewRef = useRef<GL.GLView | null>(null);
  const cameraRef = useRef<typeof Camera | null>(null);
  const planeDetection = useRef<PlaneDetection>(new PlaneDetection());
  const [snapshot, setSnapshot] = useState<GL.GLSnapshot | undefined>();
  const [zoom, setZoom] = useState(0);
  const [cameraType, setCameraType] = useState<CameraType>(CameraType.back);
  const [cameraTexture, setCameraTexture] = useState<WebGLTexture | null>(null);

  /**
   * This function is called once the GLView context is created.
   * It sets up a three.js renderer, scene, camera, and a sample cube.
   */
  const takeSnapshot = async () => {
    if (glViewRef.current) {
      const snapshot = await glViewRef.current.takeSnapshotAsync({
        format: 'png',
      });
      setSnapshot(snapshot);
    }
  };

  const createCameraTexture = async (gl: GL.ExpoWebGLRenderingContext) => {
    if (glViewRef.current && cameraRef.current) {
      const texture = await glViewRef.current.createCameraTextureAsync(cameraRef.current);
      setCameraTexture(texture);
      return texture;
    }
    return null;
  };

  const onContextCreate = async (gl: GL.ExpoWebGLRenderingContext) => {
    // Create camera texture
    const texture = await createCameraTexture(gl);

    // Create a three.js renderer using Expo's GL context
    const renderer = new Renderer({ gl });
    const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
    renderer.setSize(width, height);
    renderer.setClearColor(0xffffff, 0);

    // Create a new three.js scene
    const scene = new THREE.Scene();
    
    // Initialize plane detection with Three.js scene and camera
    planeDetection.current.setScene(scene);
    // Move camera setup before using it
    const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
    camera.position.z = 5;
    
    planeDetection.current.setCamera(camera);

  
    // Create a simple cube geometry with a basic material
    const geometry = new THREE.BoxGeometry(1, 1, 1);
    const material = new THREE.MeshBasicMaterial({ color: 'tomato' });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);

    // Animation loop: rotates the cube and renders the scene
    const animate = () => {
      requestRef.current = requestAnimationFrame(animate);
      
      // Update cube rotation for basic animation
      cube.rotation.x += 0.02;
      cube.rotation.y += 0.02;
      
      // Detect planes
      // Add state for detected planes
      const [detectedPlanes, setDetectedPlanes] = useState<Plane[]>([]);
      
      // Handle plane detection asynchronously
      planeDetection.current.detectPlanes()
        .then(planes => {
          setDetectedPlanes(planes);
        })
        .catch(error => {
          console.error('Error detecting planes:', error);
        });
      
      // Render the scene using the camera
      renderer.render(scene, camera);
      
      // Notify the GL context that the frame is complete
      if (gl.endFrameEXP) {
        gl.endFrameEXP();
      }
    };

    // Start the animation loop
    animate();
  };

  // Clean up the animation loop when the component is unmounted
  useEffect(() => {
    return () => {
      if (requestRef.current) {
        cancelAnimationFrame(requestRef.current);
      }
    };
  }, []);

  const toggleCameraType = () => {
    setCameraType(current => 
      current === CameraType.back ? CameraType.front : CameraType.back
    );
  };

  const zoomIn = () => {
    setZoom(current => Math.min(current + 0.1, 1));
  };

  const zoomOut = () => {
    setZoom(current => Math.max(current - 0.1, 0));
  };

  return (
    <View style={styles.container}>
      <Camera
        ref={cameraRef as React.RefObject<Camera>}
        style={StyleSheet.absoluteFill}
        type={cameraType}
        zoom={zoom}
      />
      <GL.GLView
        style={StyleSheet.absoluteFill}
        onContextCreate={onContextCreate}
        ref={glViewRef}
      />
      {snapshot && (
        <View style={styles.snapshot}>
          <Image
            style={[styles.flex, { aspectRatio: snapshot.width / snapshot.height }]}
            source={snapshot as { uri: string }}
          />
        </View>
      )}
      <View style={styles.controls}>
        <TouchableOpacity style={styles.controlButton} onPress={toggleCameraType}>
          <Text>Flip</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.controlButton} onPress={zoomIn}>
          <Text>Zoom In</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.controlButton} onPress={zoomOut}>
          <Text>Zoom Out</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.controlButton} onPress={takeSnapshot}>
          <Text>Snapshot</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  glView: {
    flex: 1,
  },
  flex: {
    flex: 1,
  },
  snapshot: {
    height: 100,
    margin: 10,
    borderWidth: StyleSheet.hairlineWidth,
    borderColor: 'black',
    backgroundColor: '#248e80',
    position: 'absolute',
    left: 0,
    bottom: 0,
    shadowColor: 'black',
    shadowOpacity: 0.3,
    shadowRadius: 5,
    shadowOffset: { width: 0, height: 0 },
  },
  controls: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    flexDirection: 'row',
    justifyContent: 'space-around',
    padding: 10,
  },
  controlButton: {
    paddingVertical: 10,
    paddingHorizontal: 15,
    backgroundColor: 'white',
    borderWidth: StyleSheet.hairlineWidth,
    borderColor: 'black',
    borderRadius: 5,
  },
});

export default ARRenderer;