#import <stdexcept>
#import <cstdlib>
#import <cassert>
#import <cmath>

#import <QuartzCore/QuartzCore.h>

#import "PadOptionsController.h"
#import "DetailViewController.h"
#import "RootViewController.h"
#import "SelectionView.h"
#import "HintView.h"
#import "PictView.h"
#import "PadView.h"

//C++ code.
//ROOT
#import "IOSPad.h"

//Demos.
#import "DemoBase.h"

/*
DetailViewController has several views - two pad views (to make animation while changing demos),
two scroll views to be parent of pad-views (to enable zoom and scroll)
and parent view. PadView can be placed in a parent view, or in a scroll-view (which will be placed
in parent view). Actually, I do not need all these view, but there's no serious reason to
modify this code already. This was a testbed.
*/

const unsigned nPadViews = 2;

namespace {

enum class AppMode {
   zoom,
   select
};

}

//Hidden implementation details.
@interface DetailViewController () {

   //"Editor"
   PadOptionsController *padController;
   UIPopoverController *editorPopover;

   //Transparent view with a text
   //and a pictogram for a hint.
   HintView *hintView;
  
   //Small views with pictograms: hints. 
   PictView *panPic;
   PictView *pinchPic;
   PictView *doubleTapPic;
   PictView *rotatePic;
   PictView *singleTapPic;

   NSTimer *animationTimer;
   unsigned currentFrame;

   ROOT::iOS::Pad *pad;

   UIView *parentView;
   UIScrollView *scrollViews[nPadViews];
   PadView *padViews[nPadViews];

   //Transparent view with selected object.
   SelectionView *selectionViews[nPadViews];

   UIPanGestureRecognizer *padPanGestures[nPadViews];
   UITapGestureRecognizer *padTapGestures[nPadViews];
   
   unsigned activeView;
   
   CGSize oldSizes;

   ROOT::iOS::Demos::DemoBase *activeDemo;
   
   //Either zoom or selection.
   AppMode appMode;
   
   BOOL activeAnimation;

   BOOL inTransition;
}

@property (nonatomic, retain) UIPopoverController *popoverController;

- (void) showPanHint;
- (void) showPinchHint;
- (void) showDoubleTapHint;
- (void) showRotationHint;
- (void) showSingleTapHint;
- (void) handleDoubleTapPad : (UITapGestureRecognizer *)tap;

@end



@implementation DetailViewController

//These are generated declarations.
@synthesize toolbar;
@synthesize popoverController;

//This was "generated" by me.
@synthesize help;
@synthesize tabBar;

//_________________________________________________________________
- (void)dealloc
{
   delete pad;
}

#pragma mark - initialization.

//_________________________________________________________________
- (void) initCPPObjects
{
   //Translate C++ exception into the bad C exit.
   try {
      pad = new ROOT::iOS::Pad(640, 640);
   } catch (const std::exception &e) {
      std::exit(1);//WOW???
   }
}

//_________________________________________________________________
- (void) initMainViews
{
   const CGPoint padCenter = self.view.center;
   CGRect padRect = CGRectMake(padCenter.x - 320.f, padCenter.y - 310.f, 640.f, 640.f);
   
   oldSizes.width = 640.f;
   oldSizes.height = 640.f;
   
   parentView = [[UIView alloc] initWithFrame : padRect];
   parentView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
                                 UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;

   [self.view addSubview : parentView];

   //Trick with shadow and shadow path: 
   //http://nachbaur.com/blog/fun-shadow-effects-using-custom-calayer-shadowpaths
   //Many thanks.
   parentView.layer.shadowColor = [UIColor blackColor].CGColor;
   parentView.layer.shadowOpacity = 0.7f;
   parentView.layer.shadowOffset = CGSizeMake(10.f, 10.f);
   UIBezierPath *path = [UIBezierPath bezierPathWithRect : parentView.bounds];
   parentView.layer.shadowPath = path.CGPath;
   ///
   padRect.origin.x = 0.f, padRect.origin.y = 0.f;
   for (unsigned i = 0; i < nPadViews; ++i) {
      scrollViews[i] = [[UIScrollView alloc] initWithFrame : padRect];
      scrollViews[i].backgroundColor = [UIColor darkGrayColor];
      scrollViews[i].delegate = self;
      
      padViews[i] = [[PadView alloc] initWithFrame : padRect forPad : pad];
      scrollViews[i].contentSize = padViews[i].frame.size;
      [scrollViews[i] addSubview : padViews[i]];
      //
      scrollViews[i].minimumZoomScale = 1.f;
      scrollViews[i].maximumZoomScale = 1280.f / 640.f;
      [scrollViews[i] setZoomScale : 1.f];
      [parentView addSubview : scrollViews[i]];
   }

   parentView.hidden = YES;
   //   
   activeView = 0;
   
   padRect = CGRectMake(padCenter.x - 320.f, padCenter.y - 310.f, 640.f, 640.f);
   
   for (unsigned i = 0; i < nPadViews; ++i) {
      selectionViews[i] = [[SelectionView alloc] initWithFrame : padRect];
      selectionViews[i].autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
                                           UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
      [self.view addSubview : selectionViews[i]];
      selectionViews[i].hidden = YES;   
      selectionViews[i].opaque = NO;
   }
}

