/*
 * Copyright (C) 2000 - 2024 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception. You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
package org.silverpeas.components.gallery.model;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.silverpeas.components.gallery.constant.GalleryResourceURIs;
import org.silverpeas.components.gallery.constant.MediaMimeType;
import org.silverpeas.components.gallery.constant.MediaResolution;
import org.silverpeas.components.gallery.process.media.GalleryLoadMetaDataProcess;
import org.silverpeas.core.date.period.Period;
import org.silverpeas.core.io.file.SilverpeasFile;
import org.silverpeas.core.io.file.SilverpeasFileProvider;
import org.silverpeas.core.io.media.video.ThumbnailPeriod;
import org.silverpeas.core.notification.message.MessageManager;
import org.silverpeas.core.util.DateUtil;
import org.silverpeas.kernel.util.StringUtil;
import org.silverpeas.core.util.URLUtil;
import org.silverpeas.kernel.logging.SilverLogger;

import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static org.silverpeas.kernel.util.StringUtil.isDefined;

/**
 * This class represents a Media that the content (the file in other words) is saved on the
 * Silverpeas workspace.
 */
public abstract class InternalMedia extends Media {
  private static final long serialVersionUID = 2070296932101933853L;

  private boolean downloadAuthorized = false;
  private String fileName;
  private long fileSize = 0;
  private MediaMimeType fileMimeType = MediaMimeType.ERROR;
  private Period downloadPeriod = Period.UNDEFINED;

  private LinkedHashMap<String, MetaData> metaData = null;

  public InternalMedia() {
    super();
  }

  protected InternalMedia(final InternalMedia other) {
    super(other);
    this.downloadAuthorized = other.downloadAuthorized;
    this.fileName = other.fileName;
    this.fileSize = other.fileSize;
    this.fileMimeType = other.fileMimeType;
    this.downloadPeriod = other.downloadPeriod;
    if (other.metaData != null) {
      this.metaData = new LinkedHashMap<>(other.metaData);
    }
  }

  /**
   * Indicates if the media is marked as downloadable. The visibility period is not taken into
   * account here.
   * @return true if marked as downloadable, false otherwise.
   */
  public boolean isDownloadAuthorized() {
    return downloadAuthorized;
  }

  /**
   * Indicates if the download is possible according the visibility information. The date of day
   * must be included into visibility period.
   * @return true if download is possible, false otherwise.
   */
  @Override
  public boolean isDownloadable() {
    Date dateOfDay = DateUtil.getDate();
    boolean isDownloadable = isDownloadAuthorized() && isVisible(dateOfDay);
    if (isDownloadable && getDownloadPeriod().isDefined()) {
      isDownloadable = getDownloadPeriod().contains(dateOfDay);
    }
    return isDownloadable;
  }

  public String getFileName() {
    return fileName;
  }

  public void setFileName(String fileName) {
    this.fileName = fileName;
  }

  public long getFileSize() {
    return fileSize;
  }

  public void setFileSize(long fileSize) {
    this.fileSize = fileSize;
  }

  public MediaMimeType getFileMimeType() {
    return fileMimeType;
  }

  public void setFileMimeType(MediaMimeType fileMimeType) {
    this.fileMimeType = fileMimeType != null ? fileMimeType : MediaMimeType.ERROR;
  }

  public void setDownloadAuthorized(boolean downloadAuthorized) {
    this.downloadAuthorized = downloadAuthorized;
  }

  public Period getDownloadPeriod() {
    return downloadPeriod;
  }

  public void setDownloadPeriod(final Period downloadPeriod) {
    this.downloadPeriod = Period.check(downloadPeriod);
  }

  @Override
  public String getApplicationThumbnailUrl(MediaResolution mediaResolution) {
    if (mediaResolution == null || mediaResolution == MediaResolution.ORIGINAL ||
        mediaResolution == MediaResolution.NORMAL) {
      mediaResolution = MediaResolution.PREVIEW;
    }
    if (getType().isPhoto()) {
      return getPhotoThumbnailUrl(mediaResolution);
    } else if (getType().isVideo()) {
      if (getVideoThumbnailUrl()) {
        return GalleryResourceURIs.buildVideoThumbnailURI(this, ThumbnailPeriod.Thumbnail0)
            .toString();
      }
      return super.getApplicationThumbnailUrl(mediaResolution);
    } else {
      return super.getApplicationThumbnailUrl(mediaResolution);
    }
  }

