export function createSketch(p, onGameData) {
  //cell setting
  let cellSize = 20;

  let snake = [
    {x: 10, y: 5},
    {x: 9, y: 5},
    {x: 8, y: 5},
    {x: 7, y: 5},
    {x: 6, y: 5},
    {x: 5, y: 5},
    {x: 4, y: 5},
    {x: 3, y: 5}
  ]; // initial length = 8

  let headImg, bodyImg, cornerImg, tailImg, obstacleImg, heartImg, gemstoneImg, sunImg;

  //load images
  p.preload = () => {
    headImg = p.loadImage("/assets/segments/head.png");
    bodyImg = p.loadImage("/assets/segments/body.png");
    cornerImg = p.loadImage("/assets/segments/corner.png");
    tailImg = p.loadImage("/assets/segments/tail.png");
    obstacleImg = p.loadImage("/assets/obstacle.png");
    heartImg = p.loadImage("/assets/items/heart.png");
    gemstoneImg = p.loadImage("/assets/items/gemstone.png");
    sunImg = p.loadImage("/assets/items/sun.png");
  };

  //game state variables
  let life = 3;
  let score = 0;
  let speed = 7;
  let obstacles = [];
  let obstacleInterval = 100;
  let walls = [];
  let items = [];
  let growNext = false;
  let dirHead = { x: 1, y: 0 };
  let dirTail = { x: -1, y: 0 };
  let gameState = "play";


  //game data
  function updateGameData() {
    if (typeof onGameData == "function") {
      onGameData({life, score, state: gameState});
    }
  }

  //rotation direction
  function rotationByDir(dir) {
    if (dir.x === 1 && dir.y === 0) return 0;
    if (dir.x === 0 && dir.y === 1) return p.HALF_PI;
    if (dir.x === -1 && dir.y === 0) return p.PI;
    if (dir.x === 0 && dir.y === -1) return -p.HALF_PI;
    return 0;
  }

  //make obstacle
  function makeObstacle() {
    // moving head or tail? which direction?
    let base;
    let dir;

    if (dirHead.x !==0 || dirHead.y !== 0) {
      base = snake[0];
      dir = dirHead;
    }
    else if (dirTail.x !== 0 || dirTail.y !== 0){
      base = snake[snake.length - 1];
      dir = dirTail;
    }
    else {
      return;
    }

    //gap between the snake and an obstacle
    const gap = p.floor(p.random(3, 7));
    let newX = base.x + dir.x * gap + p.floor(p.random(-1, 2));
    let newY = base.y + dir.y * gap + p.floor(p.random(-1, 2));


    //is in canvas? (clamp)
    newX = p.constrain(newX, 0, p.floor(p.width / cellSize) - 1);
    newY = p.constrain(newY, 0, p.floor(p.height / cellSize) - 1);

    //overlap check
    const overlap = [...snake, ...walls].some(seg => seg.x == newX && seg.y == newY);
    if (overlap) return;

    obstacles = [{x: newX, y: newY, hit: null, time: p.frameCount}];
  }

  //make items
  function makeItem() {
    const cols = p.width / cellSize;
    const rows = p.height / cellSize;

    let newX = p.floor(p.random(cols));
    let newY = p.floor(p.random(rows));

    const overlap = [...snake, ...walls, ...obstacles].some(seg => seg.x == newX && seg.y == newY);
    if (overlap) return;

    const types = ["heart", "gemstone", "sun"];
    const type = types[p.floor(p.random(types.length))];

    items.push({x: newX, y: newY, type});
  }

  p.setup = () => {
    p.createCanvas(1000, 520);
    p.imageMode(p.CENTER);

    const cols = p.width / cellSize;
    const rows = p.height / cellSize;

    for (let i = 0; i < cols; i += 1) {
      walls.push({x:i, y:0});
      walls.push({x:i, y: rows - 1});
    }
    for (let i = 0; i < rows; i += 1) {
      walls.push({x:0, y:i});
      walls.push({x:cols - 1, y: i});
    }

    makeItem();
  };

  p.draw = () => {
    //screen change conditions
    if (gameState === "over" || gameState === "clear") {
      updateGameData();
      p.noLoop();
      return;
    }

    p.background(0);

    updateGameData();

    let grow = growNext;
    growNext = 0;

    const head = snake[0];
    const tail = snake[snake.length - 1];

    //default movement
    if (gameState !== "play") return;

    if (score >= 90) {
      speed = 3;
    }
    else if (score >= 70){
      speed = 5;
    }
    else {
      speed = 7;
    }

    if (snake.length < 2) return;
    
    if (p.frameCount % speed == 0) {
      //new head & tail
      const head = snake[0];
      const tail = snake[snake.length - 1];
      const newHead = {
        x: head.x + dirHead.x,
        y: head.y + dirHead.y
      };
      const newTail = {
        x: tail.x + dirTail.x,
        y: tail.y + dirTail.y
      };

      if(dirHead.x !== 0 || dirHead.y !== 0) {
        snake.unshift(newHead);

        items = items.filter(item => {
          const hit = (head.x == item.x && head.y == item.y);
    
          if (hit) {
            if (item.type == "heart") {
              score += 5;
              growNext += 1;
            }
            else if (item.type == "gemstone") {
              score += 10;
              growNext += 2;
            }
            else if (item.type == "sun") {
              score += 15;
              growNext += 3;
            }
            makeItem();
            return false;
          }
          return true;
        });

        if(!growNext) snake.pop();
        growNext = false;
      }
      else if (dirTail.x !== 0 || dirTail.y !== 0) {
        snake.push(newTail);
        if (!growNext) {
          snake.shift();
          growNext = false;
        }
      }
      
      //collision test
      if (snake.length >= 2) {
        const updatedHead = snake[0];
        const updatedTail = snake[snake.length - 1];
        if (updatedHead.x == updatedTail.x && updatedHead.y == updatedTail.y) {
          gameState = "over";
          return;
        }
      }
    }

    ////GET ITEMS////

    //items rendering
    items.forEach(i => {
      const px = i.x * cellSize + cellSize / 2;
      const py = i.y * cellSize + cellSize / 2;

      if (i.type == "heart") {
        p.image(heartImg, px, py, cellSize, cellSize);
      }
      else if (i.type == "gemstone") {
        p.image(gemstoneImg, px, py, cellSize, cellSize);
      }
      if (i.type == "sun") {
        p.image(sunImg, px, py, cellSize, cellSize);
      }
    })

    if (items.length == 0) {
      makeItem();
    }

    ////SNAKE MOVEMENT////
      
    //snake rendering
    for (let i = 0; i < snake.length; i+=1) {
      const seg = snake[i];
      const px = seg.x * cellSize + cellSize/2;
      const py = seg.y * cellSize + cellSize/2;

      p.push();
      p.translate(px, py);
      
      //rotate segments
      if (i == 0) {
        //head
        const angle = rotationByDir({
          x: seg.x - snake[1].x,
          y: seg.y - snake[1].y
         });
         p.rotate(angle);
         p.image(headImg, 0, 0, cellSize, cellSize);
      }
      else if (i == snake.length - 1) {
        //tail
        const angle = rotationByDir({
          x: snake[i - 1].x - seg.x,
          y: snake[i - 1].y - seg.y
         });
        p.rotate(angle);
        p.image(tailImg, 0, 0, cellSize, cellSize);
      }
      else {
        //body & corner
        const prev = snake[i - 1];
        const next = snake[i + 1];

        const dx1 = seg.x - prev.x;
        const dy1 = seg.y - prev.y;
        const dx2 = next.x - seg.x;
        const dy2 = next.y - seg.y;

        const isCorner = dx1 !== dx2 || dy1 !== dy2;

        if (isCorner) {
          //corner
          let angle = 0;

          if (dx1 == -1 && dy1 == 0 && dx2 == 0 && dy2 == 1) {
            angle = 3 * p.HALF_PI;
          } //←↓
          else if (dx1 == 0 && dy1 == 1 && dx2 == 1 && dy2 == 0) {
            angle = p.PI;
          } //↓→
          else if (dx1 == 1 && dy1 == 0 && dx2 == 0 && dy2 == -1) {
            angle = p.HALF_PI;
          } //→↑
          else if (dx1 == 0 && dy1 == -1 && dx2 == -1 && dy2 == 0) {
            angle = 0;
          } //↑←
          else if (dx1 == 0 && dy1 == 1 && dx2 == -1 && dy2 == 0) {
            angle = p.HALF_PI; 
          } //↓←
          else if (dx1 == 1 && dy1 == 0 && dx2 == 0 && dy2 == 1) {
            angle = 0;
          } //→↓
          else if (dx1 == 0 && dy1 == -1 && dx2 == 1 && dy2 == 0) {
            angle = -p.HALF_PI;
          } //↑→
          else if (dx1 == -1 && dy1 == 0 && dx2 == 0 && dy2 == -1) {
            angle = p.PI;
          } //←↑

          p.rotate(angle);
          p.image(cornerImg, 0, 0, cellSize, cellSize);
        }
        else {
          //body
          const angle = dx1 == 0 ? p.HALF_PI : 0;
          p.rotate(angle);
          p.image(bodyImg, 0, 0, cellSize, cellSize);
        }
      }

      p.pop();
    }

    ////COLLISION WITH OBSTACLE////

    //show obstacle
    if (p.frameCount % obstacleInterval == 0) {
      const o = obstacles[0];
    
      const shouldMake = !o || o.hit !== null || (o.time !== undefined && p.frameCount - o.time > 120);
    
      if (shouldMake) {
        obstacles = [];
        makeObstacle();
      }
    }

    //collision 
    for (let i = 0; i < obstacles.length; i += 1) {
      const o = obstacles[i];
      if ((head.x == o.x && head.y == o.y) 
        || tail.x == o.x && tail.y == o.y) {
        if (o.hit == null) {
          life -= 1;
          o.hit = p.frameCount;
        }
        break;
      }
    }
  
    //game over condition
    if (life == 0) {
      gameState = "over";
      return;
    }

    //obstacles rendering
    obstacles.forEach(o => {
      const px = o.x * cellSize + cellSize / 2;
      const py = o.y * cellSize + cellSize / 2;

      if (o.hit == null) {
        p.image(obstacleImg, px, py, cellSize, cellSize);
      }
      else {
        const count = p.frameCount - o.hit;
        if (count < 32) {
          if (count % 4 == 0) {
            p.image(obstacleImg, px, py, cellSize, cellSize);
          }
        }
        else {
          const index = obstacles.indexOf(o);
          if (index !== -1) obstacles.splice(index, 1);
        }
      }
    });

    ////COLLISION WITH WALL////

    //wall rendering
    walls.forEach (w => {
      const px = w.x * cellSize + cellSize / 2;
      const py = w.y * cellSize + cellSize / 2;
      p.image(obstacleImg, px, py, cellSize, cellSize);
    });

    //collision
    for (let i = 0; i < walls.length; i += 1) {
      const w = walls[i];
      if ((head.x == w.x && head.y == w.y) || (tail.x == w.x && tail.y == w.y)) {
        gameState = "over";
        return;
      }
    }

    if (score >= 100) {
      gameState = "clear";
      return;
    }
    
    
   
  };//draw end

  //operating snake
  p.keyPressed = () => {
    // Head
    if (p.keyCode === p.LEFT_ARROW && !(dirHead.x === 1 && dirHead.y === 0)) {
      dirHead = {x: -1, y: 0};
      dirTail = {x: 0, y: 0}; // stop tail
    }
    if (p.keyCode === p.RIGHT_ARROW && !(dirHead.x === -1 && dirHead.y === 0)) {
      dirHead = {x: 1, y: 0};
      dirTail = {x: 0, y: 0};
    }
    if (p.keyCode === p.UP_ARROW && !(dirHead.x === 0 && dirHead.y === 1)) {
      dirHead = {x: 0, y: -1};
      dirTail = {x: 0, y: 0};
    }
    if (p.keyCode === p.DOWN_ARROW && !(dirHead.x === 0 && dirHead.y === -1)) {
      dirHead = {x: 0, y: 1};
      dirTail = {x: 0, y: 0};
    }

    // Tail
    if (p.key === 'a' && !(dirTail.x === 1 && dirTail.y === 0)) {
      dirTail = {x: -1, y: 0};
      dirHead = {x: 0, y: 0}; // stop head
    }
    if (p.key === 'd' && !(dirTail.x === -1 && dirTail.y === 0)) {
      dirTail = {x: 1, y: 0};
      dirHead = {x: 0, y: 0};
    }
    if (p.key === 'w' && !(dirTail.x === 0 && dirTail.y === 1)) {
      dirTail = {x: 0, y: -1};
      dirHead = {x: 0, y: 0};
    }
    if (p.key === 's' && !(dirTail.x === 0 && dirTail.y === -1)) {
      dirTail = {x: 0, y: 1};
      dirHead = {x: 0, y: 0};
    }
  };
}



