package com.dji.sample.media.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dji.sample.common.util.*;
import com.dji.sample.component.oss.model.OssConfiguration;
import com.dji.sample.component.oss.service.impl.OssServiceContext;
import com.dji.sample.manage.model.dto.DeviceDictionaryDTO;
import com.dji.sample.manage.service.IDeviceDictionaryService;
import com.dji.sample.map.model.entity.GroupElementEntity;
import com.dji.sample.map.model.param.MediaFileQueryParam;
import com.dji.sample.map.service.IGroupElementService;
import com.dji.sample.map.service.IGroupService;
import com.dji.sample.media.dao.IFileMapper;
import com.dji.sample.media.dao.IFileSumMapper;
import com.dji.sample.media.dao.IOssFileMapper;
import com.dji.sample.media.model.*;
import com.dji.sample.media.service.IFileService;
import com.dji.sample.media.service.IMediaDirService;
import com.dji.sample.wayline.dao.IWaylineJobMapper;
import com.dji.sample.wayline.model.entity.WaylineJobEntity;
import com.dji.sample.wayline.service.IWaylineFileService;
import com.dji.sdk.cloudapi.device.DeviceEnum;
import com.dji.sdk.cloudapi.map.*;
import com.dji.sdk.cloudapi.media.MediaSubFileTypeEnum;
import com.dji.sdk.cloudapi.media.MediaUploadCallbackRequest;
import com.dji.sdk.cloudapi.wayline.GetWaylineListResponse;
import com.dji.sdk.common.CoordinateUtil;
import com.dji.sdk.common.Pagination;
import com.dji.sdk.common.PaginationData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URL;
import java.net.URLEncoder;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * @author sean
 * @version 0.2
 * @date 2021/12/9
 */
@Slf4j
@Service
@Transactional
public class FileServiceImpl implements IFileService {

    @Autowired
    private IFileMapper mapper;

    @Autowired
    private IWaylineJobMapper waylineJobMapper;

    @Autowired
    private IFileSumMapper sumMapper;

    @Autowired
    private IOssFileMapper ossFileMapper;

    @Autowired
    private IDeviceDictionaryService deviceDictionaryService;

    @Autowired
    private OssServiceContext ossService;

    @Autowired
    private IMediaDirService mediaDirService;

    @Autowired
    private  IWaylineFileService waylineFileService;

    @Autowired
    private  IGroupElementService groupElementService;

    @Autowired
    private  IGroupService groupService;

    @Autowired
    private Executor fileThreadPool;

    private static final String IMAGE = "image";
    private static final String VIDEO = "video";
    private static final String TAIL = "_100x100";
    private static final String T_PATH = "thumbnails/";
    private static final String DOT = ".";
    private static final String SPLIT_DOT = "\\.";
    private static final String JPEG = "JPEG";
    private static final String MP4 = "MP4";

