//
//  SideMenuViewController.swift
//  SideMenuViewController
//
//  Created by Cyril CHANDELIER on 3/10/14.
//  Copyright (c) 2014 Cyril Chandelier. All rights reserved.
//

//#define kAnimationDuration   0.25f

import UIKit

@objc protocol SideMenuProtocol {
    func viewWillReduceFromLeft(fromLeft : Bool)
    func viewDidReduceFromLeft(fromLeft : Bool)
    func viewWillGrow()
    func viewDidGrow()
}


class SideMenuViewController : UIViewController, UIGestureRecognizerDelegate {
    
    private let kAnimationDuration = 0.25
    
    var centerViewController : UIViewController?
    var leftViewController : UIViewController?
    var rightViewController : UIViewController?
    
    var zoomScale : CGFloat = 1
    var edgeOffset : UIOffset = UIOffset(horizontal: 130.0, vertical: 0.0)
    var duration : Double = 0.25
    
    var shadowColor : UIColor = UIColor.blackColor()
    var shadowOpacity : Float = 0.4
    var shadowRadius : CGFloat = 10.0
    
    // MARK : private property
    
    // Current view controller
    private var currentSideViewController : UIViewController?
    
    // Gesture recognizers
    private var centerTapGestureRecognizer : UITapGestureRecognizer?
    var centerPanGestureRecognizer : UIPanGestureRecognizer?
    
    // Flags
    private var leftSideOpen : Bool = false
    private var rightSideOpen : Bool = false
    
    // MARK : Getters
    private var leftView : UIView? {
        if leftViewController == nil {
            return nil
        }
        
        // Prepare view controller
        prepareViewController(leftViewController!)
        return leftViewController!.view;
    }
    
    private var rightView : UIView? {
        if rightViewController == nil {
            return nil
        }
        
        // Prepare view controller
        prepareViewController(rightViewController!)
        return rightViewController!.view
    }
    
    // MARK : Constructor
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    init(centerViewController: UIViewController) {
        super.init(nibName: nil, bundle: nil)
        // Hold view controller
        self.centerViewController = centerViewController
    }
    
    // MARK: View management
    
    override func viewDidLoad() {
        
        super.viewDidLoad()
        
        // Manage center view controller
        assert(centerViewController != nil, "Center view controller can't be nil")
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        if centerViewController?.view.superview == nil {
            // Prepare and display center
            prepareAndDisplayCenterViewControllerWithTransform(CGAffineTransformIdentity)
            
            if leftSideOpen {
                openLeftSideViewControllerAnimated(false, completion: nil)
            } else if rightSideOpen {
                openRightSideViewControllerAnimated(false, completion: nil)
            }
        }
    }
    
    // MARK : Public opening / closing methods
    func openLeftSideViewControllerAnimated(animated : Bool, completion : (() -> Void)?) {
        openChildView(leftView, left:true, animated:animated, completion:completion)
    }
    
    func openRightSideViewControllerAnimated(animated : Bool, completion : (() -> Void)?) {
        openChildView(rightView, left:false, animated:animated, completion:completion)
    }
    
    private func openChildView(childView: UIView?, left : Bool, animated: Bool, completion : (() -> Void)?) {
        // Prevent empty view to start animation
        if childView == nil {
            return
        }
        // Sent it to back
        view.sendSubviewToBack(childView!)
        view.endEditing(true)
        
        // Warn that view controller will grow
        if  let centerVC = centerViewController {
            if let prot = centerVC as? SideMenuProtocol {
                
                if centerVC.respondsToSelector(#selector(SideMenuProtocol.viewWillReduceFromLeft(_:))) {
                    prot.viewWillReduceFromLeft(left)
                }
            }
        }
        if  let centerVC = centerViewController {
            if let prot = centerVC as? SideMenuProtocol {
                
                if centerVC.respondsToSelector(#selector(SideMenuProtocol.viewDidReduceFromLeft(_:))) {
                    prot.viewDidReduceFromLeft(left)
                }
            }
        }
        
        // Add shadow
        addCenterViewControllerShadow();
        
        // Determine view controller to display
        let viewControllerToDisplay = (left) ? leftViewController : rightViewController
        
        
        // Final block
        let finishedBlock: (finished : Bool) -> Void = { (finished) in
            // Flag
            if left {
                self.leftSideOpen = true
            } else {
                self.rightSideOpen = true
            }
            
            // Warn that view controller will grow
            
            if  let centerVC = self.centerViewController {
                if let prot = centerVC as? SideMenuProtocol {
                    
                    if centerVC.respondsToSelector(#selector(SideMenuProtocol.viewDidReduceFromLeft(_:))) {
                        prot.viewDidReduceFromLeft(left)
                    }
                }
            }
            
            
            if completion != nil {
                completion!()
            }
        }
        
        // Animable part
        if animated {
            UIView.animateWithDuration(
                duration,
                delay: 0.0,
                options: UIViewAnimationOptions.CurveEaseInOut,
                animations: {
                    viewControllerToDisplay!.view.transform = CGAffineTransformIdentity
                    self.centerViewController!.view.transform = self.openTransformForView((self.centerViewController!.view)!, left: left)
                }, completion: finishedBlock
            )
        }
    }
    