//_________________________________________________________________
- (void) initHints
{
   //"Hint" is a semi-transparent view, which shows gesture icon and some textual description.
   //Pictogramms.
   CGRect pictRect = CGRectMake(670.f, 450.f, 50.f, 50.f);
   pinchPic = [[PictView alloc] initWithFrame:pictRect andIcon:@"pinch_gesture_icon_small.png"];
   pinchPic.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
                               UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
   [self.view addSubview : pinchPic];
   UITapGestureRecognizer * const pinchTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : @selector(showPinchHint)];
   [pinchPic addGestureRecognizer : pinchTap];
   pinchPic.hidden = YES;

   pictRect.origin.y = 520;
   panPic = [[PictView alloc] initWithFrame:pictRect andIcon : @"pan_gesture_icon_small.png"];
   panPic.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
                             UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
   [self.view addSubview : panPic];
   UITapGestureRecognizer * const panTap = [[UITapGestureRecognizer alloc]initWithTarget : self action : @selector(showPanHint)];
   [panPic addGestureRecognizer : panTap];
   panPic.hidden = YES;
   
   pictRect.origin.y = 590;
   doubleTapPic = [[PictView alloc] initWithFrame : pictRect andIcon : @"double_tap_gesture_icon_small.png"];
   doubleTapPic.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
                                   UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
   [self.view addSubview : doubleTapPic];
   UITapGestureRecognizer * const dtapTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : @selector(showDoubleTapHint)];
   [doubleTapPic addGestureRecognizer : dtapTap];
   doubleTapPic.hidden = YES;

   rotatePic = [[PictView alloc] initWithFrame : pictRect andIcon : @"rotate_icon_small.png"];
   rotatePic.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
                                UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
   [self.view addSubview : rotatePic];
   UITapGestureRecognizer * const rotTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : @selector(showRotationHint)];
   [rotatePic addGestureRecognizer : rotTap];
   rotatePic.hidden = YES;
   
   singleTapPic = [[PictView alloc] initWithFrame : pictRect andIcon : @"single_tap_icon_small.png"];
   singleTapPic.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
                                   UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
   [self.view addSubview : singleTapPic];
   UITapGestureRecognizer *singleTapTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : @selector(showSingleTapHint)];
   [singleTapPic addGestureRecognizer : singleTapTap];
   singleTapPic.hidden = YES;

   const CGPoint center = self.view.center;
   CGRect rect = CGRectMake(center.x - 300.f, center.y - 290.f, 600.f, 600.f);
   hintView = [[HintView alloc] initWithFrame : rect];
   hintView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
                               UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
   [self.view addSubview : hintView];
   UITapGestureRecognizer *hintTap = [[UITapGestureRecognizer alloc] initWithTarget : hintView action : @selector(handleTap:)];
   [hintView addGestureRecognizer : hintTap];
   hintView.hidden = YES;
}

#pragma mark - view lifecycle.

