/*
 *   Copyright 2008 by Richard Dale <richard.j.dale@gmail.com>
 *   Copyright 2008 by Arno Rehn <arno@arnorehn.de>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as
 *   published by the Free Software Foundation; either version 2, 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 General Public License for more details
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

namespace PlasmaScriptengineKimono {

    using System;
    using System.IO;
    using System.Text;
    using System.Reflection;
    using System.Collections.Generic;

    using Qyoto;
    using Kimono;
    using Plasma;

    public class Applet : Plasma.AppletScript {
        private PlasmaScripting.Applet applet;
        private Assembly appletAssembly;
        private Type appletType;
        private Dictionary<QEvent.TypeOf, MethodInfo> eventHandlers = new Dictionary<QEvent.TypeOf, MethodInfo>();

        public Applet(QObject parent, List<QVariant> args) : base(parent) {}

        public override bool Init() {
            QSizeF oldSize = Applet().Size;
            QFileInfo program = new QFileInfo(MainScript());

            KMimeType mime = KMimeType.FindByFileContent(program.AbsoluteFilePath());
            try {
                if (mime.Name().StartsWith("text/")) {
                    Compiler c = new Compiler(program);
                    appletAssembly = c.GetAssembly();
                } else {
                    appletAssembly = Assembly.LoadFile(program.AbsoluteFilePath());
                }
            } catch (Exception e) {
                Console.WriteLine(e);
                Object[] parameters = new Object[2];
                parameters[0] = true;
                parameters[1] = e.ToString();
                MethodInfo method = Applet().GetType().GetMethod("SetFailedToLaunch",
                    BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(System.Boolean),  typeof(System.String) }, null);
                method.Invoke(Applet(), parameters);
                return false;
            }
            
            // the newly loaded assembly might reference other bindings that need to be initialized
            foreach (AssemblyName an in appletAssembly.GetReferencedAssemblies()) {
                Assembly a = null;
                try {
                    a = Assembly.Load(an);
                } catch (FileNotFoundException e) {
                    a = Assembly.LoadFile(Path.Combine(Path.GetDirectoryName(appletAssembly.Location), an.Name + ".dll"));
                }
                // if the binding has already been initialized (e.g. in SmokeInvocation.InitRuntime()), continue.
                if (SmokeInvocation.InitializedAssemblies.Contains(a)) continue;
                AssemblySmokeInitializer attr = (AssemblySmokeInitializer) Attribute.GetCustomAttribute(a, typeof(AssemblySmokeInitializer));
                if (attr != null) attr.CallInitSmoke();
                SmokeInvocation.InitializedAssemblies.Add(a);
            }
            
            string typeName = Camelize(Package().Metadata().PluginName()) + ".";  // namespace
            typeName += Camelize(program.CompleteBaseName());
            appletType = appletAssembly.GetType(typeName);
            if (appletType == null) {
                foreach (Type t in appletAssembly.GetTypes()) {
                    for (Type tmp = t.BaseType; tmp != null; tmp = tmp.BaseType) {
                        if (tmp == typeof(PlasmaScripting.Applet)) {
                            appletType = t;
                            break;
                        }
                    }
                    if (appletType != null) break;
                }
            }

            applet = (PlasmaScripting.Applet) Activator.CreateInstance(appletType, new object[] { this });
            applet.Init();
            if (oldSize.Width() > 10 && oldSize.Height() > 10)
                Applet().Size = oldSize;
            SetUpEventHandlers();
            return true;
        }

        public override void PaintInterface(QPainter painter, QStyleOptionGraphicsItem option, QRect contentsRect) {
            if (applet != null) applet.PaintInterface(painter, option, contentsRect);
            return;
        }

        public override void ConstraintsEvent(uint constraints) {
            if (applet != null) applet.ConstraintsEvent(constraints);
            return;
        }

        public override List<QAction> ContextualActions() {
            if (applet != null)
                return applet.ContextualActions();
            else
                return new List<QAction>();
        }

        public override void ShowConfigurationInterface() {
            if (applet != null) applet.ShowConfigurationInterface();
        }

        protected new virtual bool EventFilter(QObject o, QEvent e) {
            MethodInfo eventHandler;
            if (!eventHandlers.TryGetValue(e.type(), out eventHandler)) {
                return false;
            }

            Object[] args = new Object[1];
            args[0] = e;
            eventHandler.Invoke(applet, args);
            return true;
        }

        private void SetUpEventHandlers() {
            MethodInfo eventHandler = null;
            Assembly assembly = null;

            foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies()) {
                if (a.FullName.StartsWith("qt-dotnet"))
                    assembly = a;
            }
            if (assembly == null) {
                // shouldn't happen
                Console.WriteLine("Couldn't find qt-dotnet assembly!");
                return;
            }

            eventHandler = appletType.GetMethod("ContextMenuEvent", BindingFlags.DeclaredOnly 
                                                                    | BindingFlags.Instance 
                                                                    | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneContextMenu] = eventHandler;
            }

            eventHandler = appletType.GetMethod("DragEnterEvent", BindingFlags.DeclaredOnly 
                                                                  | BindingFlags.Instance 
                                                                  | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneDragEnter] = eventHandler;
            }

            eventHandler = appletType.GetMethod("DragLeaveEvent", BindingFlags.DeclaredOnly 
                                                                  | BindingFlags.Instance 
                                                                  | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneDragLeave] = eventHandler;
            }

            eventHandler = appletType.GetMethod("DragMoveEvent", BindingFlags.DeclaredOnly 
                                                                 | BindingFlags.Instance 
                                                                 | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneDragMove] = eventHandler;
            }

            eventHandler = appletType.GetMethod("DropEvent", BindingFlags.DeclaredOnly 
                                                             | BindingFlags.Instance 
                                                             | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneDrop] = eventHandler;
            }

            eventHandler = appletType.GetMethod("FocusInEvent", BindingFlags.DeclaredOnly 
                                                                | BindingFlags.Instance 
                                                                | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.FocusIn] = eventHandler;
            }

            eventHandler = appletType.GetMethod("FocusOutEvent", BindingFlags.DeclaredOnly 
                                                                 | BindingFlags.Instance 
                                                                 | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.FocusOut] = eventHandler;
            }

            eventHandler = appletType.GetMethod("HoverEnterEvent", BindingFlags.DeclaredOnly 
                                                                   | BindingFlags.Instance 
                                                                   | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneHoverEnter] = eventHandler;
            }

            eventHandler = appletType.GetMethod("HoverMoveEvent", BindingFlags.DeclaredOnly 
                                                                  | BindingFlags.Instance 
                                                                  | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneHoverMove] = eventHandler;
            }

            eventHandler = appletType.GetMethod("HoverLeaveEvent", BindingFlags.DeclaredOnly 
                                                                   | BindingFlags.Instance 
                                                                   | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneHoverLeave] = eventHandler;
            }

            eventHandler = appletType.GetMethod("KeyPressEvent", BindingFlags.DeclaredOnly 
                                                                 | BindingFlags.Instance 
                                                                 | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.KeyPress] = eventHandler;
            }

            eventHandler = appletType.GetMethod("KeyReleaseEvent", BindingFlags.DeclaredOnly 
                                                                   | BindingFlags.Instance 
                                                                   | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.KeyRelease] = eventHandler;
            }

            eventHandler = appletType.GetMethod("MousePressEvent", BindingFlags.DeclaredOnly 
                                                                   | BindingFlags.Instance 
                                                                   | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneMousePress] = eventHandler;
            }

            eventHandler = appletType.GetMethod("MouseMoveEvent", BindingFlags.DeclaredOnly 
                                                                  | BindingFlags.Instance 
                                                                  | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneMouseMove] = eventHandler;
            }

            eventHandler = appletType.GetMethod("MouseReleaseEvent", BindingFlags.DeclaredOnly 
                                                                     | BindingFlags.Instance 
                                                                     | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneMouseRelease] = eventHandler;
            }

            eventHandler = appletType.GetMethod("MouseDoubleClickEvent", BindingFlags.DeclaredOnly 
                                                                         | BindingFlags.Instance 
                                                                         | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneMouseDoubleClick] = eventHandler;
            }

            eventHandler = appletType.GetMethod("WheelEvent", BindingFlags.DeclaredOnly 
                                                              | BindingFlags.Instance 
                                                              | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.GraphicsSceneWheel] = eventHandler;
            }

            eventHandler = appletType.GetMethod("InputMethodEvent", BindingFlags.DeclaredOnly 
                                                                    | BindingFlags.Instance 
                                                                    | BindingFlags.NonPublic);
            if (eventHandler != null) {
                eventHandlers[QEvent.TypeOf.InputMethod] = eventHandler;
            }

            if (eventHandlers.Count > 0) {
                Applet().InstallEventFilter(this);
            }
        }

        private string Camelize(string str) {
            StringBuilder ret = new StringBuilder(str.Substring(0, 1).ToUpper());
            for (int i = 1; i < str.Length; i++) {
                if (str[i] == '_' || str[i] == '-') {
                    i++;
                    if (i < str.Length)
                        ret.Append(str.Substring(i, 1).ToUpper());
                } else {
                    ret.Append(str[i]);
                }
            }
            return ret.ToString();
        }
    }
}

// kate: space-indent on; indent-width 4; replace-tabs on; mixed-indent off;
