// app/DraggableWindows.tsx

'use client';

import React from 'react';
import { Rnd } from 'react-rnd';
import {
  AiOutlineClose,
  AiOutlineMinus,
  AiOutlineFullscreen,
} from 'react-icons/ai';
import { useWindowManager } from '../contexts/WindowManagerContext';
import { motion, AnimatePresence } from 'framer-motion';

const DraggableWindows: React.FC = () => {
  const {
    windows,
    windowStates,
    closeWindow,
    minimizeWindow,
    maximizeWindow,
    updateWindowState,
    bringToFront,
  } = useWindowManager();

  return (
    <div className="w-full h-full fixed top-0 left-0">
      {Object.keys(windows).map((name) => {
        const { Component } = windows[name];
        const state = windowStates[name];

        if (!Component) return null;

        const isVisible = state.visible && !state.minimized;

        return (
          <AnimatePresence key={name}>
            {isVisible && (
              <WindowWrapper
                key={name}
                name={name}
                state={state}
                bringToFront={bringToFront}
                updateWindowState={updateWindowState}
                minimizeWindow={minimizeWindow}
                maximizeWindow={maximizeWindow}
                closeWindow={closeWindow}
              >
                <Component />
              </WindowWrapper>
            )}
          </AnimatePresence>
        );
      })}
    </div>
  );
};

interface WindowState {
  visible: boolean;
  minimized: boolean;
  maximized: boolean;
  width: number;
  height: number;
  x: number;
  y: number;
  zIndex: number;
}

interface WindowWrapperProps {
  name: string;
  state: WindowState;
  bringToFront: (name: string) => void;
  updateWindowState: (name: string, newState: Partial<WindowState>) => void;
  minimizeWindow: (name: string) => void;
  maximizeWindow: (name: string) => void;
  closeWindow: (name: string) => void;
  children: React.ReactNode;
}

const WindowWrapper: React.FC<WindowWrapperProps> = ({
  name,
  state,
  bringToFront,
  updateWindowState,
  minimizeWindow,
  maximizeWindow,
  closeWindow,
  children,
}) => {
  const { windowStates } = useWindowManager();

  // Determine if the window is the topmost window
  const maxZIndex = Math.max(...Object.values(windowStates).map((w) => w.zIndex));
  const isTopWindow = state.zIndex === maxZIndex;

  // Determine box shadow based on zIndex
  const boxShadow = isTopWindow
    ? '0px 0px 10px rgba(0,0,0,0.5)'
    : '0px 0px 5px rgba(0,0,0,0.2)';

  // Use layout animations for smooth transitions
  const windowMotionProps = {
    layout: true,
    initial: { opacity: 0 },
    animate: { opacity: 1 },
    exit: { opacity: 0 },
    transition: { duration: 0.2 },
  };

  return (
    <Rnd
  size={{ width: state.width, height: state.height }}
  position={{ x: state.x, y: state.y }}
  onMouseDown={() => bringToFront(name)}
  onDragStart={() => bringToFront(name)}
  onDrag={(e, d) => {
    updateWindowState(name, { x: d.x, y: d.y }, true); // skipGrid = true
  }}
  onDragStop={(e, d) => {
    updateWindowState(name, { x: d.x, y: d.y }); // Snap to grid after drag ends
  }}
  onResize={(e, direction, ref, delta, position) => {
    const newWidth = parseInt(ref.style.width, 10);
    const newHeight = parseInt(ref.style.height, 10);
    updateWindowState(
      name,
      {
        width: newWidth,
        height: newHeight,
        x: position.x,
        y: position.y,
      },
      true // skipGrid = true
    );
  }}
  onResizeStop={(e, direction, ref, delta, position) => {
    const newWidth = parseInt(ref.style.width, 10);
    const newHeight = parseInt(ref.style.height, 10);
    updateWindowState(name, {
      width: newWidth,
      height: newHeight,
      x: position.x,
      y: position.y,
    }); // Snap to grid after resize ends
  }}
  minWidth={200}
  minHeight={100}
  grid={[25, 25]} // Snapping to 25px grid
  dragHandleClassName="window-bar" 
  disableDragging={state.maximized}
  enableResizing={!state.maximized}
  bounds="parent"
  style={{ zIndex: state.zIndex }}
>
      <motion.div
        {...windowMotionProps}
        className="bg-white border border-gray-500 flex flex-col h-full"
        style={{ boxShadow }}
        onMouseDown={() => bringToFront(name)}
      >
        {/* Window Bar */}
        <div
          className="flex items-center justify-between bg-gray-200 p-1 window-bar" // Added 'window-bar' class
          style={{ cursor: state.maximized ? 'default' : 'move' }}
        >
          <span className="pl-2">{name}</span>
          <div className="flex">
            <button
              onClick={(e) => {
                e.stopPropagation();
                minimizeWindow(name);
              }}
              className="p-1 hover:bg-gray-300"
            >
              <AiOutlineMinus />
            </button>
            <button
              onClick={(e) => {
                e.stopPropagation();
                maximizeWindow(name);
              }}
              className="p-1 hover:bg-gray-300"
            >
              <AiOutlineFullscreen />
            </button>
            <button
              onClick={(e) => {
                e.stopPropagation();
                closeWindow(name);
              }}
              className="p-1 hover:bg-gray-300"
            >
              <AiOutlineClose />
            </button>
          </div>
        </div>
        {/* Window Content */}
        <div className="flex-grow overflow-auto window-content">{children}</div>
      </motion.div>
    </Rnd>
  );
};

