using DlibDotNet;
using System.Runtime.Serialization;

namespace View_by_Distance.FaceRecognitionDotNet.Models;

/// <summary>
/// Represents a feature data of face. This class cannot be inherited.
/// </summary>
[Serializable]
public sealed class FaceEncoding : DisposableObject, ISerializable
{

    #region Fields

    [NonSerialized]
    private readonly Matrix<double> _Encoding;

    #endregion

    #region Constructors

    internal FaceEncoding(Matrix<double> encoding) => _Encoding = encoding;

    private FaceEncoding(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
            throw new NullReferenceException(nameof(info));

        double[]? array = info.GetValue(nameof(_Encoding), typeof(double[])) as double[];
        int? row = (int?)info.GetValue(nameof(_Encoding.Rows), typeof(int));
        int? column = (int?)info.GetValue(nameof(_Encoding.Columns), typeof(int));
        if (row is null)
            throw new NullReferenceException(nameof(row));
        if (column is null)
            throw new NullReferenceException(nameof(column));
        _Encoding = new Matrix<double>(array, row.Value, column.Value);
    }

    #endregion

    #region Properties

    internal Matrix<double> Encoding => _Encoding;

    /// <summary>
    /// Gets the size of feature data.
    /// </summary>
    /// <exception cref="ObjectDisposedException">This object is disposed.</exception>
    public int Size
    {
        get
        {
            ThrowIfDisposed();
            return _Encoding.Size;
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Gets a feature data of face as raw format.
    /// </summary>
    /// <returns>A <see cref="double"/> array that represents a feature data.</returns>
    /// <remarks><see cref="FaceEncoding"/> class supports serialization. This method is for interoperability between FaceRecognitionotNet and dlib.</remarks>
    /// <exception cref="ObjectDisposedException">This object is disposed.</exception>
    public double[] GetRawEncoding()
    {
        ThrowIfDisposed();
        return _Encoding.ToArray();
    }

    #region Overrides 

    /// <summary>
    /// Releases all unmanaged resources.
    /// </summary>
    protected override void DisposeUnmanaged()
    {
        base.DisposeUnmanaged();
        _Encoding?.Dispose();
    }

    #endregion

    #endregion

    #region ISerializable Members

    /// <summary>
    /// Populates a <see cref="SerializationInfo"/> with the data needed to serialize the target object.
    /// </summary>
    /// <param name="info">The <see cref="SerializationInfo"/> to populate with data.</param>
    /// <param name="context">The destination (see <see cref="StreamingContext"/>) for this serialization.</param>
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue(nameof(_Encoding), _Encoding.ToArray());
        info.AddValue(nameof(_Encoding.Rows), _Encoding.Rows);
        info.AddValue(nameof(_Encoding.Columns), _Encoding.Columns);
    }

    #endregion

}