//    Copyright (C) 2016  Jonathan Jason Dennis [Meticulus]
//    theonejohnnyd@gmail.com
//
//    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.
//
//    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 <http://www.gnu.org/licenses/>.
//
﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using meticulus.global;

namespace meticulus.plugin
{
    public class PluginManager
    {
        string _directory = "";
        Type _type;
        List<object> _plugins = new List<object>();
        string _pluginext;
		private static String tag;
        public object[] Plugins
        {
            get { return _plugins.ToArray(); }
        }
        public PluginManager()
        {
			tag = this.GetType ().Name;
        }
        public void LoadPlugins(string directory_relative, Type type,string fileext)
        {
			// UNDONE: Do we want to ensure that the plugins are clear?
            //_plugins.Clear();
			// TODO: This seems to do nothing!? Was there a reason for this?
            //_directory = Path.Combine("/etc", directory_relative);

			_directory = directory_relative;
            _type = type;
            _pluginext = fileext;

            string[] dlls = Directory.GetFiles(_directory, "*"+_pluginext);
            foreach (string dll in dlls)
            {
                Assembly Plug = AppDomain.CurrentDomain.Load(File.ReadAllBytes(dll));
            	LoadPluginsWithInterface(Plug, _type);
            }
        }


        public void LoadPluginsWithInterface(Assembly Plug, Type interfacetype)
        {
			// UNDONE: Do we want to ensure that the plugins are clear?
			//_plugins.Clear();
            _type = interfacetype;
			try {
	            Type[] types = Plug.GetExportedTypes();
	            foreach (Type type in types)
	            {
	                Type[] interfaces = type.GetInterfaces();
	                foreach (Type interfac in interfaces)
	                {
						/* Don't try to create instances of pure interfaces */
						if(type.Attributes.HasFlag(TypeAttributes.Interface))
							continue;
	                    if (interfac == _type)
	                    {
	                        try
	                        {
	                            _plugins.Add(AppDomain.CurrentDomain.CreateInstanceAndUnwrap(Plug.FullName,type.FullName));
	                        }
	                        catch(Exception ex) 
	                        {
								log.e(tag, String.Format("Error loading plugin {0} of interface type {1} in Assembly {2}", type, interfacetype, Plug));
								log.e(tag, ex);
	                        }

	                    }
	                }
	            }
			} catch (Exception ex) {
				log.e (tag, ex);
			}
        }
        public void LoadPluginsofType(Assembly Plug, Type t)
        {
            _plugins.Clear();
            _type = t;
            Type[] types = Plug.GetExportedTypes();
            foreach (Type type in types)
            {
                if (t.IsAssignableFrom(type) & t != type)
                {
                    try
                    {
                        _plugins.Add(AppDomain.CurrentDomain.CreateInstanceAndUnwrap(Plug.FullName,type.FullName));
                    }
                    catch (Exception ex)
                    {
						log.e(tag, String.Format("Error loading plugin {0} in Assembly {1}", type, Plug));
						log.e(tag, ex);
                    }
                }
            }
        }
    }
}