export default DraggableWindows;

AND:

// components/AnimatedBackground.tsx

'use client';

import React, { useEffect, useRef } from 'react';
import { useWindowManager } from '../contexts/WindowManagerContext';

interface AnimatedBackgroundProps {
  gridSpacing?: number;
  effectRadius?: number;
  maxDisplacement?: number;
  dotColor?: string;
  dotSize?: number;
}

const AnimatedBackground: React.FC<AnimatedBackgroundProps> = ({
  gridSpacing = 25,      // Adjusted for fewer dots
  effectRadius = 500,    // Increased for a larger gravity area
  maxDisplacement = 10,  // Increased for more noticeable effects
  dotColor = '#888',
  dotSize = 1.5,
}) => {
  const { windows, windowStates } = useWindowManager();

  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const animationFrameId = useRef<number | null>(null);
  const dots = useRef<{ x: number; y: number }[]>([]);
  const mousePosition = useRef<{ x: number; y: number }>({ x: -1000, y: -1000 });

  useEffect(() => {
    const canvas = canvasRef.current;
    const context = canvas?.getContext('2d');

    if (!canvas || !context) return;

    let width = window.innerWidth;
    let height = window.innerHeight;
    canvas.width = width;
    canvas.height = height;

    const initializeDots = () => {
      const columns = Math.floor(width / gridSpacing) + 1;
      const rows = Math.floor(height / gridSpacing) + 1;

      const dotArray: { x: number; y: number }[] = [];
      for (let row = 0; row < rows; row++) {
        for (let col = 0; col < columns; col++) {
          dotArray.push({ x: col * gridSpacing, y: row * gridSpacing });
        }
      }
      dots.current = dotArray;
    };

    initializeDots();

    const handleResize = () => {
      width = window.innerWidth;
      height = window.innerHeight;
      canvas.width = width;
      canvas.height = height;

      initializeDots();
    };

    window.addEventListener('resize', handleResize);

    const handleMouseMove = (event: MouseEvent) => {
      mousePosition.current = { x: event.clientX, y: event.clientY };
    };

    window.addEventListener('mousemove', handleMouseMove);

    const animate = () => {
      if (!context) return;

      context.clearRect(0, 0, canvas.width, canvas.height);
      context.fillStyle = dotColor;

      const gravitySources: {
        x: number;
        y: number;
        gravity: number;
        effectRadius: number;
        maxDisplacement: number;
        windowRect?: { x: number; y: number; width: number; height: number };
      }[] = [];

      // Add windows with gravity
      Object.keys(windows).forEach((name) => {
        const windowData = windows[name];
        const windowState = windowStates[name];
        const gravity = windowData.windowProps.gravity;

        if (gravity && gravity > 0 && windowState.visible && !windowState.minimized) {
          gravitySources.push({
            x: windowState.x + windowState.width / 2,
            y: windowState.y + windowState.height / 2,
            gravity: gravity,
            effectRadius: effectRadius,
            maxDisplacement: maxDisplacement,
            windowRect: {
              x: windowState.x,
              y: windowState.y,
              width: windowState.width,
              height: windowState.height,
            },
          });
        }
      });

      // For each dot, calculate displacement due to gravity sources
      for (let i = 0; i < dots.current.length; i++) {
        const dot = dots.current[i];

        let totalDisplacementX = 0;
        let totalDisplacementY = 0;

        gravitySources.forEach((source) => {
          // If windowRect is available, compute distance differently
          if (source.windowRect) {
            const rect = source.windowRect;

            // Compute the shortest distance from dot to the rectangle
            const dx = Math.max(rect.x - dot.x, 0, dot.x - (rect.x + rect.width));
            const dy = Math.max(rect.y - dot.y, 0, dot.y - (rect.y + rect.height));
            const distance = Math.sqrt(dx * dx + dy * dy);

            // Inside the rectangle, set distance to zero
            const insideRect = dot.x >= rect.x && dot.x <= rect.x + rect.width && dot.y >= rect.y && dot.y <= rect.y + rect.height;
            const effectiveDistance = insideRect ? 0 : distance;

            if (effectiveDistance < source.effectRadius) {
              // Calculate strength inversely proportional to distance
              const strength = (source.effectRadius - effectiveDistance) / source.effectRadius;

              // Calculate angle toward center of window
              const angle = Math.atan2(source.y - dot.y, source.x - dot.x);

              const gravityStrength = source.gravity * strength;

              const displacementX = Math.cos(angle) * gravityStrength * source.maxDisplacement;
              const displacementY = Math.sin(angle) * gravityStrength * source.maxDisplacement;

              totalDisplacementX += displacementX;
              totalDisplacementY += displacementY;
            }
          }
          // Handle other gravity sources (e.g., mouse) if needed
          else {
            const dx = source.x - dot.x;
            const dy = source.y - dot.y;
            const distance = Math.sqrt(dx * dx + dy * dy);

            if (distance < source.effectRadius) {
              const strength = (source.effectRadius - distance) / source.effectRadius;
              const angle = Math.atan2(dy, dx);

              const gravityStrength = source.gravity * strength;

              const displacementX = Math.cos(angle) * gravityStrength * source.maxDisplacement;
              const displacementY = Math.sin(angle) * gravityStrength * source.maxDisplacement;

              totalDisplacementX += displacementX;
              totalDisplacementY += displacementY;
            }
          }
        });

        // Draw the dot with the total displacement
        context.beginPath();
        context.arc(
          dot.x + totalDisplacementX,
          dot.y + totalDisplacementY,
          dotSize,
          0,
          Math.PI * 2
        );
        context.fill();
      }

      animationFrameId.current = requestAnimationFrame(animate);
    };

    animate();

    return () => {
      window.removeEventListener('resize', handleResize);
      window.removeEventListener('mousemove', handleMouseMove);
      if (animationFrameId.current !== null) {
        cancelAnimationFrame(animationFrameId.current);
      }
    };
  }, [
    gridSpacing,
    effectRadius,
    maxDisplacement,
    dotColor,
    dotSize,
    windows,
    windowStates,
  ]);

  return (
    <canvas
      ref={canvasRef}
      className="absolute inset-0 pointer-events-none"
      style={{ display: 'block' }}
    />
  );
};

