package com.example.PhotoGallery.Services;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;

import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.example.PhotoGallery.Models.Photo;
import com.example.PhotoGallery.Repos.PhotoRepo;

@Service
public class PhotoService {

    @Autowired
    private ExtractImageMetadata extractMetaData;
    @Autowired
    private ImageCompression imageCompression;
    @Autowired
    private PhotoRepo photoRepo;

    private Path originalFilesPath;
    private Path thumbnailsPath;

    public PhotoService(@Value("${photogallery.paths.originals}") String originalFilesPath,
            @Value("${photogallery.paths.thumbnails}") String thumbnailsPath) {
        this.originalFilesPath = Paths.get(originalFilesPath);
        this.thumbnailsPath = Paths.get(thumbnailsPath);
    }

    /**
     * Get all the original images from the server
     * then compress each image
     * and write the compressed image(thumbnail) to the thumnails directory.
     * We also save a record of the image to the DB (writePhotoDataToDB)
     * 
     * 
     */
    @Async("taskExecutor")
    public void writeImagesFromDirToDB() {

        List<String> allFileName = getAllFileNames();

        try (Stream<Path> imagePaths = Files.list(originalFilesPath)) {
            imagePaths.forEach(imagePath -> {

                if (Thread.currentThread().isInterrupted()) {
                    return;
                }

                String filename = imagePath.getFileName().toString();
                if (!allFileName.contains(filename)) {

                    imageCompression.compressImage(
                            imagePath.toString(),
                            thumbnailsPath.toString());

                    try {
                        writePhotoDataToDB(imagePath);
                    } catch (DataIntegrityViolationException ignored) {
                        // already exists
                    }

                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public List<String> getAllFileNames() {

        return photoRepo.getAllFileNames();

    }

    public List<Photo> getAllPhotots() {
        return photoRepo.findAll();
    }

    public Page<Photo> getPagedPhotos(int pageNo, int pageSize) {
        Pageable pageable = PageRequest.of(pageNo, pageSize);
        return photoRepo.getPagedPhotos(pageable);
    }

    public List<Photo> getRandomPhotos(int numRandRecords) {
        return photoRepo.getRandomPhotos(numRandRecords);
    }

    public List<Photo> searchPhotos(String search) {

        return photoRepo.searchPhotos(search);
    }

    public Photo findById(Long id) {
        return photoRepo.findById(id).get();
    }

    public void save(Photo photo) {
        photoRepo.save(photo);
    }

    public Page<Photo> findAllExpect(List<Long> ids, int pageNo, int pageSize) {
        Pageable pageable = PageRequest.of(pageNo, pageSize);
        return photoRepo.findAllExpect(ids, pageable);
    }

    public void updatePhotoDetails(Long id, Photo newPhoto) {

        Photo photo = photoRepo.findById(id)
                .orElseThrow(() -> new RuntimeException("No Photo with Id" + id));

        photo.setTitle(newPhoto.getTitle());
        photo.setDescription(newPhoto.getDescription());
        photo.setTags(newPhoto.getTags());
        photo.setCopyRight(newPhoto.getCopyRight());

        photoRepo.save(photo);

    }

    public void setFavourite(Long id) {

        Photo photo = photoRepo.findById(id)
                .orElseThrow(() -> new RuntimeException("No Photo with Id" + id));

        photo.setFavourite(!photo.isFavourite());

        photoRepo.save(photo);

    }

    private void writePhotoDataToDB(Path imagePath) {

        Photo photo = new Photo();

        photo.setFilePath("images" + "/" + imagePath.getFileName().toString());
        photo.setFileName(imagePath.getFileName().toString());
        photo.setTitle(imagePath.getFileName().toString());
        photo.setFavourite(false);

        JSONObject extractedData = extractMetaData.getImageMetaData(imagePath.toString());
        populatePhotoData(photo, extractedData);

        photo.setThumbnailPath("thumbnails" + "/" + imagePath.getFileName().toString());

        // Set<String> generatedTags = taggingService.generateTags(imagePath);
        // photo.getTags().addAll(generatedTags);

        photoRepo.save(photo);

    }

    /**
     * 
     * Populate the passed in photo object with the extracted data
     * 
     * 
     * @param photo
     * @param extractedData
     * @return
     */
    private Photo populatePhotoData(Photo photo, JSONObject extractedData) {
        // File Size
        JSONObject fileObj = (JSONObject) extractedData.get("File");
        String fileSize = (fileObj != null && fileObj.get("File Size") != null)
                ? fileObj.get("File Size").toString().replace(" bytes", "")
                : null;

        // MIME Type
        JSONObject fileTypeObj = (JSONObject) extractedData.get("File Type");
        String mimeType = (fileTypeObj != null && fileTypeObj.get("Detected File Type Name") != null)
                ? fileTypeObj.get("Detected File Type Name").toString()
                : null;

        // JPEG dimensions
        JSONObject jpegObj = (JSONObject) extractedData.get("JPEG");
        int height = 0;
        int width = 0;
        if (jpegObj != null) {
            if (jpegObj.get("Image Height") != null) {
                try {
                    height = Integer.parseInt(jpegObj.get("Image Height").toString().replace(" pixels", ""));
                } catch (NumberFormatException ignored) {
                }
            }
            if (jpegObj.get("Image Width") != null) {
                try {
                    width = Integer.parseInt(jpegObj.get("Image Width").toString().replace(" pixels", ""));
                } catch (NumberFormatException ignored) {
                }
            }
        }

        // Exif data
        JSONObject exifSubIFD = (JSONObject) extractedData.get("Exif SubIFD");
        String colourSpace = (exifSubIFD != null && exifSubIFD.get("Color Space") != null)
                ? exifSubIFD.get("Color Space").toString()
                : null;

        JSONObject exifIFD0 = (JSONObject) extractedData.get("Exif IFD0");
        String copyright = (exifIFD0 != null && exifIFD0.get("Copyright") != null)
                ? exifIFD0.get("Copyright").toString()
                : null;

        photo.setFile_size(Math.ceil(Double.parseDouble(fileSize) / 1048576));
        photo.setHeight(height);
        photo.setWidth(width);
        photo.setMimeType(mimeType);
        photo.setColourSpace(colourSpace);
        photo.setCopyRight(copyright);
        photo.setTags(null);

        return photo;
    }

}