    func closeSideViewControllerAnimated(animated: Bool, completion: (() -> Void)?) {
        let finishedBlock : (finished : Bool) -> Void = { (finished) in
            // Remove from superview
            self.currentSideViewController?.view.removeFromSuperview()
            
            // Remove from parent view controller
            self.currentSideViewController?.removeFromParentViewController()
            
            // Release view controller
            self.currentSideViewController = nil;
            
            // Flag
            self.leftSideOpen = false
            self.rightSideOpen = false;
            
            // Warn that view controller did grow
            if  let centerVC = self.centerViewController {
                if let prot = centerVC as? SideMenuProtocol {
                    
                    if centerVC.respondsToSelector(#selector(SideMenuProtocol.viewDidGrow)) {
                        prot.viewDidGrow()
                    }
                }
            }
            
            // Execute completion block
            if completion != nil {
                completion!();
            }
        }
        
        // Remove gesture recognizer
        centerTapGestureRecognizer?.view?.removeFromSuperview()
        centerTapGestureRecognizer = nil;
        
        // Warn that view controller will grow
        if  let centerVC = self.centerViewController {
            if let prot = centerVC as? SideMenuProtocol {
                
                if centerVC.respondsToSelector(#selector(SideMenuProtocol.viewWillGrow)) {
                    prot.viewWillGrow()
                }
            }
        }
        
        // Add shadow
        removeCenterViewControllerShadow()
        
        // Animable part
        if animated {
            //            UIView.animateWithDuration(
            //                duration,
            //                delay: 0.0,
            //                options: UIViewAnimationOptions.CurveEaseInOut,
            //                animations: {
            //                    // FIXME : Bug
            //                    self.centerViewController?.view.transform = CGAffineTransformIdentity
            //                } as () -> Void,
            //                completion: finishedBlock
            //            )
            UIView.animateWithDuration(
                duration,
                delay: 0.0,
                options: UIViewAnimationOptions.CurveEaseInOut,
                animations: {
                    self.centerViewController?.view.transform = CGAffineTransformIdentity
                    print("hide")
                }, completion: finishedBlock
            )
        } else {
            centerViewController?.view.transform = CGAffineTransformIdentity
            finishedBlock(finished: true)
        }
    }
    
    func presentCenterViewController(aViewController: UIViewController, animated: Bool) {
        
        if aViewController != centerViewController {
            // Reset display view controller transform
            aViewController.view.transform = CGAffineTransformIdentity
            var transform = CGAffineTransformIdentity;
            
            if let _ = centerViewController?.view.superview {
                // Compute frame
                transform = centerViewController!.view.transform
                
                // Remove old view controller
                centerViewController!.view.removeFromSuperview()
                centerViewController!.removeFromParentViewController()
            }
            
            // Hold as new center view controller
            centerViewController = aViewController
            
            // Display center
            prepareAndDisplayCenterViewControllerWithTransform(transform)
        }
        
        // Close side panel
        closeSideViewControllerAnimated(animated, completion:nil)
    }
    
    // MARK : Animations
    
    private func openTransformForView(view : UIView, left: Bool) -> CGAffineTransform {
        let transformSize = zoomScale
        var newTransform : CGAffineTransform
        if left {
            newTransform = CGAffineTransformTranslate(view.transform, view.bounds.midX + edgeOffset.horizontal, edgeOffset.vertical)
        } else {
            newTransform = CGAffineTransformTranslate(view.transform, -(view.bounds.midX + edgeOffset.horizontal), edgeOffset.vertical)
        }
        return CGAffineTransformScale(newTransform, transformSize, transformSize)
    }
    
    private func closeTransformForMenuView(left left : Bool) -> CGAffineTransform {
        // Transform variables
        let transformSize = 1.0 / zoomScale;
        
        // Transform
        let transform = CGAffineTransformScale(self.centerViewController!.view.transform, transformSize, transformSize)
        
        if left {
            return CGAffineTransformTranslate(transform, -(self.view.bounds.midX) - edgeOffset.horizontal, -edgeOffset.vertical)
        } else {
            return CGAffineTransformTranslate(transform, self.view.bounds.midX + edgeOffset.horizontal, edgeOffset.vertical)
        }
    }
    