//_________________________________________________________________
- (void) viewDidLoad
{
   self.view.backgroundColor = [UIColor lightGrayColor];
   
   [self initCPPObjects];
   [self initMainViews];
   [self initHints];
   
   inTransition = NO;
   
   appMode = AppMode::zoom;
  
   UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTapPad:)];
   [parentView addGestureRecognizer:tapGesture];
   tapGesture.numberOfTapsRequired = 2;
      
   //Load a help view from a nib file.
   [[NSBundle mainBundle] loadNibNamed:@"HelpView" owner:self options:nil];
   CGRect helpFrame = help.frame;
   helpFrame.origin.x = self.view.center.x - helpFrame.size.width / 2;
   helpFrame.origin.y = self.view.center.y - helpFrame.size.height / 2;
   help.frame = helpFrame;
   help.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
   [self.view addSubview:help];
         
   //Editor view and help view (created in a IB) are on top.
   [self.view bringSubviewToFront:help];
   
   //Shadow for editor.   
   tabBar.selectedItem = [tabBar.items objectAtIndex : 0];

   [super viewDidLoad];
}

#pragma mark - Split view support

//_________________________________________________________________
- (void) splitViewController : (UISplitViewController *) svc willHideViewController : (UIViewController *) aViewController
         withBarButtonItem : (UIBarButtonItem *) barButtonItem forPopoverController : (UIPopoverController *) pc
{
   barButtonItem.title = @"Tutorials";
   NSMutableArray * const items = [[self.toolbar items] mutableCopy];
   [items insertObject : barButtonItem atIndex : 0];
   [self.toolbar setItems : items animated : YES];
   self.popoverController = pc;
}

//_________________________________________________________________
- (void) splitViewController : (UISplitViewController *) svc willShowViewController : (UIViewController *) aViewController
   invalidatingBarButtonItem : (UIBarButtonItem *) barButtonItem
{
   // Called when the view is shown again in the split view, invalidating the button and popover controller.
   NSMutableArray * const items = [[self.toolbar items] mutableCopy];
   [items removeObjectAtIndex : 0];
   [self.toolbar setItems : items animated:YES];
   self.popoverController = nil;
}

#pragma mark - App's logic.

//_________________________________________________________________
- (void) prepareHints
{
   if (appMode == AppMode::zoom) {
      //In 'zoom' mode, pinch, double tap,
      //pan gestures are activated and
      //we show the corresponding hints.
      panPic.hidden = NO;
      pinchPic.hidden = NO;
      doubleTapPic.hidden = NO;

      singleTapPic.hidden = YES;
      rotatePic.hidden = YES;
   } else {
      //Hide zoom mode's pictograms.
      panPic.hidden = YES;
      pinchPic.hidden = YES;
      doubleTapPic.hidden = YES;

      //Show selection or rotation pictogram.
      rotatePic.hidden = !activeDemo->Supports3DRotation();
      singleTapPic.hidden = !rotatePic.hidden;
   }
}

//_________________________________________________________________
- (void) setActiveDemo : (ROOT::iOS::Demos::DemoBase *) demo
{
   assert(demo != nullptr && "setActiveDemo:, parameter 'demo' is null");
   
   if (inTransition)
      return;

   help.hidden = YES;
   hintView.hidden = YES;

   if (demo != activeDemo) {
      selectionViews[0].hidden = YES;
      selectionViews[1].hidden = YES;
   
      parentView.hidden = NO;
      //Stop any animated demo (previously active).
      if (animationTimer) {
         [animationTimer invalidate];
         animationTimer = nil;
      }
      
      currentFrame = 0;
      //
      
      //Prepare to make an animation: remove one view, show another.
      activeDemo = demo;
      [self prepareHints];
   
      UIView * showView = 0;
      UIView * hideView = 0;
      
      const unsigned hide = activeView;
      const unsigned show = !hide;
      activeView = show;
   
      //Pad's parent is different in 'zoom' and 'select' mode.
   
      if (appMode == AppMode::zoom) {
         showView = scrollViews[show];
         hideView = scrollViews[hide];
      } else {
         showView = padViews[show];
         hideView = padViews[hide];         
      }

      //This is temporary hack.
      [padViews[activeView] setProcessPan : activeDemo->Supports3DRotation()];
      [padViews[activeView] setProcessTap : !activeDemo->Supports3DRotation()];

      //Remove old contents of pad, 
      //set pad's parameters (if required by demo)
      //reset demo (if required), add demo's primitives to pad.
      pad->Clear();
      activeDemo->AdjustPad(pad);
      activeDemo->ResetDemo();
      activeDemo->PresentDemo();
      
      //Repaint active view's content.
      [padViews[activeView] setNeedsDisplay];

      if (activeDemo->IsAnimated()) {
         //Start timer for animated demo.
         activeDemo->StartAnimation();
         animationTimer = [NSTimer scheduledTimerWithTimeInterval : 0.5 / 25 target : self selector : @selector(onTimer) userInfo : nil repeats : YES];
      }
      
      //Make an animation: hide one view (demo), show another one.
      showView.hidden = NO;
      hideView.hidden = YES;
      // First create a CATransition object to describe the transition
      CATransition *transition = [CATransition animation];
      // Animate over 3/4 of a second
      transition.duration = 0.75;
      // using the ease in/out timing function
      transition.timingFunction = [CAMediaTimingFunction functionWithName : kCAMediaTimingFunctionEaseInEaseOut];
      // Now to set the type of transition.
      transition.type = kCATransitionReveal;
		transition.subtype = kCATransitionFromLeft;
      // Finally, to avoid overlapping transitions we assign ourselves as the delegate for the animation and wait for the
      // -animationDidStop:finished: message. When it comes in, we will flag that we are no longer transitioning.
      inTransition = YES;
      transition.delegate = self;
      // Next add it to the containerView's layer. This will perform the transition based on how we change its contents.
      [parentView.layer addAnimation : transition forKey : nil];
   }
}