export default AnimatedBackground;

AND:

// contexts/WindowManagerContext.tsx

'use client';

import React, { createContext, useContext, useState, useEffect } from 'react';

type WindowProps = {
  width?: string;
  height?: string;
  initialState?: 'open' | 'closed' | 'minimized';
  gravity?: number; // New gravity property
};

type WindowData = {
  name: string;
  Component: React.ComponentType;
  windowProps: WindowProps;
};

type WindowState = {
  visible: boolean;
  minimized: boolean;
  maximized: boolean;
  width: number;
  height: number;
  x: number;
  y: number;
  zIndex: number;
  prevWidth?: number;
  prevHeight?: number;
  prevX?: number;
  prevY?: number;
};

type WindowManagerContextType = {
  windows: { [key: string]: WindowData };
  windowStates: { [key: string]: WindowState };
  openWindow: (name: string) => void;
  closeWindow: (name: string) => void;
  minimizeWindow: (name: string) => void;
  restoreWindow: (name: string) => void;
  maximizeWindow: (name: string) => void;
  updateWindowState: (name: string, newState: Partial<WindowState>) => void;
  bringToFront: (name: string) => void;
};

const WindowManagerContext = createContext<WindowManagerContextType | undefined>(undefined);

export const useWindowManager = () => {
  const context = useContext(WindowManagerContext);
  if (!context) {
    throw new Error('useWindowManager must be used within a WindowManagerProvider');
  }
  return context;
};