  private boolean getVideoThumbnailUrl() {
    SilverpeasFile thumbFile =
        SilverpeasFileProvider.getFile(FileUtils
            .getFile(Media.BASE_PATH.getPath(), getComponentInstanceId(),
                getWorkspaceSubFolderName(),
                "img0.jpg").getPath());
    return thumbFile != null && thumbFile.exists();
  }

  private String getPhotoThumbnailUrl(final MediaResolution mediaResolution) {
    if (MediaResolution.WATERMARK == mediaResolution) {
      SilverpeasFile fileWatermark = getFile(MediaResolution.WATERMARK);
      if (!fileWatermark.exists()) {
        return "";
      }
    }
    if (StringUtil.isDefined(getFileName()) && isPreviewable()) {
      return GalleryResourceURIs.buildMediaContentURI(this, mediaResolution).toString();
    } else {
      String thumbnailUrl = URLUtil.getApplicationURL() + "/gallery/jsp/icons/notAvailable_" +
          MessageManager.getLanguage() + mediaResolution.getThumbnailSuffix() + ".jpg";
      return FilenameUtils.normalize(thumbnailUrl, true);
    }
  }

  @Override
  public SilverpeasFile getFile(final MediaResolution mediaResolution, final String size) {
    if (StringUtil.isNotDefined(getFileName())) {
      return SilverpeasFile.NO_FILE;
    }
    final int maxSize = 2;
    List<String> potentialFileNames = new ArrayList<>(maxSize);
    potentialFileNames.add(getFileName());
    if (getType().isPhoto()) {
      final String thumbnailSuffix = mediaResolution.getThumbnailSuffix();
      final String originalFileExt = "." + FilenameUtils.getExtension(getFileName());
      if (mediaResolution == MediaResolution.NORMAL) {
        potentialFileNames.add(getId() + thumbnailSuffix + originalFileExt);
      } else if (StringUtil.isDefined(thumbnailSuffix)) {
        potentialFileNames.set(0, getId() + thumbnailSuffix + originalFileExt);
      }
      if (!".jpg".equalsIgnoreCase(originalFileExt)) {
        // this case is about taking in charge the old medias
        potentialFileNames.add(getId() + thumbnailSuffix + ".jpg");
      }
    }
    SilverpeasFile file = SilverpeasFile.NO_FILE;
    for (String potentialFileName : potentialFileNames) {
      File physicalFile = FileUtils
          .getFile(Media.BASE_PATH.getPath(), getComponentInstanceId(), getWorkspaceSubFolderName(),
              potentialFileName);
      if (potentialFileNames.size() > 1 && !physicalFile.exists()) {
        continue;
      }
      if (isDefined(size)) {
        physicalFile = Paths.get(physicalFile.getParentFile().getPath(), size, physicalFile.getName()).toFile();
      }
      file = SilverpeasFileProvider.getFile(physicalFile.getPath());
    }
    return file;
  }

  private Map<String, MetaData> getAllMetaData() {
    if (metaData == null) {
      metaData = new LinkedHashMap<>();
      try {
        GalleryLoadMetaDataProcess.load(this);
      } catch (Exception e) {
        SilverLogger.getLogger(this).warn(e);
      }
    }
    return metaData;
  }

  /**
   * Adds a metadata.
   * @param data a metadata.
   */
  public void addMetaData(MetaData data) {
    getAllMetaData().put(data.getProperty(), data);
  }

  /**
   * Gets a metadata according to the specified property name.
   * @param property the property name for which the metadata is requested.
   * @return the metadata if it exists, null otherwise.
   */
  public MetaData getMetaData(String property) {
    return getAllMetaData().get(property);
  }

  /**
   * Gets all metadata property names.
   * @return the list of metadata property names, empty list if no metadata.
   */
  public Collection<String> getMetaDataProperties() {
    Collection<MetaData> values = getAllMetaData().values();
    Collection<String> properties = new ArrayList<>();
    for (MetaData meta : values) {
      if (meta != null) {
        properties.add(meta.getProperty());
      }
    }
    return properties;
  }
}
