// Copyright Dirk Lemstra https://github.com/dlemstra/Magick.NET.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Collections.Generic;
using System.Globalization;

namespace ImageMagick.Formats;

/// <summary>
/// Class for defines that are used when a <see cref="MagickFormat.Pdf"/> image is written.
/// </summary>
public sealed class PdfWriteDefines : IWriteDefines
{
    private static readonly List<MagickFormat> AllowedFormats = [MagickFormat.Pdf, MagickFormat.Pdfa];

    private readonly MagickFormat _format;

    /// <summary>
    /// Initializes a new instance of the <see cref="PdfWriteDefines"/> class.
    /// </summary>
    /// <param name="format">The pdf format.</param>
    public PdfWriteDefines(MagickFormat format = MagickFormat.Pdf)
        => _format = CheckFormat(format);

    /// <summary>
    /// Gets or sets the author of the pdf document (pdf:author).
    /// </summary>
    public string? Author { get; set; }

    /// <summary>
    /// Gets or sets the creation time of the pdf document (pdf:create-epoch).
    /// </summary>
    public DateTime? CreationTime { get; set; }

    /// <summary>
    /// Gets or sets the creator of the pdf document (pdf:creator).
    /// </summary>
    public string? Creator { get; set; }

    /// <summary>
    /// Gets the format where the defines are for.
    /// </summary>
    public MagickFormat Format
        => _format;

    /// <summary>
    /// Gets or sets the keywords of the pdf document (pdf:keywords).
    /// </summary>
    public string? Keywords { get; set; }

    /// <summary>
    /// Gets or sets the modification time of the pdf document (pdf:modify-epoch).
    /// </summary>
    public DateTime? ModificationTime { get; set; }

    /// <summary>
    /// Gets or sets a value indicating whether an identifier should be written (pdf:no-identifier).
    /// </summary>
    public bool? NoIdentifier { get; set; }

    /// <summary>
    /// Gets or sets the producer of the pdf document (pdf:producer).
    /// </summary>
    public string? Producer { get; set; }

    /// <summary>
    /// Gets or sets the subject of the pdf document (pdf:subject).
    /// </summary>
    public string? Subject { get; set; }

    /// <summary>
    /// Gets or sets a value indicating whether a thumbnail should be added to the pdf document (pdf:thumbnail).
    /// </summary>
    public bool? Thumbnail { get; set; }

    /// <summary>
    /// Gets or sets the title of the pdf document (pdf:title).
    /// </summary>
    public string? Title { get; set; }

    /// <summary>
    /// Gets or sets the version of the pdf document, for example 1.4 or 1.7 (pdf:version).
    /// </summary>
    public double? Version { get; set; }

    /// <summary>
    /// Gets the defines that should be set as a define on an image.
    /// </summary>
    public IEnumerable<IDefine> Defines
    {
        get
        {
            if (Author?.Length > 0)
                yield return new MagickDefine(MagickFormat.Pdf, "author", Author);

            if (CreationTime is not null)
                yield return new MagickDefine(MagickFormat.Pdf, "create-epoch", ToUnixTimeSeconds(CreationTime.Value));

            if (Creator?.Length > 0)
                yield return new MagickDefine(MagickFormat.Pdf, "creator", Creator);

            if (Keywords?.Length > 0)
                yield return new MagickDefine(MagickFormat.Pdf, "keywords", Keywords);

            if (ModificationTime is not null)
                yield return new MagickDefine(MagickFormat.Pdf, "modify-epoch", ToUnixTimeSeconds(ModificationTime.Value));

            if (NoIdentifier == true)
                yield return new MagickDefine(MagickFormat.Pdf, "no-identifier", NoIdentifier.Value);

            if (Producer?.Length > 0)
                yield return new MagickDefine(MagickFormat.Pdf, "producer", Producer);

            if (Subject?.Length > 0)
                yield return new MagickDefine(MagickFormat.Pdf, "subject", Subject);

            if (Thumbnail.HasValue)
                yield return new MagickDefine(MagickFormat.Pdf, "thumbnail", Thumbnail.Value);

            if (Version.HasValue)
                yield return new MagickDefine(MagickFormat.Pdf, "version", Version.Value.ToString(".0", CultureInfo.InvariantCulture));

            if (Title?.Length > 0)
                yield return new MagickDefine(MagickFormat.Pdf, "title", Title);
        }
    }

    private static MagickFormat CheckFormat(MagickFormat format)
    {
        if (!AllowedFormats.Contains(format))
            throw new ArgumentException("The specified format is not a pdf format.", nameof(format));

        return format;
    }

    private static long ToUnixTimeSeconds(DateTime value)
    {
        var dateTimeOffset = (DateTimeOffset)value.ToUniversalTime();
        return dateTimeOffset.ToUnixTimeSeconds();
    }
}