//_________________________________________________________________
- (void) dismissPopover
{
   [popoverController dismissPopoverAnimated : YES];
}

//_________________________________________________________________
- (void) onTimer
{
   assert(activeDemo != nullptr && "onTimer, activeDemo is null");
   assert(animationTimer != nil && "onTimer, animationTimer is nil");

   if (currentFrame == activeDemo->NumOfFrames()) {
      [animationTimer invalidate];
      animationTimer = nil;
   } else {
      ++currentFrame;
      activeDemo->NextStep();      
      [padViews[activeView] setNeedsDisplay];
   }
}

//_________________________________________________________________
- (void) resetPadView : (unsigned) view
{
   //Reset the pad view to its original dimensions (640x640) to
   //avoid different ugly donwscaling effects.

   UIScrollView * const scroll = (UIScrollView *)padViews[view].superview;
   const CGRect oldRect = padViews[view].frame;
   
   if (std::abs(640.f - oldRect.size.width) < 0.01 && std::abs(640.f - oldRect.size.height) < 0.01)
      return;
      
   const CGRect padRect = CGRectMake(0.f, 0.f, 640.f, 640.f);
   [padViews[view] removeFromSuperview];
   padViews[view] = [[PadView alloc] initWithFrame : padRect forPad : pad];
   [scroll addSubview : padViews[view]];
  
   scroll.minimumZoomScale = 1.f;
   scroll.maximumZoomScale = 2.f;
   [scroll setZoomScale : 1.f];
   scroll.contentSize = padRect.size;
   scroll.contentOffset = CGPointMake(0.f, 0.f);

   oldSizes.width = 640.f;
   oldSizes.height = 640.f;
}

#pragma mark - delegate for a transition animation.

//_________________________________________________________________
-(void) animationDidStop : (CAAnimation *) animation finished : (BOOL) flag
{
#pragma unused(animation)

   //After one view was hidden, resize it's scale to 1 and
   //view itself to original size.
   if (flag) {
      inTransition = NO;
      if (appMode == AppMode::zoom)
         [self resetPadView : activeView ? 0 : 1];
   }
}

#pragma mark - That's what I call action :)

//zoomButtonPressed/selectButtonPressed - legacy code (before I really had buttons), now they are
//simply called from tabs' tap gestures.
//_________________________________________________________________
- (void) zoomButtonPressed
{
   if (appMode == AppMode::zoom)
      return;
 
   //Zoom mode was selected.
   appMode = AppMode::zoom;
   //The mode was 'select' previously.
   //Reparent pad views, now scrollview is a parent for a pad view.
   
   for (unsigned i = 0; i < nPadViews; ++i) {
      selectionViews[i].hidden = YES;
      [padViews[i] removeGestureRecognizer : padPanGestures[i]];
      [padViews[i] removeGestureRecognizer : padTapGestures[i]];

      padViews[i].hidden = NO;
      [padViews[i] removeFromSuperview];
      [scrollViews[i] addSubview : padViews[i]];
   }
   
   if (activeDemo) {
      scrollViews[activeView].hidden = NO;
      [self prepareHints];
   }
}

