#import <cmath>

#import <QuartzCore/QuartzCore.h>

#import "PadScrollView.h"
#import "PadView.h"

//C++ (ROOT) imports.
#import "TObject.h"
#import "IOSPad.h"

static const CGFloat defaultImageW = 700.f;
static const CGFloat defaultImageH = 700.f;
static const CGFloat maxZoom = 2.f;
static const CGFloat minZoom = 1.f;

@implementation PadScrollView {
   ROOT::iOS::Pad *pad;

   PadView *nestedView;
}

//____________________________________________________________________________________________________
+ (CGRect) defaultImageFrame
{
   return CGRectMake(0.f, 0.f, defaultImageW, defaultImageH);
}

//____________________________________________________________________________________________________
- (CGPoint) adjustOriginForFrame : (CGRect)frame withSize : (CGSize) sz
{
   return CGPointMake(frame.size.width / 2 - sz.width / 2, frame.size.height / 2 - sz.height / 2);
}

//____________________________________________________________________________________________________
- (void) initPadView : (CGRect) frame
{
   CGRect padFrame = [PadScrollView defaultImageFrame];
   padFrame.origin = [self adjustOriginForFrame : frame withSize : padFrame.size];

   nestedView = [[PadView alloc] initImmutableViewWithFrame : padFrame];
   nestedView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;

   [self addSubview : nestedView];
}


//____________________________________________________________________________________________________
- (void) setContentSize : (CGSize) size contentOffset : (CGPoint) offset minScale : (CGFloat) min maxScale : (CGFloat) max scale : (CGFloat) scale
{
   self.maximumZoomScale = max;
   self.minimumZoomScale = min;
   self.zoomScale = scale;
   self.contentSize = size;
   self.contentOffset = offset;
}

//____________________________________________________________________________________________________
- (instancetype) initWithFrame : (CGRect) frame
{
   if (self = [super initWithFrame : frame]) {
      self.delegate = self; //hehehehe
      self.bouncesZoom = NO;
      self.bounces = NO;
      self.backgroundColor = [UIColor clearColor];
      self.decelerationRate = UIScrollViewDecelerationRateFast;

      [self setContentSize : frame.size contentOffset : CGPointZero minScale : minZoom maxScale : maxZoom scale : 1];
      
      UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : @selector(handleDoubleTap:)];
      doubleTap.numberOfTapsRequired = 2;
      [self addGestureRecognizer : doubleTap];
   }
    
   return self;
}

#pragma mark - Child view's management
//____________________________________________________________________________________________________
- (void) clearScroll
{
   [self setContentSize : [PadScrollView defaultImageFrame].size contentOffset : CGPointZero minScale : minZoom maxScale : maxZoom scale : 1];
   [nestedView removeFromSuperview];
   nestedView = nil;
}

#pragma mark - Image/pad/geometry management.

//____________________________________________________________________________________________________
- (void) setPad : (ROOT::iOS::Pad *) p
{
   pad = p;
   pad->SetViewWH(defaultImageW, defaultImageH);

   if (nestedView && nestedView.zoomed) {
      [self clearScroll];
      [self initPadView : self.frame];
   } else if (!nestedView) {
      [self initPadView : self.frame];
   }

   [nestedView setPad : pad];
   [nestedView setNeedsDisplay];
}

//____________________________________________________________________________________________________
- (void) resetToFrame : (CGRect) newFrame
{
   self.frame = newFrame; 
   [self setContentSize : newFrame.size contentOffset : CGPointZero minScale : minZoom maxScale : maxZoom scale : 1];
   
   if (nestedView.zoomed) {
      [self clearScroll];
      [self initPadView : newFrame];
      [nestedView setPad : pad];
      [nestedView setNeedsDisplay];
   }
}

//_________________________________________________________________
- (CGRect) centeredFrameForScrollView : (UIScrollView *) scroll andUIView : (UIView *) rView
{
   CGSize boundsSize = scroll.bounds.size;
   CGRect frameToCenter = rView.frame;
   // center horizontally
   if (frameToCenter.size.width < boundsSize.width) {
      frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
   }
   else {
      frameToCenter.origin.x = 0;
   }
   // center vertically
   if (frameToCenter.size.height < boundsSize.height) {
      frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
   }
   else {
      frameToCenter.origin.y = 0;
   }
   
   return frameToCenter;
}

//____________________________________________________________________________________________________
- (void) scrollViewDidZoom : (UIScrollView *) scroll
{
   nestedView.frame = [self centeredFrameForScrollView : scroll andUIView : nestedView];
}

//____________________________________________________________________________________________________
- (void) scrollViewDidEndZooming : (UIScrollView *) scroll withView : (UIView *) view atScale : (float) scale
{
   const CGPoint offset = [scroll contentOffset];
   const CGRect newFrame = nestedView.frame;
  
   [scroll setZoomScale : 1.f];
   
   const unsigned base = [PadScrollView defaultImageFrame].size.width;

   scroll.minimumZoomScale = base / newFrame.size.width;
   scroll.maximumZoomScale = maxZoom * base / newFrame.size.width;

   [nestedView removeFromSuperview];

   nestedView = [[PadView alloc] initImmutableViewWithFrame : newFrame];
   [nestedView setPad : pad];

   [scroll addSubview : nestedView];

   scroll.contentSize = newFrame.size;
   scroll.contentOffset = offset;

   nestedView.zoomed = YES;
}

//____________________________________________________________________________________________________
- (UIView *) viewForZoomingInScrollView : (UIScrollView *) scrollView
{
   return nestedView;
}

//____________________________________________________________________________________________________
- (CGRect) zoomRectForScale : (float) scale withCenter : (CGPoint) center
{
    
    CGRect zoomRect;
    
    // the zoom rect is in the content view's coordinates. 
    //    At a zoom scale of 1.0, it would be the size of the imageScrollView's bounds.
    //    As the zoom scale decreases, so more content is visible, the size of the rect grows.
    zoomRect.size.height = [self frame].size.height / scale;
    zoomRect.size.width  = [self frame].size.width  / scale;
    
    // choose an origin so as to get the right center.
    zoomRect.origin.x    = center.x - (zoomRect.size.width  / 2.0);
    zoomRect.origin.y    = center.y - (zoomRect.size.height / 2.0);
    
    return zoomRect;
}

//____________________________________________________________________________________________________
- (void) handleDoubleTap : (UITapGestureRecognizer *) tap
{
   //Identify, if we should unzoom.
   if (std::abs(nestedView.frame.size.width - maxZoom * defaultImageW) < 10) {
      [self resetToFrame : self.frame];
   } else {
      //Zoom in.
      const CGFloat newScale = maxZoom * defaultImageW / nestedView.frame.size.width;
      CGRect zoomRect = [self zoomRectForScale : newScale withCenter : [tap locationInView : self]];
      [self zoomToRect : zoomRect animated : YES];
   }
}

@end