    // MARK : Shadow
    
    private func addCenterViewControllerShadow() {
        // Rect
        var rect = centerViewController!.view.layer.bounds
        rect.origin.y -= 10.0
        rect.size.width += 20.0
        rect.size.height += 20.0
        
        // Shadow
        centerViewController!.view.layer.shadowPath = UIBezierPath(rect: rect).CGPath
        centerViewController!.view.layer.shadowColor = shadowColor.CGColor
        centerViewController!.view.layer.shadowOffset = CGSize(width: -10.0, height: 0.0)
        centerViewController!.view.layer.shadowRadius = shadowRadius
        
        // Animation
        let anim = CABasicAnimation(keyPath: "shadowOpacity")
        anim.fromValue = 0.0
        anim.toValue = shadowOpacity
        anim.duration = duration
        centerViewController!.view.layer.addAnimation(anim, forKey: "shadowOpacity")
        centerViewController!.view.layer.shadowOpacity = shadowOpacity
    }
    
    private func removeCenterViewControllerShadow() {
        // Animation
        let anim = CABasicAnimation(keyPath: "shadowOpacity")
        anim.fromValue = shadowOpacity
        anim.toValue = 0.0
        anim.duration = duration;
        centerViewController!.view.layer.addAnimation(anim, forKey: "shadowOpacity")
        centerViewController!.view.layer.shadowOpacity = 0.0
    }
    
    // MARK : Gesture recognizers
    
    func centerViewTapped(gestureRecognizer: UIGestureRecognizer) {
        if currentSideViewController == nil {
            return
        }
        
        // Close side view controller
        closeSideViewControllerAnimated(true, completion:nil)
    }
    
    func movePanel(sender: UIPanGestureRecognizer) {
        let velocity = sender.velocityInView(sender.view)
        // Gesture ended
        if sender.state == .Ended {
            if velocity.x > 0 {
                if UIDevice.currentDevice().userInterfaceIdiom == .Pad && UIApplication.sharedApplication().statusBarOrientation.isLandscape {
                } else {
                    openLeftSideViewControllerAnimated(true, completion:nil)
                }
            } else {
                openRightSideViewControllerAnimated(true, completion:nil)
            }
        }
    }
    
    // MARK : UIGestureRecognizerDelegate methods
    
    func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
        var location = gestureRecognizer.locationInView(centerViewController?.view)
        guard location.x < 60 else {
            return false
        }
        if centerPanGestureRecognizer != nil && (leftSideOpen || rightSideOpen) {
            return false
        }
        return true
    }
    
    // MARK : Utils
    
    private func prepareViewController(aViewController : UIViewController) {
        // Add view as subview
        view.addSubview(aViewController.view)
        
        // View controller containment
        addChildViewController(aViewController)
        
        // Current side view controller
        currentSideViewController = aViewController
        
        // Resize view
        aViewController.view.frame = view.bounds;
        
        // Gesture recognizer
        centerTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(SideMenuViewController.centerViewTapped(_:)))
        centerTapGestureRecognizer!.cancelsTouchesInView = true
        let tappableView = UIView(frame: centerViewController!.view.frame)
        tappableView.autoresizingMask = [UIViewAutoresizing.FlexibleTopMargin, UIViewAutoresizing.FlexibleBottomMargin,
                                         UIViewAutoresizing.FlexibleLeftMargin, UIViewAutoresizing.FlexibleRightMargin]
        tappableView.addGestureRecognizer(centerTapGestureRecognizer!)
        centerViewController!.view.addSubview(tappableView)
    }
    
    private func prepareAndDisplayCenterViewControllerWithTransform(transform: CGAffineTransform) {
        // Add gestures
        centerPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(SideMenuViewController.movePanel(_:)))
        centerPanGestureRecognizer!.minimumNumberOfTouches = 1
        centerPanGestureRecognizer!.maximumNumberOfTouches = 1
        centerPanGestureRecognizer!.delegate = self
        centerPanGestureRecognizer!.cancelsTouchesInView = false
        
        centerViewController!.view.addGestureRecognizer(centerPanGestureRecognizer!)
        // Add center view as subview
        view.addSubview(centerViewController!.view)
        
        // Resize center view
        centerViewController!.view.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: view.frame.width, height: view.frame.height))
        centerViewController!.view.transform = transform
        
        // View controller containment
        addChildViewController(centerViewController!)
    }
    
}
