using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace InABox.Core
{
    public delegate void ImportNotificationEvent(object sender, string message);

    public delegate void ImportLogEvent(object sender, string message);

    public delegate bool ImportPreProcessEvent(object sender, Dictionary<string, string> values);

    public delegate bool ImportPostProcessEvent(object sender, object entity, Dictionary<string, string> values);

    public delegate void ImportSaveEvent(object sender, object entity);

    public delegate CoreTable ImportLoadEvent(object sender, Type type, String[] fields, String ID);
    
    public interface IImporter
    {
        string[] Properties { get; }

        /// <summary>
        /// Represents the fields in the header of the imported file, and therefore what fields <see cref="Mappings"/> can map from.
        /// These should be populated by <see cref="ReadHeader"/>.
        /// </summary>
        string[] Fields { get; }

        bool HasHeader { get; set; }

        int HeaderRow { get; set; }

        /// <summary>
        /// While <see cref="Fields"/> represents the fields that this importer recognises, <see cref="Mappings"/> represents how the properties of
        /// the imported object are populated, so while there may be a one to one mapping from <see cref="Fields"/>, this does not have to be the case.
        /// </summary>
        /// <remarks>
        /// These mappings must be initialised to get any data from <see cref="Import"/>.
        /// </remarks>
        List<ImportMapping> Mappings { get; }

        IEnumerable<string> Log { get; }
        event ImportNotificationEvent OnNotify;
        event ImportLogEvent OnError;
        event ImportLogEvent OnLog;

        event ImportPreProcessEvent BeforeProcess;
        event ImportPostProcessEvent AfterProcess;
        event ImportSaveEvent OnSave;
        event ImportLoadEvent OnLoad;

        bool Open(Stream stream);

        void Close();

        /// <summary>
        /// Import the data from the stream specified when <see cref="Open(Stream)"/> was called, using <see cref="OnSave"/> to for each line.
        /// </summary>
        /// <remarks>
        /// This method <i>must</i> be called after <see cref="Open(Stream)"/> and <see cref="ReadHeader"/>.
        /// 
        /// One should also have set <see cref="OnSave"/> and <see cref="OnLoad"/>.
        /// </remarks>
        /// <returns>The number of items imported.</returns>
        int Import();

        /// <summary>
        /// Move to the next line in the import.
        /// </summary>
        /// <remarks>
        /// While not all importers will need to do anything with this method, it <i>must</i> be called before <see cref="ReadLine"/>.
        /// </remarks>
        /// <returns><see langword="true"/> if there is still data to be read.</returns>
        bool MoveNext();

        /// <summary>
        /// Read the header of the file, populating <see cref="Fields"/>.
        /// </summary>
        /// <remarks>
        /// This is called regardless of whether <see cref="HasHeader"/> is <see langword="true"/>.
        /// </remarks>
        /// <returns><see langword="true"/> if the header was read successfully.</returns>
        bool ReadHeader();

        /// <summary>
        /// Read the values of a line.
        /// </summary>
        /// <remarks>
        /// The keys of the results are those present in <see cref="Fields"/>.
        /// </remarks>
        /// <returns>
        /// A dictionary of values, where the keys are given by <see cref="Fields"/>.
        /// </returns>
        Dictionary<string, string> ReadLine();
    }

}