Create a complete, interactive p5.js sketch based on these detailed specifications:

```json
{
  "SIMULATION": "Projectile Motion Interactive Simulation",
  "DESCRIPTION": "This simulation demonstrates projectile motion with adjustable launch angle, initial velocity, gravity, and air resistance. Users can observe the trajectory of a projectile and analyze its range, maximum height, and time of flight. The simulation includes visual aids like trajectory lines and range markers.",
  "CONTROLS": [
    {
      "type": "slider",
      "parameter": "launchAngle",
      "label": "Launch Angle (°)",
      "min": 0,
      "max": 90,
      "default": 45,
      "step": 1,
      "description": "Adjusts the initial launch angle of the projectile in degrees."
    },
    {
      "type": "slider",
      "parameter": "initialVelocity",
      "label": "Initial Velocity (m/s)",
      "min": 5,
      "max": 50,
      "default": 25,
      "step": 1,
      "description": "Adjusts the initial velocity of the projectile in meters per second."
    },
    {
      "type": "slider",
      "parameter": "gravity",
      "label": "Gravity (m/s²)",
      "min": 0.1,
      "max": 20,
      "default": 9.81,
      "step": 0.1,
      "description": "Adjusts the acceleration due to gravity."
    },
    {
      "type": "slider",
      "parameter": "airResistance",
      "label": "Air Resistance (Drag Coefficient)",
      "min": 0.0,
      "max": 0.2,
      "default": 0.0,
      "step": 0.01,
      "description": "Adjusts the air resistance coefficient (drag). 0 means no air resistance."
    },
    {
      "type": "button",
      "label": "Launch",
      "function": "launchProjectile",
      "description": "Launches the projectile with the current settings."
    },
    {
      "type": "button",
      "label": "Reset",
      "function": "resetSimulation",
      "description": "Resets the projectile to its initial position and clears the trajectory."
    },
    {
      "type": "button",
      "label": "Toggle Trail",
      "function": "toggleTrail",
      "description": "Toggles the display of the projectile's trajectory trail."
    }
  ],
  "VISUALS": {
    "projectile": {
      "shape": "circle",
      "size": 15,
      "color": "red",
      "x": "initialX",
      "y": "initialY",
      "description": "Represents the projectile being launched."
    },
    "ground": {
      "shape": "rectangle",
      "x": 0,
      "y": "groundLevel",
      "width": "canvasWidth",
      "height": 20,
      "color": "green",
      "description": "Represents the ground."
    },
    "trajectory": {
      "type": "line",
      "color": "rgba(0, 0, 255, 0.5)",
      "thickness": 2,
      "points": "trajectoryPoints",
      "visibility": "showTrail",
      "description": "A line showing the projectile's path.  `trajectoryPoints` is an array of {x,y} points. `showTrail` is a boolean."
    },
    "rangeMarker": {
      "type": "line",
      "color": "black",
      "thickness": 1,
      "x1": "range",
      "y1": "groundLevel",
      "x2": "range",
      "y2": "groundLevel - 10",
      "description": "A line indicating the range of the projectile."
    },
    "textDisplay": [
        {
            "label": "Range: ",
            "variable": "range",
            "x": 20,
            "y": 30,
            "color": "black",
            "description": "Displays the range of the projectile."
        },
        {
            "label": "Max Height: ",
            "variable": "maxHeight",
            "x": 20,
            "y": 50,
            "color": "black",
            "description": "Displays the maximum height achieved by the projectile."
        },
        {
            "label": "Time of Flight: ",
            "variable": "timeOfFlight",
            "x": 20,
            "y": 70,
            "color": "black",
            "description": "Displays the total time the projectile is in the air."
        }
    ]
  },
  "PHYSICS": {
    "variables": [
      "initialX",
      "initialY",
      "velocityX",
      "velocityY",
      "currentTime",
      "x",
      "y",
      "range",
      "maxHeight",
      "timeOfFlight",
      "launched",
      "groundLevel",
      "trajectoryPoints",
      "showTrail"
    ],
    "initialConditions": {
      "initialX": 50,
      "initialY": 450,
      "currentTime": 0,
      "x": "initialX",
      "y": "initialY",
      "range": 0,
      "maxHeight": 0,
      "timeOfFlight": 0,
      "launched": false,
      "groundLevel": 470,
      "trajectoryPoints": "[]",
      "showTrail": true
    },
    "equations": [
      "velocityX = initialVelocity * cos(radians(launchAngle))",
      "velocityY = initialVelocity * sin(radians(launchAngle))",
      "x = initialX + velocityX * currentTime",
      "y = initialY - (velocityY * currentTime - 0.5 * gravity * currentTime * currentTime)",
      "airResistanceForceX = -0.5 * airResistance * velocityX * velocityX * sign(velocityX)",
      "airResistanceForceY = -0.5 * airResistance * velocityY * velocityY * sign(velocityY)",
      "accelerationX = airResistanceForceX / projectileMass", // projectileMass needs to be defined (e.g., 1)
      "accelerationY = -gravity + (airResistanceForceY / projectileMass)",
      "velocityX = velocityX + accelerationX * deltaTime", // Need to introduce deltaTime for frame-based calculation
      "velocityY = velocityY + accelerationY * deltaTime" // Need to introduce deltaTime for frame-based calculation
    ],
    "calculations": [
      "range = x - initialX",
      "maxHeight = initialY - Math.min(...trajectoryPoints.map(p => p.y)) if trajectoryPoints is not empty, otherwise zero.",
      "timeOfFlight = currentTime"
    ],
    "collisionDetection": {
      "condition": "y >= groundLevel",
      "action": "launched = false; y = groundLevel; timeOfFlight = currentTime;"
    },
    "deltaTime": 0.02  // Simulation timestep. Smaller values are more accurate but slower.
  },
  "INTERACTIONS": {
    "mouse": {
      "click": "N/A - Launch button triggers action",
      "move": "N/A"
    },
    "keyboard": {
      "keys": {
        "SPACE": "If not launched, call `launchProjectile`",
        "R": "Call `resetSimulation`"
      }
    },
    "launchProjectile": {
      "description": "Launches the projectile with current settings. Sets `launched` to true, resets `currentTime`, `x`, `y` and clears the `trajectoryPoints` array.",
      "action": "launched = true; currentTime = 0; x = initialX; y = initialY; trajectoryPoints = [];"
    },
    "resetSimulation": {
      "description": "Resets the projectile to its initial position and state.",
      "action": "launched = false; currentTime = 0; x = initialX; y = initialY; range = 0; maxHeight = 0; timeOfFlight = 0; trajectoryPoints = [];"
    },
    "toggleTrail": {
      "description": "Toggles the display of the trajectory trail.",
      "action": "showTrail = !showTrail"
    },
    "simulationUpdate": {
      "description": "Updates the projectile's position and other parameters each frame.",
      "condition": "launched",
      "actions": [
        "currentTime += deltaTime",
        "Update velocityX and velocityY based on airResistance force calculations.",
        "x = initialX + velocityX * currentTime",
        "y = initialY - (velocityY * currentTime - 0.5 * gravity * currentTime * currentTime)",
        "trajectoryPoints.push({x: x, y: y})",
        "if (y >= groundLevel) { collisionDetection.action }",
        "Calculate range, maxHeight and timeOfFlight based on the updated positions and flight history."
      ]
    }
  }
}
```

Requirements:
- Include both setup() and draw() functions
- Use createCanvas(400, 400) to set canvas size
- Implement ALL specified controls (sliders, buttons) using p5.js GUI libraries or simple implementations
- Add all visual elements as described
- Implement the physics/logic accurately
- Include all specified user interactions
- Add clear comments explaining the code
- Make it educational and visually appealing
- Ensure smooth animations and responsive controls

IMPORTANT: Only output the JavaScript code, no explanations or markdown formatting. The code should be self-contained and runnable in the p5.js editor with all interactive controls working.