//_________________________________________________________________
- (void) selectButtonPressed
{
   if (appMode == AppMode::select)
      return;

   appMode = AppMode::select;

   //hide both scroll views, re-parent pad-views.
   for (unsigned i = 0; i < nPadViews; ++i) {
      scrollViews[i].hidden = YES;
      //1. Check, if views must be reset (unzoom).
      [self resetPadView : i];

      [padViews[i] removeFromSuperview];
      
      padPanGestures[i] = [[UIPanGestureRecognizer alloc] initWithTarget:padViews[i] action:@selector(handlePanGesture:)];
      [padViews[i] addGestureRecognizer:padPanGestures[i]];
      
      padTapGestures[i] = [[UITapGestureRecognizer alloc] initWithTarget:padViews[i] action:@selector(handleTapGesture:)];
      [padViews[i] addGestureRecognizer : padTapGestures[i]];

      [padViews[i] setSelectionView : selectionViews[i]];
   
      [parentView addSubview : padViews[i]];
 
      if (activeDemo) //In case no demo was selected - nothing to show yet.
         padViews[i].hidden = i == activeView ? NO : YES;
   }

   if (activeDemo) {
      [padViews[activeView] setProcessPan : activeDemo->Supports3DRotation()];
      [padViews[activeView] setProcessTap : !activeDemo->Supports3DRotation()];
      [self prepareHints];
   }
}

//_________________________________________________________________
- (IBAction) editButtonPressed : (id) sender
{
#pragma unsued(sender)

   if (editorPopover && editorPopover.popoverVisible) {
      [editorPopover dismissPopoverAnimated : YES];
   } else {
      if (!padController) {
         padController = [[PadOptionsController alloc] initWithNibName : @"PadOptionsController" bundle : nil];
         padController.preferredContentSize = CGSizeMake(250.f, 650.f);
      }

      if (!editorPopover) {
         editorPopover = [[UIPopoverController alloc] initWithContentViewController : padController];
         editorPopover.popoverContentSize = CGSizeMake(250.f, 650.f);
      }

      assert(pad != nullptr && "editButtonPressed:, pad is null");
      [editorPopover presentPopoverFromBarButtonItem : sender permittedArrowDirections : UIPopoverArrowDirectionAny animated : YES];
      [padController setView : padViews[activeView] andPad : pad];
   }
}

//_________________________________________________________________
- (IBAction) showHelp
{
   CATransition * const transition = [CATransition animation];
   transition.duration = 0.25;
   transition.timingFunction = [CAMediaTimingFunction functionWithName : kCAMediaTimingFunctionEaseInEaseOut];
   transition.type = kCATransitionReveal;
   transition.subtype = kCATransitionFade;
   help.hidden = !help.hidden;
   [help.layer addAnimation : transition forKey : nil];
}


#pragma mark - Tab bar delegate.

//_________________________________________________________________
- (void) tabBar : (UITabBar *) tb didSelectItem : (UITabBarItem *) item
{
   if (item.tag == 1)
      [self zoomButtonPressed];
   else
      [self selectButtonPressed];
}

#pragma mark - UIScrollView's delegate.

//_________________________________________________________________
- (UIView *) viewForZoomingInScrollView : (UIScrollView *) scrollView
{
   if (scrollView == scrollViews[0])
      return padViews[0];

   return padViews[1];
}

//_________________________________________________________________
- (CGRect) centeredFrameForScrollView : (UIScrollView *)scroll andUIView : (UIView *) rView
{
   const 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.f;
   // center vertically
   if (frameToCenter.size.height < boundsSize.height)
      frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
   else
      frameToCenter.origin.y = 0.f;

   return frameToCenter;
}