export const WindowManagerProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [windows, setWindows] = useState<{ [key: string]: WindowData }>({});
  const [windowStates, setWindowStates] = useState<{ [key: string]: WindowState }>({});
  const [nextZIndex, setNextZIndex] = useState(1);

  // Define grid size constant
  const GRID_SIZE = 25;

  type WindowDataAndState = {
    windowData: WindowData;
    windowState: WindowState;
  };

  useEffect(() => {
    // Dynamically import window components
    const importWindows = async () => {
      const windowsContext = require.context('../windows', false, /\.tsx$/);
      const windowFiles = windowsContext.keys();

      let zIndexCounter = 1;

      const windowsDataPromises: Promise<WindowDataAndState>[] = windowFiles.map(
        async (file: string, index: number): Promise<WindowDataAndState> => {
          const mod = await windowsContext(file);
          const name = file.replace('./', '').replace('.tsx', '');
          const Component = mod.default;
          const windowProps = mod.windowProps || {};

          // Parse initial size
          let width = windowProps.width
            ? parseSize(windowProps.width, 'width')
            : 0.35 * window.innerWidth;
          let height = windowProps.height
            ? parseSize(windowProps.height, 'height')
            : 0.2 * window.innerHeight;

          // Adjust width and height to align with grid
          width = Math.round(width / GRID_SIZE) * GRID_SIZE;
          height = Math.round(height / GRID_SIZE) * GRID_SIZE;

          // Determine initial state
          const initialState = windowProps.initialState || 'open';
          let visible = true;
          let minimized = false;

          switch (initialState) {
            case 'open':
              visible = true;
              minimized = false;
              break;
            case 'closed':
              visible = false;
              minimized = false;
              break;
            case 'minimized':
              visible = false;
              minimized = true;
              break;
            default:
              visible = true;
              minimized = false;
              break;
          }

          // Adjust initial positions to align with grid
          const initialX = Math.round((100 + index * 50) / GRID_SIZE) * GRID_SIZE;
          const initialY = Math.round((100 + index * 50) / GRID_SIZE) * GRID_SIZE;

          const windowData: WindowData = { name, Component, windowProps };
          const windowState: WindowState = {
            visible,
            minimized,
            maximized: false,
            width,
            height,
            x: initialX,
            y: initialY,
            zIndex: zIndexCounter++,
          };

          return { windowData, windowState };
        }
      );

      const windowsDataArray: WindowDataAndState[] = await Promise.all(windowsDataPromises);

      const newWindows: { [key: string]: WindowData } = {};
      const newWindowStates: { [key: string]: WindowState } = {};

      windowsDataArray.forEach(({ windowData, windowState }) => {
        newWindows[windowData.name] = windowData;
        newWindowStates[windowData.name] = windowState;
      });

      setWindows(newWindows);
      setWindowStates(newWindowStates);
      setNextZIndex(zIndexCounter);
    };

    importWindows();
  }, []);

  const bringToFront = (name: string) => {
    setWindowStates((prev) => {
      const maxZ = nextZIndex;
      setNextZIndex(maxZ + 1);
      return {
        ...prev,
        [name]: {
          ...prev[name],
          zIndex: maxZ,
        },
      };
    });
  };

  const openWindow = (name: string) => {
    setWindowStates((prev) => ({
      ...prev,
      [name]: {
        ...prev[name],
        visible: true,
        minimized: false,
      },
    }));
    bringToFront(name);
  };

  const closeWindow = (name: string) => {
    setWindowStates((prev) => ({
      ...prev,
      [name]: {
        ...prev[name],
        visible: false,
        minimized: false,
      },
    }));
  };

  const minimizeWindow = (name: string) => {
    setWindowStates((prev) => ({
      ...prev,
      [name]: {
        ...prev[name],
        minimized: true,
        visible: false,
      },
    }));
  };

  const restoreWindow = (name: string) => {
    setWindowStates((prev) => ({
      ...prev,
      [name]: {
        ...prev[name],
        minimized: false,
        visible: true,
      },
    }));
    bringToFront(name);
  };

  const maximizeWindow = (name: string) => {
    setWindowStates((prev) => {
      const state = prev[name];
      const isMaximized = !state.maximized;

      if (isMaximized) {
        // Store previous size and position
        const prevSizePosition = {
          prevWidth: state.width,
          prevHeight: state.height,
          prevX: state.x,
          prevY: state.y,
        };

        const maxWidth = window.innerWidth - 20; // Adjusted width for spacing
        const maxHeight = window.innerHeight - 20; // Adjusted height for spacing

        // Adjust to align with grid
        const adjustedWidth = Math.floor(maxWidth / GRID_SIZE) * GRID_SIZE;
        const adjustedHeight = Math.floor(maxHeight / GRID_SIZE) * GRID_SIZE;

        return {
          ...prev,
          [name]: {
            ...state,
            maximized: true,
            ...prevSizePosition,
            x: 10, // Adjusted for spacing when maximized
            y: 10, // Adjusted for spacing when maximized
            width: adjustedWidth,
            height: adjustedHeight,
          },
        };
      } else {
        // Restore previous size and position
        return {
          ...prev,
          [name]: {
            ...state,
            maximized: false,
            width: state.prevWidth || state.width,
            height: state.prevHeight || state.height,
            x: state.prevX || state.x,
            y: state.prevY || state.y,
          },
        };
      }
    });
    bringToFront(name);
  };

  const updateWindowState = (
    name: string,
    newState: Partial<WindowState>,
    skipGrid = false
  ) => {
    setWindowStates((prev) => {
      let updatedState = {
        ...prev[name],
        ...newState,
      };
  
      if (!skipGrid) {
        // Ensure positions and sizes align with the grid
        updatedState.x = Math.round(updatedState.x / GRID_SIZE) * GRID_SIZE;
        updatedState.y = Math.round(updatedState.y / GRID_SIZE) * GRID_SIZE;
        updatedState.width = Math.round(updatedState.width / GRID_SIZE) * GRID_SIZE;
        updatedState.height = Math.round(updatedState.height / GRID_SIZE) * GRID_SIZE;
      }
  
      return {
        ...prev,
        [name]: updatedState,
      };
    });
  };

  function parseSize(size: string, dimension: 'width' | 'height'): number {
    if (typeof window === 'undefined') return 400;
    if (size.endsWith('%')) {
      const percentage = parseFloat(size) / 100;
      if (dimension === 'width') {
        return percentage * window.innerWidth;
      } else {
        return percentage * window.innerHeight;
      }
    } else if (size.endsWith('px')) {
      return parseFloat(size);
    } else {
      return dimension === 'width' ? 400 : 300;
    }
  }

  return (
    <WindowManagerContext.Provider
      value={{
        windows,
        windowStates,
        openWindow,
        closeWindow,
        minimizeWindow,
        restoreWindow,
        maximizeWindow,
        updateWindowState,
        bringToFront,
      }}
    >
      {children}
    </WindowManagerContext.Provider>
  );
};

TASK: Can we make it so that it is easier to drag windows around because currently it is very hard to drag windows around so make it easier and faster and less reseorce intensive. Write the code out in full