    private Optional<MediaFileEntity> getMediaByFingerprint(String workspaceId, String fingerprint) {
        MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getWorkspaceId, workspaceId)
                .eq(MediaFileEntity::getFingerprint, fingerprint));
        return Optional.ofNullable(fileEntity);
    }

    @Override
    public MediaFileDTO getMediaFileByFileId(String workspaceId, String fileId) {
        MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getWorkspaceId, workspaceId)
                .eq(MediaFileEntity::getFileId, fileId));
       return entityConvertToDto(fileEntity);
    }

    @Override
    public ElementPicture getMediaFileByElementId(String workspaceId, String elementId) {
        MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getWorkspaceId, workspaceId)
                .eq(MediaFileEntity::getElementId, elementId)
                .last(" limit 1 "));
        String thumbnailURL = getThumbnailURL(fileEntity);
        return new ElementPicture().setFileId(fileEntity.getFileId()).setMediaType(fileEntity.getMediaType()).setThumbnailUrl(thumbnailURL);
    }

    @Override
    public List<ElementPicture> getElementMediaFiles(String workspaceId) {
        List<MediaFileEntity> fileEntity = mapper.selectList(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getWorkspaceId, workspaceId)
                .isNotNull(MediaFileEntity::getElementId));
        List<ElementPicture> ePList = new ArrayList<>();
        fileEntity.forEach(f -> {
            String thumbnailURL = getThumbnailURL(f);
            ePList.add(new ElementPicture()
                    .setFileId(f.getFileId())
                    .setMediaType(f.getMediaType())
                    .setThumbnailUrl(thumbnailURL)
                    .setElementId(f.getElementId())
                    .setDirId(f.getDirId()));
        });
        return ePList;
    }

    private Optional<MediaFileEntity> getMediaByFileId(String workspaceId, String fileId) {
        MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getWorkspaceId, workspaceId)
                .eq(MediaFileEntity::getFileId, fileId));
        return Optional.ofNullable(fileEntity);
    }

    @Override
    public Boolean checkExist(String workspaceId, String fingerprint) {
        return this.getMediaByFingerprint(workspaceId, fingerprint).isPresent();
    }

    @Override
    public void uploadMediaFile(MultipartFile file, String workspaceId, String dirId, String creator) {
        try (InputStream is = file.getInputStream()) {
            long size = is.available();

            String fileId = UUID.randomUUID().toString();
            String objectKey = OssConfiguration.objectDirPrefix + File.separator + file.getOriginalFilename();

            MediaDirDTO dirDto = mediaDirService.getMediaDirById(workspaceId,dirId);
            if(dirDto == null) {
                throw new RuntimeException("文件夹不存在 ID:" + dirId);
            }

            ossService.putObject(OssConfiguration.bucket, objectKey, file.getInputStream());
            log.info("上传成功. {}", file.getOriginalFilename());
            MediaFileEntity fileEntity = MediaFileEntity.builder()
                    .dirId(Integer.valueOf(dirId))
                    .fileId(fileId)
                    .fileName(file.getOriginalFilename())
                    .filePath(dirDto.getDirName())
                    .workspaceId(workspaceId)
                    .objectKey(objectKey)
                    .subFileType(0)
                    .isOriginal(false)
                    .createTime(System.currentTimeMillis())
                    .updateTime(System.currentTimeMillis())
                    .build();

            mapper.insert(fileEntity);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }

    @Override
    public Integer saveFile(String workspaceId, MediaUploadCallbackRequest file) {
        //保存基本信息
        MediaFileEntity fileEntity = this.fileUploadConvertToEntity(workspaceId,file);
        fileEntity.setWorkspaceId(workspaceId);
        fileEntity.setFileId(UUID.randomUUID().toString());
        int result = mapper.insert(fileEntity);
        //更新exif信息
        updateExifInfo(fileEntity,workspaceId,file);
        return result;
    }

    private void updateExifInfo(MediaFileEntity fileEntity, String workspaceId, MediaUploadCallbackRequest file) {
        //生成缩略图
        fileThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    String objectKey = file.getObjectKey();

                    //生成缩略图
                    String tObjectKey = getThumbnailObjectKey(file.getName());
                    putThumbnailImage(tObjectKey, objectKey, true);

                    //获取图片信息
                    MediaExifDTO exif = PicExifUtil.getPicExif(objectKey);

                    if (file != null) {
                        fileEntity.setImageWidth(exif.getImageWidth());
                        fileEntity.setImageHeight(exif.getImageHeight());
                        fileEntity.setXResolution(exif.getXResolution());
                        fileEntity.setYResolution(exif.getYResolution());
                        fileEntity.setLatitude(BigDecimal.valueOf(CoordinateUtil.checkValidVal(exif.getLatitude())).setScale(14, RoundingMode.DOWN));
                        fileEntity.setLongitude(BigDecimal.valueOf(CoordinateUtil.checkValidVal(exif.getLongitude())).setScale(14, RoundingMode.DOWN));
                        fileEntity.setLongitudeRef(exif.getLongitudeRef());
                        fileEntity.setLatitudeRef(exif.getLatitudeRef());
                        fileEntity.setMediaType(exif.getPictureType());
                        fileEntity.setPictureType(exif.getPictureType());
                        fileEntity.setDurationSeconds(exif.getDurationSeconds());

                        mapper.update(fileEntity,
                                new LambdaUpdateWrapper<MediaFileEntity>().eq(MediaFileEntity::getFileId, fileEntity.getFileId()));
                    }
                } catch (Exception e) {
                    e.printStackTrace();

                }
            }
        });
    }

    private InputStream copyInputStream(InputStream input) {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        IoUtil.copy(input,output);
        return new ByteArrayInputStream(output.toByteArray());
    }

    @Override
    public List<MediaFileDTO> getAllFilesByWorkspaceId(String workspaceId) {
        return mapper.selectList(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getWorkspaceId, workspaceId))
                .stream()
                .map(this::entityConvertToDto)
                .collect(Collectors.toList());
    }

    @Override
    public PaginationData<MediaFileDTO> getMediaFilesPaginationByWorkspaceId(String workspaceId, MediaFileQueryParam param, long page, long pageSize) {
        Page<MediaFileEntity> pageData = mapper.selectPage(
                new Page<MediaFileEntity>(page, pageSize),
                new LambdaQueryWrapper<MediaFileEntity>()
                        .eq(MediaFileEntity::getWorkspaceId, workspaceId)
                        .ge(param != null && param.getBeginTime() != null, MediaFileEntity::getCreateTime,param.getBeginTime())
                        .le(param != null && param.getEndTime() != null, MediaFileEntity::getCreateTime,param.getEndTime())
                        .and(StringUtils.hasText(param.getSearchInfo()),
                                wrapper -> wrapper.like( MediaFileEntity::getFileName, param.getSearchInfo())
                                        .or().like( MediaFileEntity::getPayload, param.getSearchInfo())
                        ).and(MediaTypeEnum.PIC_ORIGIN.getVal() == param.getMediaType(),
                                wrapper -> wrapper.eq( MediaFileEntity::getPictureType, JPEG)
                                        .eq(MediaFileEntity::getIsOriginal, true)
                                        .eq(MediaFileEntity::getSubFileType, MediaSubFileTypeEnum.NORMAL.getType())
                        ).and(MediaTypeEnum.PIC_SHOT.getVal() == param.getMediaType(),
                                wrapper -> wrapper.eq( MediaFileEntity::getPictureType, JPEG)
                                        .eq( MediaFileEntity::getIsOriginal, false)
                                        .eq(MediaFileEntity::getSubFileType, MediaSubFileTypeEnum.NORMAL.getType())
                        ).and(MediaTypeEnum.PIC_PANORAMA.getVal() == param.getMediaType(),
                                wrapper -> wrapper.eq( MediaFileEntity::getPictureType, JPEG)
                                        .eq(MediaFileEntity::getSubFileType, MediaSubFileTypeEnum.PANORAMA.getType())
                        ).and(MediaTypeEnum.VIDEO.getVal() == param.getMediaType(),
                                wrapper -> wrapper.like( MediaFileEntity::getMediaType, VIDEO)
                        ).and(MediaTypeEnum.UNKNOWN.getVal() == param.getMediaType(),
                                wrapper -> wrapper.notLike(MediaFileEntity::getMediaType, VIDEO)
                                        .notLike(MediaFileEntity::getMediaType, IMAGE)
                        )
                        .orderByDesc(MediaFileEntity::getCreateTime));
        List<MediaFileDTO> records = pageData.getRecords()
                .stream()
                .map(this::entityConvertToDto)
                .collect(Collectors.toList());

        return new PaginationData<MediaFileDTO>(records, new Pagination(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()));
    }

    @Override
    public URL getObjectUrl(String workspaceId, String fileId) {
        Optional<MediaFileEntity> mediaFileOpt = getMediaByFileId(workspaceId, fileId);
        if (mediaFileOpt.isEmpty()) {
            throw new IllegalArgumentException("{} doesn't exist.");
        }

        return ossService.getObjectUrl(OssConfiguration.bucket, mediaFileOpt.get().getObjectKey());
    }

    @Override
    public List<MediaFileDTO> getFilesByWorkspaceAndJobId(String workspaceId, String jobId) {
        return mapper.selectList(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getWorkspaceId, workspaceId)
                .eq(MediaFileEntity::getJobId, jobId))
                .stream()
                .map(this::entityConvertToDto).collect(Collectors.toList());
    }

    @Override
    public PaginationData<MediaFileDTO> getMediaFilesPaginationByDirId(String dirId, long page, long pageSize,MediaFileQueryParam param) {
        Page<MediaFileEntity> pageData = mapper.selectPage(
                new Page<MediaFileEntity>(page, pageSize),
                new LambdaQueryWrapper<MediaFileEntity>()
                        .eq(MediaFileEntity::getDirId, dirId)
                        .ge(param != null && param.getBeginTime() != null, MediaFileEntity::getCreateTime,param.getBeginTime())
                        .le(param != null && param.getEndTime() != null, MediaFileEntity::getCreateTime,param.getEndTime())
                        .and(StringUtils.hasText(param.getSearchInfo()),
                                wrapper -> wrapper.like( MediaFileEntity::getFileName, param.getSearchInfo())
                                        .or().like( MediaFileEntity::getPayload, param.getSearchInfo())
                        )
                        .orderByDesc(MediaFileEntity::getId));
        List<MediaFileDTO> records = pageData.getRecords()
                .stream()
                .map(this::entityConvertToDto)
                .collect(Collectors.toList());
        if (param.getMediaType() != null) {
            records = records.stream().filter(mediaFileDTO -> param.getMediaType().equals(mediaFileDTO.getMediaType())).collect(Collectors.toList());
        }

        MediaDirDTO mediaDirDTO = mediaDirService.getMediaDirById(null,String.valueOf(dirId));
        return new PaginationData<MediaFileDTO>(mediaDirDTO.getDirName(), records, new Pagination(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()));
    }

    @Override
    public List<MediaFileDTO> getMediaFilesByDirId(String dirId) {
        List<MediaFileEntity> listData = mapper.selectList(
                new LambdaQueryWrapper<MediaFileEntity>()
                        .eq(MediaFileEntity::getDirId, dirId)
                        .orderByDesc(MediaFileEntity::getId));
        List<MediaFileDTO> records = listData
                .stream()
                .map(this::entityConvertToDto)
                .collect(Collectors.toList());

        return records;
    }

    @Override
    public List<MediaFileEntity> getFlightTrackMediaFiles(String workspaceId, String droneSn, long startTime, long endTime) {
        List<MediaFileEntity> listData = mapper.selectList(
                new LambdaQueryWrapper<MediaFileEntity>()
                        .select(MediaFileEntity::getWorkspaceId,
                                MediaFileEntity::getDrone,
                                MediaFileEntity::getLongitude,
                                MediaFileEntity::getLatitude,
                                MediaFileEntity::getAbsoluteAltitude,
                                MediaFileEntity::getPictureTime)
                        .eq(MediaFileEntity::getWorkspaceId, workspaceId)
                        .eq(MediaFileEntity::getDrone, droneSn)
                        .ge(MediaFileEntity::getPictureTime,startTime)
                        .le(MediaFileEntity::getPictureTime,endTime)
                        .ne(MediaFileEntity::getLongitude,0)
                        .orderByAsc(MediaFileEntity::getPictureTime));

        return listData;
    }

    /**
     * Convert the received file object into a database entity object.
     * @param file
     * @return
     */
    private MediaFileEntity fileUploadConvertToEntity(String workspaceId, MediaUploadCallbackRequest file) {
        //通过path 获取航线名称，文件夹名称
        String waylineName = getWaylineName(workspaceId,file.getExt().getFileGroupId());
//        String dirName = getDirName(waylineName, file.getPath());
        String dirName = file.getPath();
        MediaDirDTO dirDto = null;
        synchronized(dirName.intern()) {
            dirDto = mediaDirService.getMediaDirByName(workspaceId, dirName, null);
            //文件夹不存在，则创建文件夹
            if (dirDto == null) {
                Optional<GetWaylineListResponse> waylineOpt = waylineFileService.getWaylineByWaylineName(workspaceId, waylineName);
                MediaDirEntity.MediaDirEntityBuilder dirBuilder = MediaDirEntity.builder();
                dirBuilder.dirName(dirName)
                        .workspaceId(workspaceId)
                        .waylineName(waylineName)
                        .createTime(System.currentTimeMillis())
                        .deviceName(getDeviceNameByModelKey(file.getExt().getDroneModelKey()))
                        .payload(getDeviceNameByModelKey(file.getExt().getPayloadModelKey()))
                        .username(UserRequest.getCurrentUser().getUsername());
                if (waylineOpt.isPresent()) {
                    waylineOpt.ifPresent(wayline -> dirBuilder.templateType(wayline.getTemplateTypes().get(0).name()));
                }
                dirDto = mediaDirService.createDir(dirBuilder.build());
            }


            MediaFileEntity.MediaFileEntityBuilder builder = MediaFileEntity.builder();

            if (file != null) {


                //经纬度转换
                //AMap aMap = CoordinateUtil.transform(file.getMetadata().getShootPosition().getLng(),file.getMetadata().getShootPosition().getLat());
                //AMap aMap = CoordinateUtil.transform(exif.getLongitude(),exif.getLatitude());
                builder.fileName(file.getName())
                        .filePath(file.getPath())
                        .fingerprint(file.getFingerprint())
                        .objectKey(file.getObjectKey())
                        .subFileType(Optional.ofNullable(file.getSubFileType()).map(MediaSubFileTypeEnum::getType).orElse(null))
                        .isOriginal(file.getExt().getOriginal())
                        .jobId(file.getExt().getFileGroupId())
                        .drone(file.getExt().getSn())
                        .tinnyFingerprint(file.getExt().getTinnyFingerprint())
                        .payload(getDeviceNameByModelKey(file.getExt().getPayloadModelKey()))
                        .dirId(dirDto.getId())
                        .waylineName(waylineName)
//                        .imageWidth(exif.getImageWidth())
//                        .imageHeight(exif.getImageHeight())
//                        .xResolution(exif.getXResolution())
//                        .yResolution(exif.getYResolution())
//                        .latitude(BigDecimal.valueOf(CoordinateUtil.checkValidVal(exif.getLatitude())).setScale(14, RoundingMode.DOWN))
//                        .longitude(BigDecimal.valueOf(CoordinateUtil.checkValidVal(exif.getLongitude())).setScale(14, RoundingMode.DOWN))
                        .absoluteAltitude(BigDecimal.valueOf(CoordinateUtil.checkValidVal(file.getMetadata().getAbsoluteAltitude())).setScale(6, RoundingMode.DOWN))
                        .relativeAltitude(BigDecimal.valueOf(CoordinateUtil.checkValidVal(file.getMetadata().getRelativeAltitude())).setScale(6, RoundingMode.DOWN))
                        .size(ossService.getObjectSize(OssConfiguration.bucket, file.getObjectKey()))
                        .pictureTime(file.getMetadata().getCreatedTime() != null ? file.getMetadata().getCreatedTime().atZone(ZoneId.of("UTC+0")).toInstant().toEpochMilli() : null)
//                        .longitudeRef(exif.getLongitudeRef())
//                        .latitudeRef(exif.getLatitudeRef())
//                        .mediaType(exif.getPictureType())
//                        .pictureType(exif.getPictureType())
//                        .durationSeconds(exif.getDurationSeconds())
                        .taskName(dirDto.getDirName())//先取远程计划任务名称，没有就取文件夹名称
                ;
            }
            return builder.build();
        }
    }

    private String getDeviceNameByModelKey(DeviceEnum modelKey) {
        Optional<DeviceDictionaryDTO> payloadDict = deviceDictionaryService
                .getOneDictionaryInfoByTypeSubType(modelKey.getDomain().getDomain(),
                        modelKey.getType().getType(), modelKey.getSubType().getSubType());
        if(payloadDict.isPresent()) {
            return payloadDict.get().getDeviceName();
        }
        return  "";
    }

    private String getWaylineName(String workspaceId,  String jobId) {
        if(StringUtils.hasText(jobId)) {
            Optional<WaylineJobEntity> wlJobOpt = getJobByJobId(workspaceId,jobId);
            if(wlJobOpt.isPresent()) {
                Optional<GetWaylineListResponse> wlOpt = waylineFileService.getWaylineByWaylineId(workspaceId, wlJobOpt.get().getFileId());
                if(wlOpt.isPresent()) {
                    return wlOpt.get().getName();
                }
            }
        }
        return "N/A";
//        if(StringUtils.hasText(path)) {
//            String[] pathSplit = path.split("-");
//            if(pathSplit.length > 1) {
//                return pathSplit[pathSplit.length - 1];
//            }
//        }
//        return "";
    }

    public Optional<WaylineJobEntity> getJobByJobId(String workspaceId, String jobId) {
        WaylineJobEntity jobEntity = waylineJobMapper.selectOne(
                new LambdaQueryWrapper<WaylineJobEntity>()
                        .eq(WaylineJobEntity::getWorkspaceId, workspaceId)
                        .eq(WaylineJobEntity::getJobId, jobId));
        return Optional.ofNullable(jobEntity);
    }

    //path:DJI_202406241424_027_项目编号0001-新建面状航线2
    private String getDirName(String waylineName,String path) {
        if(StringUtils.hasText(path)) {
            String[] pathSplit = path.split("_");
            String datetime = pathSplit[1];
            return (waylineName + " " + datetime.substring(0,4)+ "-"
                    + datetime.substring(4,6)+ "-"
                    + datetime.substring(6,8)+ "-"
                    + " "
                    + datetime.substring(8,10)+ ":"
                    + datetime.substring(10,12)).trim();
        }
        return "temp";
    }

    /**
     * Convert database entity objects into file data transfer object.
     * @param entity
     * @return
     */
    private MediaFileDTO entityConvertToDto(MediaFileEntity entity) {
        MediaFileDTO.MediaFileDTOBuilder builder = MediaFileDTO.builder();

        if (entity != null) {
            String taskName = entity.getTaskName();
            if(!StringUtils.hasText(taskName)) {
                MediaDirDTO mediaDirDTO = mediaDirService.getMediaDirById(null,String.valueOf(entity.getDirId()));
                taskName = mediaDirDTO.getDirName();
            }
            MediaTypeEnum mediaTypeEnum = convertMediaType(entity);
            String url = ossService.getObjectUrl(OssConfiguration.bucket, entity.getObjectKey()).toString();
            String thumbnailURL = getThumbnailURL(entity);
            builder.fileName(entity.getFileName())
                    .fileId(entity.getFileId())
                    .filePath(entity.getFilePath())
                    .isOriginal(entity.getIsOriginal())
                    .fingerprint(entity.getFingerprint())
                    .objectKey(entity.getObjectKey())
                    .tinnyFingerprint(entity.getTinnyFingerprint())
                    .payload(entity.getPayload())
                    .createTime(LocalDateTime.ofInstant(
                            Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()))
                    .drone(entity.getDrone())
                    .jobId(entity.getJobId())
                    .dirId(entity.getDirId())
                    .waylineName(entity.getWaylineName())
                    .size(entity.getSize())
                    .longitude(entity.getLongitude())
                    .latitude(entity.getLatitude())
                    .imageWidth(entity.getImageWidth())
                    .imageHeight(entity.getImageHeight())
                    .absoluteAltitude(entity.getAbsoluteAltitude())
                    .gimbalYawDegree(entity.getGimbalYawDegree())
                    .relativeAltitude(entity.getRelativeAltitude())
                    .taskName(taskName)
                    .sourceFrom(entity.getSourceFrom())
                    .pictureTime(LocalDateTime.ofInstant(
                            Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()))
                    .xResolution(entity.getXResolution())
                    .yResolution(entity.getYResolution())
                    .url(url)
                    .thumbnailUrl(null == thumbnailURL ? url : thumbnailURL)
                    .longitudeRef(entity.getLongitudeRef())
                    .latitudeRef(entity.getLatitudeRef())
                    .mediaType(mediaTypeEnum.getVal())
                    .mediaTypeText(mediaTypeEnum.getText())
                    .pictureType(entity.getPictureType())
                    .durationSeconds(entity.getDurationSeconds())
                    .elementId(entity.getElementId());

        }

        return builder.build();
    }

    private String getThumbnailURL(MediaFileEntity entity) {
        String originFileName = entity.getFileName();
        //缩略图路径
        String tObjectKey = getThumbnailObjectKey(originFileName);
        return ossService.getObjectUrl(OssConfiguration.bucket, tObjectKey).toString();

    }

    private boolean putThumbnailImage(String tObjectKey, String originObjectKey,boolean toCreate) {
        //生成缩略图
        if(!ossService.objectExists(OssConfiguration.bucket,tObjectKey)) {
            if(toCreate) {
                System.out.println("======生成缩略图开始======：" + originObjectKey);
                URL url = ossService.getObjectUrl(OssConfiguration.bucket, originObjectKey);
                BufferedImage bufferedImage = ThumbnailatorUtil.getThumbnail(originObjectKey, url,100, 100);
                if (bufferedImage != null) {
                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                    try {
                        ImageIO.write(bufferedImage, JPEG, outputStream);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    ossService.putObject(OssConfiguration.bucket, tObjectKey, new ByteArrayInputStream(outputStream.toByteArray()));
                }
                System.out.println("======生成缩略图结束======：" + originObjectKey);
            } else {
                return false;
            }
        }
        return true;
    }

    private String getThumbnailObjectKey(String originFileName) {
        String[] nameSplit = originFileName.split(SPLIT_DOT);
        //缩略图路径
        return T_PATH + nameSplit[0] + TAIL + PicExifUtil.JPG;
    }

    private MediaTypeEnum convertMediaType(MediaFileEntity entity) {
        if(StringUtils.hasText(entity.getMediaType())) {
            if (JPEG.equalsIgnoreCase(entity.getPictureType()) && MediaSubFileTypeEnum.NORMAL.getType() == entity.getSubFileType()) {
                if (entity.getIsOriginal()) {
                    return MediaTypeEnum.PIC_ORIGIN;
                } else {
                    return MediaTypeEnum.PIC_SHOT;
                }
            } else if (entity.getMediaType().startsWith(VIDEO) || MP4.equalsIgnoreCase(entity.getPictureType())) {
                return MediaTypeEnum.VIDEO;
            } else if (JPEG.equalsIgnoreCase(entity.getPictureType()) && MediaSubFileTypeEnum.PANORAMA.getType() == entity.getSubFileType()) {
                return MediaTypeEnum.PIC_PANORAMA;
            }
        }
        return MediaTypeEnum.UNKNOWN;
    }

    @Override
    public void deleteFiles(List<String> fileIds) {
        List<MediaFileEntity> listData = mapper.selectList(
                new LambdaQueryWrapper<MediaFileEntity>()
                        .in(MediaFileEntity::getFileId, fileIds));
        for(MediaFileEntity entity : listData) {
            try {
                mapper.delete(new LambdaQueryWrapper<MediaFileEntity>().eq(MediaFileEntity::getFileId, entity.getFileId()));
                ossService.deleteObject(OssConfiguration.bucket, entity.getObjectKey());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 下载多个文件夹转zip压缩包
     *
     * @param workspaceId
     * @param dirIds
     * @param response
     * @throws Exception
     */
    @Override
    public void dowloadDirsToZip(String workspaceId, List<String> dirIds, HttpServletResponse response) throws Exception {
        dowloadToZip(workspaceId,dirIds,response);
    }

    /**
     * 下载多个文件转zip压缩包
     *
     * @param workspaceId
     * @param fileIds
     * @param response
     * @throws Exception
     */
    @Override
    public void dowloadFilesToZip(String workspaceId, List<String> fileIds, HttpServletResponse response) throws Exception {
        try {
            //被压缩文件名称
            List<String> srcFileNames = new ArrayList<String>();
            //被压缩文件流集合
            List<InputStream> srcFiles = new ArrayList<InputStream>();

            List<MediaFileEntity> listData = mapper.selectList(
                    new LambdaQueryWrapper<MediaFileEntity>()
                            .in(MediaFileEntity::getFileId, fileIds)
                            .orderByDesc(MediaFileEntity::getId));

            //如果有附件 进行zip处理
            String dirName = "";
            if (listData != null && listData.size() > 0) {
                MediaDirDTO mediaDirDTO = mediaDirService.getMediaDirById(workspaceId,String.valueOf(listData.get(0).getDirId()));
                dirName = mediaDirDTO.getDirName();
                for (MediaFileEntity entity : listData) {
                    //以下代码为获取图片inputStream
                    InputStream ins = ossService.getObject(OssConfiguration.bucket, entity.getObjectKey());
                    if (ins == null) {
                        continue;
                    }
                    //塞入流数组中
                    srcFiles.add(ins);
                    srcFileNames.add(dirName + "/" + entity.getFileName());
                }
            }
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(dirName + ".zip", "UTF-8"));
            //多个文件压缩成压缩包返回
            ZipUtil.zip(response.getOutputStream(), srcFileNames.toArray(new String[srcFileNames.size()]), srcFiles.toArray(new InputStream[srcFiles.size()]));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 下载多个文件转zip压缩包
     * @param workspaceId
     * @param dirIds
     * @param response
     * @throws Exception
     */
    private void dowloadToZip(String workspaceId,List<String> dirIds, HttpServletResponse response) throws Exception {
        try {
            //被压缩文件名称
            List<String> srcFileNames = new ArrayList<String>();
            //被压缩文件流集合
            List<InputStream> srcFiles = new ArrayList<InputStream>();
            for(String dirId : dirIds) {
                if(StringUtils.isEmpty(dirId)) {
                    continue;
                }
                MediaDirDTO mediaDirDTO = mediaDirService.getMediaDirById(workspaceId, dirId);
                List<MediaFileEntity> listData = mapper.selectList(
                        new LambdaQueryWrapper<MediaFileEntity>()
                                .eq(MediaFileEntity::getDirId, dirId)
                                .orderByDesc(MediaFileEntity::getId));
                //如果有附件 进行zip处理
                if (listData != null && listData.size() > 0) {
                    for (MediaFileEntity entity : listData) {
                        //以下代码为获取图片inputStream
                        InputStream ins = ossService.getObject(OssConfiguration.bucket, entity.getObjectKey());
                        if (ins == null) {
                            continue;
                        }
                        //塞入流数组中
                        srcFiles.add(ins);
                        srcFileNames.add(mediaDirDTO.getDirName() + "/" + entity.getFileName());
                    }
                }
            }
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("下载.zip", "UTF-8"));
            //多个文件压缩成压缩包返回
            ZipUtil.zip(response.getOutputStream(), srcFileNames.toArray(new String[srcFileNames.size()]), srcFiles.toArray(new InputStream[srcFiles.size()]));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Map<Integer, Long> getParentDirSize(List<Integer> dirIds) {
        return sumMapper.selectList( new LambdaQueryWrapper<MediaFileSumEntity>()
                .select(MediaFileSumEntity::getDirId,MediaFileSumEntity::getSumSize)
                .in(MediaFileSumEntity::getDirId, dirIds)
                .groupBy(MediaFileSumEntity::getDirId)
                ).stream()
                .collect(Collectors.toMap(a -> a.getDirId(), MediaFileSumEntity::getSumSize));
    }

    @Override
    public Boolean updateFile(MediaFileDTO mediaFileDTO) {
        int update = mapper.update(this.dtoToEntity(mediaFileDTO),
                new LambdaUpdateWrapper<MediaFileEntity>().eq(MediaFileEntity::getFileId, mediaFileDTO.getFileId()));
        return update > 0;
    }

    @Override
    public Boolean updateElementId(MediaFileDTO mediaFileDTO) {
        int update = mapper.update(null,
                new LambdaUpdateWrapper<MediaFileEntity>().set(MediaFileEntity::getElementId,mediaFileDTO.getElementId())
                        .eq(MediaFileEntity::getFileId, mediaFileDTO.getFileId()));
        return update > 0;
    }

    @Override
    public boolean createMediaElement(String workspaceId, Integer elementFrom, MediaFileDTO mediaFileDTO) {
        Optional<GetMapElementsResponse> groupOpt = groupService.getGroupByWorkspaceId(workspaceId,GroupTypeEnum.PICTURE);
        if (groupOpt.isEmpty()) {
            throw new RuntimeException("图片标注文件夹不存在");
        }
        //保存图片地图元素
        String elementId = UUID.randomUUID().toString();
        GroupElementEntity groupElementEntity = GroupElementEntity.builder()
                .groupId(groupOpt.get().getId())
                .elementId(elementId)
                .elementName(mediaFileDTO.getFileName())
                .username(UserRequest.getCurrentUser().getUsername())
                .elementType(ElementResourceTypeEnum.MEDIA.getType())
                .elementFrom(elementFrom)
                .build();
        ElementCoordinate elementCoordinate = new ElementCoordinate();
        elementCoordinate.setAltitude(mediaFileDTO.getRelativeAltitude().doubleValue());
        elementCoordinate.setLatitude(mediaFileDTO.getLatitude().doubleValue());
        elementCoordinate.setLongitude(mediaFileDTO.getLongitude().doubleValue());
        boolean isSuccess = groupElementService.saveElement(groupOpt.get().getId(),elementFrom,groupElementEntity,elementCoordinate);
        //更新图片中的elementId
        if(isSuccess) {
            mediaFileDTO.setElementId(elementId);
            return updateFile(mediaFileDTO);
        };
        return false;
    }

    @Override
    public boolean deleteMediaElement(String workspaceId, MediaFileDTO mediaFileDTO) {
        if(!StringUtils.hasText(mediaFileDTO.getElementId())) {
            return false;
        }
        boolean isSucc = groupElementService.deleteElement(mediaFileDTO.getElementId());
        if(isSucc) {
            mediaFileDTO.setElementId(null);
            return updateElementId(mediaFileDTO);
        }
        return false;
    }


    private MediaFileEntity dtoToEntity(MediaFileDTO mediaFileDTO) {
        MediaFileEntity.MediaFileEntityBuilder builder = MediaFileEntity.builder();

        if (mediaFileDTO != null) {
            builder.fileName(mediaFileDTO.getFileName())
                    .fileId(mediaFileDTO.getFileId())
                    .filePath(mediaFileDTO.getFilePath())
                    .isOriginal(mediaFileDTO.getIsOriginal())
                    .fingerprint(mediaFileDTO.getFingerprint())
                    .objectKey(mediaFileDTO.getObjectKey())
                    .tinnyFingerprint(mediaFileDTO.getTinnyFingerprint())
                    .payload(mediaFileDTO.getPayload())
                    .createTime(mediaFileDTO.getCreateTime() != null ?
                            mediaFileDTO.getCreateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null)
                    .drone(mediaFileDTO.getDrone())
                    .jobId(mediaFileDTO.getJobId())
                    .dirId(mediaFileDTO.getDirId())
                    .waylineName(mediaFileDTO.getWaylineName())
                    .size(mediaFileDTO.getSize())
                    .longitude(mediaFileDTO.getLongitude())
                    .latitude(mediaFileDTO.getLatitude())
                    .imageWidth(mediaFileDTO.getImageWidth())
                    .imageHeight(mediaFileDTO.getImageHeight())
                    .absoluteAltitude(mediaFileDTO.getAbsoluteAltitude())
                    .gimbalYawDegree(mediaFileDTO.getGimbalYawDegree())
                    .relativeAltitude(mediaFileDTO.getRelativeAltitude())
                    .taskName(mediaFileDTO.getTaskName())
                    .sourceFrom(mediaFileDTO.getSourceFrom())
                    .pictureTime(mediaFileDTO.getPictureTime() != null ?
                            mediaFileDTO.getPictureTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null)
                    .xResolution(mediaFileDTO.getXResolution())
                    .yResolution(mediaFileDTO.getYResolution())
                    .longitudeRef(mediaFileDTO.getLongitudeRef())
                    .latitudeRef(mediaFileDTO.getLatitudeRef())
                    .pictureType(mediaFileDTO.getPictureType())
                    .durationSeconds(mediaFileDTO.getDurationSeconds())
                    .elementId(mediaFileDTO.getElementId());
        }

        return builder.build();
    }

    @Override
    public PaginationData<UpstreamMediaFileDTO> getMediaFilesPagination(String workspaceName, Long startTime, Long endTime, long page, long pageSize,String workspaceId) {
        List<UpstreamMediaFileDTO> respData = mapper.getMediaFiles(startTime,endTime,workspaceName, page-1,  pageSize,workspaceId);
        long total = mapper.getMediaFilesCount(startTime,endTime,workspaceName,workspaceId);
        return new PaginationData<UpstreamMediaFileDTO>(respData, new Pagination(page, pageSize, total));
    }

    @Override
    public UploadResultDTO uploadFile(MultipartFile file,String creator) {
        try (InputStream is = file.getInputStream()) {
            UploadResultDTO dto = new UploadResultDTO();
            String objectKey = OssConfiguration.objectDirPrefix + File.separator + file.getOriginalFilename();
            ossService.putObject(OssConfiguration.bucket, objectKey, file.getInputStream());
            log.info("上传成功. {}", file.getOriginalFilename());
            String url = ossService.getObjectUrl(OssConfiguration.bucket, objectKey).toString();
            OssFileEntity ossFileEntity = new OssFileEntity();
            ossFileEntity.setUrl(url);
            ossFileEntity.setObjectKey(objectKey);
            ossFileEntity.setUsername(creator);
            ossFileMapper.insert(ossFileEntity);
            dto.setUrl(url);
            dto.setObjectKey(objectKey);
            dto.setId(ossFileEntity.getId());
            return dto;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public void deleteByDirId(String dirId) {
        mediaDirService.deleteDir(dirId);
        QueryWrapper<MediaFileEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(MediaFileEntity::getDirId,dirId);
        List<MediaFileEntity> list = mapper.selectList(wrapper);
        for(MediaFileEntity entity : list) {
            deleteFileById(String.valueOf(entity.getId()));
        }
    }

    @Override
    public void deleteFileById(String id) {
        List<MediaFileEntity> listData = mapper.selectList(
                new LambdaQueryWrapper<MediaFileEntity>()
                        .eq(MediaFileEntity::getId, id));
        for(MediaFileEntity entity : listData) {
            try {
                mapper.deleteById(id);
                ossService.deleteObject(OssConfiguration.bucket, entity.getObjectKey());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}