//_________________________________________________________________
- (void) scrollViewDidEndZooming : (UIScrollView *) scrollView withView : (UIView *) view atScale : (float) scale
{
   //The problem with a scrollview: it scales the original 'image', making it look badly.
   //At the end of zoom I'm recreating the pad view and it must be re-rendered, creating
   //a new nice image.

   const CGPoint off = [scrollView contentOffset];
   CGRect oldRect = padViews[activeView].frame;
   oldRect.origin.x = 0.f;
   oldRect.origin.y = 0.f;
   
   if (abs(oldSizes.width - oldRect.size.width) < 0.01 && (abs(oldSizes.height - oldRect.size.height) < 0.01))
      return;

   oldSizes = oldRect.size;
   
   [padViews[activeView] removeFromSuperview];
   padViews[activeView] = [[PadView alloc] initWithFrame : oldRect forPad : pad];
   [scrollView addSubview : padViews[activeView]];
  
   [scrollView setZoomScale : 1.f];
   scrollView.contentSize = oldRect.size;   
   scrollView.contentOffset = off;

   scrollView.minimumZoomScale = 640.f / oldRect.size.width;
   scrollView.maximumZoomScale = 1280.f / oldRect.size.width;
}

//_________________________________________________________________
- (void) scrollViewDidZoom : (UIScrollView *) scrollView
{
   padViews[activeView].frame = [self centeredFrameForScrollView : scrollView andUIView : padViews[activeView]];
}

#pragma mark - Tap gesture handler.

//_________________________________________________________________
- (void) handleDoubleTapPad : (UITapGestureRecognizer *) tap
{
   if (appMode != AppMode::zoom || !activeDemo)
      return;

   if (oldSizes.width > 640.f)
      [self resetPadView : activeView];
   else {
      //Zoom to maximum.
      oldSizes = CGSizeMake(1280.f, 1280.f);
      const CGRect newRect = CGRectMake(0.f, 0.f, 1280.f, 1280.f);
      
      [padViews[activeView] removeFromSuperview];

      padViews[activeView] = [[PadView alloc] initWithFrame : newRect forPad : pad];
      [scrollViews[activeView] addSubview : padViews[activeView]];
  
      [scrollViews[activeView] setZoomScale : 1.f];
      scrollViews[activeView].contentSize = newRect.size;

      scrollViews[activeView].minimumZoomScale = 1.f;
      scrollViews[activeView].maximumZoomScale = 1.f;

      const CGPoint tapXY = [tap locationInView : tap.view];  
      scrollViews[activeView].contentOffset = CGPointMake(tapXY.x, tapXY.y);    
   }
}

#pragma mark - Hints.

//_________________________________________________________________
- (void) showPinchHint
{
   [hintView setHintIcon : @"pinch_gesture_icon.png" hintText : @"Use a pinch gesture to zoom/unzoom a pad. Tap to close this hint."];
   [hintView setNeedsDisplay];
   hintView.hidden = NO;
}

//_________________________________________________________________
- (void) showPanHint
{
   [hintView setHintIcon : @"pan_gesture_icon.png" hintText : @"When a pad zoomed in, use a pan gesture to scroll a pad. Tap to close this hint."];
   [hintView setNeedsDisplay];
   hintView.hidden = NO;
}

//_________________________________________________________________
- (void) showDoubleTapHint
{
   [hintView setHintIcon : @"double_tap_gesture_icon.png" hintText : @"Use the double tap gesture to zoom/unzoom a pad. Tap to close this hint."];
   [hintView setNeedsDisplay];
   hintView.hidden = NO;
}

//_________________________________________________________________
- (void) showRotationHint
{
   [hintView setHintIcon:@"rotate_icon.png" hintText : @"You can rotate 3D object, using pan gesture. Tap to close this hint."];
   [hintView setNeedsDisplay];
   hintView.hidden = NO;
}

//_________________________________________________________________
- (void) showSingleTapHint
{
   [hintView setHintIcon:@"single_tap_icon.png" hintText : @"Use a single tap gesture to select pad's contents. Tap to close this hint."];
   [hintView setNeedsDisplay];
   hintView.hidden = NO;
}

#pragma mark - Interface orientation.

//_________________________________________________________________
- (BOOL) shouldAutorotateToInterfaceOrientation : (UIInterfaceOrientation) interfaceOrientation
{
#pragma unused(interfaceOrientation)

    return YES;
}


@end
