Source: pages-sections/Foodhealth-sections/CameraContent.js

import classNames from "classnames";
import { makeStyles } from "@material-ui/core/styles";
import React, { useEffect, useState, useMemo } from "react";
import { getDatabase, ref, get } from "firebase/database";
import Link from "next/link";

import styles from "/styles/jss/nextjs-material-kit/pages/components.js";
import CameraInput from "./CameraInput";
import LocalStorageInput from "./LocalStorageInput";
import Button from "/components/CustomButtons/Button.js";
import Card from "/components/Card/Card.js";
import CardMedia from "@material-ui/core/CardMedia";
import CardBody from "/components/Card/CardBody.js";
import CardFooter from "/components/Card/CardFooter.js";
import GridContainer from "/components/Grid/GridContainer.js";
import GridItem from "/components/Grid/GridItem.js";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch";

const useStyles = makeStyles(styles);

/**
 * The `CameraContent` function in JavaScript fetches food data based on image predictions and displays
 * food information along with images.
 * @returns The `CameraContent` function is being exported as the default export. It returns JSX
 * elements that conditionally render different sections based on the state of the component.
 */
export function CameraContent() {
  const classes = useStyles();
  const [checkedStates, setCheckedStates] = useState([]);
  const [image, setImage] = React.useState(null);
  const [predictions, setPredictions] = React.useState(null);
  const [foods, setFoods] = useState([]);
  const [debouncedSearch, setDebouncedSearch] = useState("");

  const database = getDatabase();

  useEffect(() => {
    const fetchFoods = async () => {
      if (debouncedSearch.trim() === "") {
        setFoods([]);
        return;
      }

      const foodRef = ref(database, "food/");
      try {
        const snapshot = await get(foodRef);
        if (snapshot.exists()) {
          const foodArray = Object.entries(snapshot.val()).map(
            ([idd, data]) => ({
              idd,
              ...data,
            })
          );
          const filteredFoods = foodArray.filter((food) =>
            food.idd.toLowerCase().includes(debouncedSearch.toLowerCase())
          );
          setFoods(filteredFoods);
          setCheckedStates(Array(foodArray.length).fill(false));
        } else {
          console.log("No data available");
        }
      } catch (error) {
        console.error("Error retrieving data:", error);
      }
    };

    fetchFoods();
  }, [debouncedSearch]);

  const cachedFoods = useMemo(() => foods, [foods]);

  return (
    <>
      {!image && (
        <div style={{ display: "flex", justifyContent: "center", gap: "10px" }}>
          <CameraInput onImageCapture={setImage} />{" "}
          <LocalStorageInput onImageSelect={setImage} />
        </div>
      )}
      {image && (
        <div>
          <div
            style={{ display: "flex", justifyContent: "center", gap: "25px" }}
          >
            <img
              src={image}
              alt="Selected Image"
              style={{
                maxWidth: "1000px",
                maxHeight: "1000px",
                width: "auto",
                height: "auto",
                objectFit: "contain",
              }}
            />
          </div>
          <div
            style={{ display: "flex", justifyContent: "center", gap: "25px" }}
          >
            <Button color="info" onClick={uploadImageAndGetPrediction}>
              Get Prediction
            </Button>
            <Button color="danger" onClick={handleDiscardClick}>
              Discard Picture
            </Button>
          </div>
        </div>
      )}
      {foods.length > 0 && (
        <GridContainer justify="left" spacing={4}>
          {cachedFoods.map((food, index) => (
            <GridItem xs={12} sm={6} md={4} lg={4} key={food.idd}>
              <Card style={{ backgroundColor: "#fff9c4" }}>
                <CardMedia
                  align="left"
                  component="img"
                  alt="Image cannot be loaded"
                  width="100"
                  height="200"
                  image={
                    food.category !== "Others"
                      ? `/img/foods/${food.category.toLowerCase()}.jpg`
                      : "/img/fudpic.jpg"
                  }
                  title="Picture of a food"
                />
                <CardBody>
                  <h4 className={food.idd} style={{ fontWeight: "bold" }}>
                    {food.idd}
                  </h4>
                  <p>
                    Calories:{" "}
                    {checkedStates[index] ? food.scalories : food.calories}
                  </p>
                  <p>Fats: {checkedStates[index] ? food.sfats : food.fats}</p>
                  <p>
                    Carbs: {checkedStates[index] ? food.scarbs : food.carbs}
                  </p>
                  <p>
                    Protein:{" "}
                    {checkedStates[index] ? food.sprotein : food.protein}
                  </p>
                </CardBody>
                <CardFooter>
                  {/* More Info button linking to FoodInfo page */}
                  <Link href={`/foodinfo?id=${food.id}`}>
                    <a>More Info</a>
                  </Link>
                </CardFooter>
              </Card>
            </GridItem>
          ))}
        </GridContainer>
      )}
    </>
  );
}
/**
 * The function `handleDiscardClick` logs "Hello" to the console and resets the image and foods state
 * variables.
 * @returns If the `image` variable is falsy, the function will return early without executing the
 * remaining code.
 */
export const handleDiscardClick = () => {
  console.log("Hello");
  if (!image) return;
  setImage(null);
  setFoods([]);
};
/**
 * The function `fetchFoodInfo` asynchronously retrieves food information based on provided IDs,
 * filters the data, and sets states accordingly.
 * @param foodIds - Food IDs are unique identifiers for different food items in the database. The
 * `fetchFoodInfo` function takes an array of foodIds as a parameter to retrieve information about
 * those specific food items from the database.
 */
export const fetchFoodInfo = async (foodIds) => {
  console.log("Requested food IDs:", foodIds);
  const foodRef = ref(database, "food/");
  try {
    const snapshot = await get(foodRef);
    if (snapshot.exists()) {
      const foodArray = Object.entries(snapshot.val()).map(([idd, data]) => ({
        idd,
        ...data,
      }));
      console.log("Fetched food array:", foodArray);
      const filteredFoods = foodArray.filter((food) =>
        foodIds.includes(food.id)
      );
      console.log("Filtered foods:", filteredFoods);
      setFoods(filteredFoods);
      // Initialize checkedStates with false for each food item
      setCheckedStates(filteredFoods.map(() => false));
    } else {
      console.log("No data available");
    }
  } catch (error) {
    console.error("Error retrieving data:", error);
  }
};
/**
 * The function `uploadImageAndGetPrediction` fetches an image blob, sends it to a Flask API for
 * prediction, processes the prediction result, and fetches additional food information based on the
 * prediction.
 */
export const uploadImageAndGetPrediction = async () => {
  try {
    console.log("Fetching image blob...");
    const imageBlob = await fetch(image).then((r) => r.blob());
    const formData = new FormData();
    formData.append("file", imageBlob, "image.jpg");

    console.log("Sending image to Flask API...");
    const FLASK_API_URL = "http://127.0.0.1:5000/predict";
    const response = await fetch(FLASK_API_URL, {
      method: "POST",
      body: formData,
    });

    if (!response.ok) {
      throw new Error("Failed to upload image and get prediction");
    }

    const result = await response.json();
    console.log("Prediction received:", result.prediction);
    const foodIds = result.prediction;
    setPredictions(foodIds);

    console.log("Fetching food info...");
    await fetchFoodInfo(foodIds);
  } catch (error) {
    console.error("Error:", error);
  }
};