import { NavigationTransition } from "./frame";
import { Transition } from "../transition/transition";
import { SlideTransition } from "../transition/slide-transition";
import { FadeTransition } from "../transition/fade-transition";

import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories } from "../../trace";

module UIViewControllerAnimatedTransitioningMethods {
    const methodSignature = NSMethodSignature.signatureWithObjCTypes("v@:c");
    const invocation = NSInvocation.invocationWithMethodSignature(methodSignature);
    invocation.selector = "completeTransition:";

    export function completeTransition(didComplete: boolean) {
        const didCompleteReference = new interop.Reference(interop.types.bool, didComplete);
        invocation.setArgumentAtIndex(didCompleteReference, 2);
        invocation.invokeWithTarget(this);
    }
}

class AnimatedTransitioning extends NSObject implements UIViewControllerAnimatedTransitioning {
    public static ObjCProtocols = [UIViewControllerAnimatedTransitioning];

    private _transition: Transition;
    private _operation: UINavigationControllerOperation;
    private _fromVC: UIViewController;
    private _toVC: UIViewController;
    private _transitionType: string;

    public static init(transition: Transition, operation: UINavigationControllerOperation, fromVC: UIViewController, toVC: UIViewController): AnimatedTransitioning {
        const impl = <AnimatedTransitioning>AnimatedTransitioning.new();
        impl._transition = transition;
        impl._operation = operation;
        impl._fromVC = fromVC;
        impl._toVC = toVC;
        return impl;
    }

    public animateTransition(transitionContext: any): void {
        const containerView = transitionContext.valueForKey("containerView");
        const completion = UIViewControllerAnimatedTransitioningMethods.completeTransition.bind(transitionContext);
        switch (this._operation) {
            case UINavigationControllerOperation.Push: this._transitionType = "push"; break;
            case UINavigationControllerOperation.Pop: this._transitionType = "pop"; break;
            case UINavigationControllerOperation.None: this._transitionType = "none"; break;
        }

        if (traceEnabled()) {
            traceWrite(`START ${this._transition} ${this._transitionType}`, traceCategories.Transition);
        }
        this._transition.animateIOSTransition(containerView, this._fromVC.view, this._toVC.view, this._operation, completion);
    }

    public transitionDuration(transitionContext: UIViewControllerContextTransitioning): number {
        return this._transition.getDuration();
    }

    public animationEnded(transitionCompleted: boolean): void {
        if (transitionCompleted) {
            if (traceEnabled()) {
                traceWrite(`END ${this._transition} ${this._transitionType}`, traceCategories.Transition);
            }
        } else {
            if (traceEnabled()) {
                traceWrite(`CANCEL ${this._transition} ${this._transitionType}`, traceCategories.Transition);
            }
        }
    }
}

export function _createIOSAnimatedTransitioning(navigationTransition: NavigationTransition, nativeCurve: UIViewAnimationCurve, operation: UINavigationControllerOperation, fromVC: UIViewController, toVC: UIViewController): UIViewControllerAnimatedTransitioning {
    const instance = navigationTransition.instance;
    let transition: Transition;

    if (instance) {
        // Instance transition should take precedence even if the given name match existing transition.
        transition = instance;
    } else if (navigationTransition.name) {
        const name = navigationTransition.name.toLowerCase();
        if (name.indexOf("slide") === 0) {
            const direction = name.substr("slide".length) || "left"; //Extract the direction from the string
            transition = new SlideTransition(direction, navigationTransition.duration, nativeCurve);
        } else if (name === "fade") {
            transition = new FadeTransition(navigationTransition.duration, nativeCurve);
        }
    }

    return transition ? AnimatedTransitioning.init(transition, operation, fromVC, toVC) : undefined;
}