#import "TuneViewController.h"
#import "EditABCController.h"
#import "Tune.h"
#import "TunePlayer.h"
#import "TuneParser.h"
#import "TuneSettingsController.h"
#import "AppDelegate.h"
#if TARGET_IPAD
#include "MainViewController.h"
#include "PlaylistViewController.h"
#include "SearchViewController.h"
#include "ManageTunesController.h"
#include "SettingsController.h"
#endif
//FIXME - chgrid test
#if 0
#import "../CHGridView/TuneGridViewController.h"
#endif

#if TARGET_IPAD
#define kTuneMargin 25.0f
#else
#define kTuneMargin 25.0f
#endif

static TunePlayer *tunePlayer;
static BOOL printingEnabled;

@implementation TuneViewController

@synthesize tune;
@synthesize savedDots;
@synthesize playlist;

//-(id<TuneViewDelegate>)tuneViewDelegate
//{
//	return tuneView.delegate;
//}
//
//-(void)setTuneViewDelegate:(id<TuneViewDelegate>)delegate
//{
//	tuneView.delegate = [delegate retain];
//}

-(id)initWithTune:(Tune *)aTune
{
  [self initWithTune:aTune fromPlayList:nil];
  return self;
}

-(id)initWithTune:(Tune *)aTune fromPlayList:(Playlist *)pl
{
	self = [super initWithNibName:@"TuneView" bundle:nil];
	if ( self != nil ) {
    self.playlist = pl;
    self.tune = aTune;
		self.title = aTune.title;
    saveChangesSheet = NULL;
    multipleTunesSheet = NULL;
    editController = NULL;
    showingABC = NO;
    settingsController = nil;
    showingSettings = NO;
    tunePlayer = nil;
    numPages = 1;

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(finishedPlaying:) name:@"finishedPlaying" object:nil]; 
#if TARGET_IPAD
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(redrawTune:) name:@"musicPreferencesChanged" object:nil]; 
#endif
	}
	return self;
}

-(void)viewDidLoad {
  barsHidden = false;
  CGRect bounds;
#if 1//Measuring
  numPages = 1;
  if ( tune ) {
    UIGraphicsBeginImageContext( CGSizeMake( 8, 8 ) );
    numPages = [Tune drawTune:tune.dots intoContext:UIGraphicsGetCurrentContext() bounds:CGRectMake(0, 0, 8, 8) scale:1.0f];
    if ( numPages < 1 )
      numPages = 1;
    bounds = CGRectMake( 0, 0, PaperWidth, numPages * PaperHeight );
    UIGraphicsEndImageContext();
  } else
    bounds = CGRectMake( 0, 0, PaperWidth, PaperHeight );
#else
  bounds = CGRectMake( 0, 0, PaperWidth, PaperHeight );
#endif
  scrollView = [[UIScrollView alloc] initWithFrame:[mainView bounds]];
	[scrollView setContentSize:bounds.size];
	[scrollView setScrollEnabled:YES];
  scrollView.bouncesZoom = NO;
  scrollView.minimumZoomScale = 0.5f;
  scrollView.maximumZoomScale = 4.0f;
  scrollView.delegate = self;
	scrollView.autoresizesSubviews = YES;
  scrollView.clipsToBounds = YES;
	scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
//  [scrollView retain];

#if TARGET_IPAD
  background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Linen.jpg"]];
  scrollView.backgroundColor = [UIColor clearColor];
#endif

  tuneView = [[TuneView alloc] initWithFrame:CGRectMake(0.0, 0.0, 1.0, 1.0)];
  tuneView.delegate = self;
  tuneView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  tuneView.scale = 1.0f;

  if ( [[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [tuneView respondsToSelector:@selector(contentScaleFactor)] )
    tuneView.contentScaleFactor = [UIScreen mainScreen].scale;

	tuneView.frame = bounds;
  tuneView.tune = tune ? [tune dots] : nil;
  tuneView.contentMode = UIViewContentModeRedraw;

#if !TARGET_IPAD
  // I've no idea why this doesn't need to be done on the iPad, but if you do
  // the view appears twice at different scales.
	[scrollView addSubview:tuneView];
#endif
	[mainView addSubview:scrollView];
	[mainView sendSubviewToBack:scrollView];
#if TARGET_IPAD
  mainView.clipsToBounds = YES;
	[mainView addSubview:background];
	[mainView sendSubviewToBack:background];
#endif

  scrollView.contentInset = UIEdgeInsetsMake( 0, 0, 88, 0 );
  scrollView.directionalLockEnabled = YES;

	[scrollView release];

  self.navigationItem.rightBarButtonItem = self.editButtonItem;

  // Set up the arrays of toolbar items we'll need
  NSArray *items = toolbar.items;
  playItems = [NSMutableArray arrayWithArray:items];
  [playItems removeObject:playButton];
  [playItems retain];
  stopItems = [NSMutableArray arrayWithArray:items];
  [stopItems removeObject:pauseButton];
  [stopItems retain];

  abcButton = [[[UIBarButtonItem alloc]
    initWithTitle:@"ABC"
    style:UIBarButtonItemStylePlain 
    target:self 
    action:@selector(editABC)] autorelease];
  [abcButton retain];

  Class uipc = NSClassFromString(@"UIPrintInteractionController");
  printingEnabled = uipc && [uipc isPrintingAvailable];

  if ( !printingEnabled ) {
    [playItems removeObject:printButton];
    [stopItems removeObject:printButton];
  } else {
    [playItems removeObject:printSpacer];
    [stopItems removeObject:printSpacer];
  }

  toolbar.items = stopItems;

#if TARGET_IPAD
  printButton.enabled = NO;
  emailButton.enabled = NO;
  editButton.enabled = NO;
  playButton.enabled = NO;
#endif
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerFinished:) name:@"playerFinished" object:nil];

  messageLayer = [[TextLayer alloc] init];
  messageLayer.position = CGPointMake( self.view.bounds.size.width/2, self.view.bounds.size.height/2 );
  messageLayer.bounds = CGRectMake( 0, 0, 230, 40 );
  messageLayer.opaque = NO;
  [messageLayer setValue:[UIFont systemFontOfSize:22] forKey:@"font"];
  [messageLayer setValue:@"Text" forKey:@"text"];
  [messageLayer setValue:[UIColor whiteColor] forKey:@"textColor"];
  messageLayer.cornerRadius = 16.0f;
  messageLayer.backgroundColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.8f].CGColor;
  [messageLayer setNeedsDisplay];
  messageLayer.needsDisplayOnBoundsChange = YES;
  messageLayer.opacity = 0.0f;
  [self.view.layer addSublayer:messageLayer];
}

#if TARGET_IPAD
- (void)dotsChanged
{
  tuneView.tune = tune.dots;
  [tuneView setNeedsDisplay];
}
#endif

- (void)redrawTune:(id)sender
{
  [tuneView setNeedsDisplay];
}

- (void)showTune:(Tune *)t
{
  [self showTune:t fromPlayList:nil];
}

- (void)showTune:(Tune *)t fromPlayList:(Playlist *)pl
{
  [self showTune:t fromPlayList:nil direction:1];
}

- (void)showTune:(Tune *)aTune fromPlayList:(Playlist *)aPlaylist direction:(int)direction
{
  self.playlist = aPlaylist;
#if 1//TARGET_IPAD
  if ( aTune == nil && self.tune == nil )
    return;

  AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
  appDelegate.currentTune = aTune.primaryKey;

  [self stopPlaying:self];

  self.tune = aTune;

  printButton.enabled = printingEnabled && aTune != nil;
  emailButton.enabled = aTune != nil;
#if TARGET_IPAD
  editButton.enabled = aTune != nil;
#endif
  playButton.enabled = aTune != nil;

  CGRect bounds;
#if 1//Measuring
  numPages = 1;
  if ( tune ) {
    UIGraphicsBeginImageContext( CGSizeMake( 8, 8 ) );
    numPages = [Tune drawTune:tune.dots intoContext:UIGraphicsGetCurrentContext() bounds:CGRectMake(0, 0, 8, 8) scale:1.0f];
    if ( numPages < 1 )
      numPages = 1;
    bounds = CGRectMake( 0, 0, PaperWidth, numPages * PaperHeight );
    UIGraphicsEndImageContext();
  } else
    bounds = CGRectMake( 0, 0, PaperWidth, PaperHeight );
#else
  bounds = CGRectMake( 0, 0, PaperWidth, PaperHeight );
#endif

  TuneView *newView = [[TuneView alloc] initWithFrame:CGRectMake(0.0, 0.0, 1.0, 1.0)];
  newView.delegate = self;
  newView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  newView.scale = 1.0f;

	newView.frame = tuneView.frame;
  newView.tune = aTune ? [aTune dots] : nil;
  newView.contentMode = UIViewContentModeRedraw;

  float scale = scrollView.bounds.size.width / (PaperWidth - 2*kTuneMargin);
  newView.scale = scale;

  CGSize contentSize;
  contentSize.width = PaperWidth * scale;
  contentSize.height = PaperHeight * scale * numPages;

  CGSize scrollFrameSize = scrollView.bounds.size;
  CGPoint frameOrigin;
  frameOrigin.x = (contentSize.width < scrollFrameSize.width) ? ((scrollFrameSize.width - contentSize.width) / 2) : 0;
  frameOrigin.y = (contentSize.height < scrollFrameSize.height) ? ((scrollFrameSize.height - contentSize.height) / 2) : 0;

  [newView setTransform:CGAffineTransformIdentity];
  [newView setFrame:CGRectMake(frameOrigin.x, frameOrigin.y, contentSize.width, contentSize.height)];
  [scrollView setContentSize:contentSize];
  scrollView.contentOffset = CGPointMake(kTuneMargin*scale, 0.0f);

  if ( appDelegate.transitionType != 0 ) {
    if ( appDelegate.transitionType == 3 )
      newView.alpha = 0.0f;
    else if ( appDelegate.transitionType == 2 ) {
      CGRect r = newView.frame;
      r.origin.x += direction*r.size.width;
      newView.frame = r;
    }
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.5f];
  }
  if ( appDelegate.transitionType == 1 )
    [UIView setAnimationTransition:direction == -1 ? UIViewAnimationTransitionCurlDown : UIViewAnimationTransitionCurlUp forView:mainView cache:YES];
  else if ( appDelegate.transitionType == 3 ) {
    tuneView.alpha = 0.0f;
    newView.alpha = 1.0f;
  }
  else if ( appDelegate.transitionType == 2 ) {
    CGRect r = newView.frame;
    r.origin.x -= direction*r.size.width;
    newView.frame = r;

    r = tuneView.frame;
    r.origin.x -= direction*r.size.width;
    tuneView.frame = r;
  }
	[UIView setAnimationDelegate:tuneView];
	[UIView setAnimationDidStopSelector:@selector(removeMeFromParent:finished:context:)];
//  [tuneView removeFromSuperview];
	[scrollView addSubview: newView];
  [tuneView release];
  tuneView = newView;
  if ( appDelegate.transitionType != 0 )
    [UIView commitAnimations];
#endif
}

- (NSString *)currentDots
{
  return self.editing ? editController.text : tune.dots;
}

- (void)showMusic
{
	if ( showingABC )
    [self editABC];
}

- (void)showABC
{
  [self hideSettings];
	if ( !showingABC )
    [self editABC];
}

- (void)editABC
{
#if TARGET_IPAD
  editController = [EditABCController alloc];
  [editController initWithNibName:@"EditABC" bundle:nil];
  editController.editTune = tune;
	[self presentModalViewController:editController animated:YES];
  [editController release];
#else
  if ( !editController ) {
    editController = [EditABCController alloc];
    [editController initWithTitle:tune.title frame:mainView.bounds];
    editController.text = tune.dots;
    [editController retain];
  }

  // If the device was rotated while editing, we don't hear about it...
  if ( showingABC )
    mainView.frame = self.view.bounds;

	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:1];
	[UIView setAnimationTransition:([self.view superview] ? UIViewAnimationTransitionFlipFromRight : UIViewAnimationTransitionFlipFromLeft) forView:self.view cache:YES];
	
	if ( !showingABC ) {
		[editController viewWillAppear:YES];
		[self viewWillDisappear:YES];
		[mainView removeFromSuperview];
		[self.view addSubview:editController.view];
		[self viewDidDisappear:YES];
		[editController viewDidAppear:YES];
    showingABC = YES;
	} else {
    tuneView.tune = editController.text;
    [tuneView setNeedsDisplay];
		[self viewWillAppear:YES];
		[editController viewWillDisappear:YES];
		[editController.view removeFromSuperview];
		[self.view addSubview:mainView];
		[editController viewDidDisappear:YES];
		[self viewDidAppear:YES];
    showingABC = NO;
	}
	[UIView commitAnimations];
#endif
}

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
  [super setEditing:editing animated:animated];
#if TARGET_IPAD
  self.navigationItem.hidesBackButton = editing;
  if ( editing ) {
    [self.navigationItem setLeftBarButtonItem: abcButton animated:YES];
  } else {
    self.navigationItem.leftBarButtonItem = self.navigationItem.backBarButtonItem;
  }
#endif

  if ( editing ) {
    [self showABC];
  } else {
    [self showMusic];
    if ( editController.changed ) {
      saveChangesSheet = [[UIActionSheet alloc]
        initWithTitle:[NSString stringWithFormat:@"Save Changes to '%@'?", tune.title]
        delegate:self
#if TARGET_IPAD
        cancelButtonTitle:@"Don't Save"
        destructiveButtonTitle:@"Save"
#else
        cancelButtonTitle:@"Save"
        destructiveButtonTitle:@"Don't Save"
#endif
        otherButtonTitles:nil
      ];
      [saveChangesSheet showInView:self.view];
    } else {
      [editController release];
      editController = NULL;
    }
  }
}

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
  AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
  if ( actionSheet == saveChangesSheet ) {
#if TARGET_IPAD
    if ( buttonIndex == 2 ) { // Save
#else
    if ( buttonIndex == 1 ) { // Save
#endif
      numTunes = 0;
      parsedTunes = [[NSMutableArray alloc] init];
      [parsedTunes retain];
      TuneParser *parser = [[TuneParser alloc] init];
      parser.delegate = self;
      NSString *dots = editController.text;
      [parser parseString:dots];
      if ( numTunes == 0 ) {
        UIAlertView *alert = [[UIAlertView alloc] 
          initWithTitle: @"Bad ABC" 
          message:@"The ABC does not contain any valid tunes." 
          delegate:nil
          cancelButtonTitle:nil 
          otherButtonTitles:@"OK", nil];
        [alert show];
        [parsedTunes release];
      } else if ( numTunes > 1 ) {
        multipleTunesSheet = [[UIActionSheet alloc]
          initWithTitle:[NSString stringWithFormat:@"Multiple tunes in ABC"]
          delegate:self
          cancelButtonTitle:@"Cancel"
          destructiveButtonTitle:@"Add Extra Tunes"
          otherButtonTitles:nil
        ];
        [multipleTunesSheet showInView:self.view];
      } else {
        Tune *t = (Tune *)[parsedTunes objectAtIndex:0];
        tune.title = t.title;//FIXME-method to do this...
        tune.dots = t.dots;
        tune.rating = t.rating;
        tune.key = t.key;
        tune.numSharps = t.numSharps;
        tune.timeSigNum = t.timeSigNum;
        tune.timeSigDen = t.timeSigDen;
        if ( [tune inDatabase] ) {
          [tune dehydrate];
          [tune hydrate];
        } else
          [appDelegate addTune:tune];
        [parsedTunes release];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"databaseChanged" object:self]; 
      }
    } else { // Don't Save
      tuneView.tune = [tune dots];
      [tuneView setNeedsDisplay];
//      tune.dots = savedDots;
    }
  } else if ( actionSheet == multipleTunesSheet ) {
    if ( buttonIndex == 0 ) {
      Tune *t = (Tune *)[parsedTunes objectAtIndex:0];
      tune.title = t.title;
      tune.dots = t.dots;
      tune.rating = t.rating;
      tune.key = t.key;
      tune.numSharps = t.numSharps;
      tune.timeSigNum = t.timeSigNum;
      tune.timeSigDen = t.timeSigDen;
      if ( [tune inDatabase] )
        [tune dehydrate];
      else
        [appDelegate addTune:tune];

      // Add the extra tunes
      for ( int i = 1; i < [parsedTunes count]; i++ )
        [appDelegate addTune:(Tune *)[parsedTunes objectAtIndex:i]];
      [[NSNotificationCenter defaultCenter] postNotificationName:@"databaseChanged" object:self]; 
    }
    [parsedTunes release];
  }
  [editController release];
  editController = NULL;
}

- (void) parsedTune:(TuneParser *)parser tune:(Tune *)t
{
  numTunes++;
  [parsedTunes addObject:t];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  return YES;
}

- (BOOL)shouldAutorotate {
  return YES;
}

- (NSUInteger)supportedInterfaceOrientations {
  return UIInterfaceOrientationMaskAll;
}

- (void)didReceiveMemoryWarning {
	[super didReceiveMemoryWarning];
}

-(void)dealloc {
	[[NSNotificationCenter defaultCenter] removeObserver:self];
  [scrollView retain];
	[tuneView release];
	[playItems release];
	[stopItems release];
  if ( settingsController )
    [settingsController release];
  [abcButton release];
	[messageLayer release];
#if TARGET_IPAD
  [background release];
#endif
	[super dealloc];
}

- (IBAction)hideStatusBar {
  [[self navigationController] setNavigationBarHidden:YES animated:YES];
  CGRect bounds = self.view.bounds;
  float w = bounds.size.width;
  float h = bounds.size.height;
  scrollView.frame = CGRectMake( 0, 0, w, h );
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:0.30];
  toolbar.alpha = 0.0;
  [self hideSettings];
	[UIView commitAnimations];
  barsHidden = true;
}

- (IBAction)showStatusBar {
  [[self navigationController] setNavigationBarHidden:NO animated:YES];
  CGRect bounds = self.view.bounds;
  float w = bounds.size.width;
  float h = bounds.size.height;
  float t = 44; // toolbar height
  scrollView.frame = CGRectMake( 0, 0, w, h-t );
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:0.30];
  toolbar.alpha = 1.0;
	[UIView commitAnimations];
  barsHidden = false;
}

- (void)viewTapped:(id)sender {
//#if !TARGET_IPAD
  if ( barsHidden )
    [self showStatusBar];
  else
    [self hideStatusBar];
//#endif
}

- (IBAction)zoomToFit:(id)sender
{
  if ( self.tune == nil ) {
    tuneView.frame = scrollView.frame;
    scrollView.contentSize = scrollView.frame.size;
    return;
  }

  float scale = scrollView.bounds.size.width / (PaperWidth - 2*kTuneMargin);
  tuneView.scale = scale;

  tuneView.transform = CGAffineTransformIdentity;
	tuneView.frame = CGRectMake( 0, 0, PaperWidth*scale, numPages*PaperHeight*scale );
	scrollView.contentSize = CGSizeMake( PaperWidth*scale, numPages*PaperHeight*scale );
  scrollView.contentOffset = CGPointMake(kTuneMargin*scale, 0.0f);
  [tuneView setNeedsLayout];
  [tuneView setNeedsDisplay];
  if ( self.tune != nil )
    [self scrollViewDidEndZooming:scrollView withView:self.view atScale:1.0f];
}

- (void)viewDoubleTapped:(id)sender {
#if TARGET_IPAD
  if ( barsHidden )
    [self showStatusBar];
  else
    [self hideStatusBar];
#else
  [self hideStatusBar];
#endif
  [self zoomToFit:sender];
}

#pragma mark UIViewController delegate methods

- (void)viewWillAppear:(BOOL)animated
{
  [self zoomToFit:self];
  if ( self.editing )
    [tuneView setNeedsDisplay]; // Force a refresh if we're editing
}

- (void)viewWillDisappear:(BOOL)animated
{
  [self hideSettings];
  if ( tunePlayer )
    [tunePlayer stop];
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
  return self.tune ? tuneView : nil;
}

#if 1//TARGET_IPAD
- (void)scrollViewDidEndZooming:(UIScrollView *)sv withView:(UIView *)view atScale:(float)scale
{
  float zoomScale = scrollView.zoomScale;
  float oldScale = tuneView.scale;
  scale *= oldScale;

  tuneView.scale = scale;

  CGSize contentSize=scrollView.contentSize;
  CGPoint newContentOffset = scrollView.contentOffset;
  CGSize oldContentViewSize = [tuneView frame].size;

  float xMult = contentSize.width/oldContentViewSize.width;
  float yMult = contentSize.height/oldContentViewSize.height;

  newContentOffset.x *= xMult;
  newContentOffset.y *= yMult;

  float currentMinZoom = scrollView.minimumZoomScale;
  float currentMaxZoom = scrollView.maximumZoomScale;

  float newMinZoom = currentMinZoom/zoomScale;
  float newMaxZoom = currentMaxZoom/zoomScale;

  [scrollView setMinimumZoomScale:1.0];
  [scrollView setMaximumZoomScale:1.0];
  [scrollView setZoomScale:1.0 animated:NO];

  CGSize scrollFrameSize = sv.bounds.size;
  CGPoint frameOrigin;
  frameOrigin.x = (contentSize.width < scrollFrameSize.width) ? ((scrollFrameSize.width - contentSize.width) / 2) : 0;
  frameOrigin.y = (contentSize.height < scrollFrameSize.height) ? ((scrollFrameSize.height - contentSize.height) / 2) : 0;

  [tuneView setTransform:CGAffineTransformIdentity];
  [tuneView setFrame:CGRectMake(frameOrigin.x, frameOrigin.y, contentSize.width, contentSize.height)];

  scrollView.contentSize = contentSize;
  [scrollView setContentOffset:newContentOffset animated:NO];

  [scrollView setMinimumZoomScale:newMinZoom];
  [scrollView setMaximumZoomScale:newMaxZoom];
 
  [tuneView setNeedsDisplay];
}

- (void)scrollViewDidZoom:(UIScrollView *)sv
{
  CGRect innerFrame = tuneView.frame;
  CGRect scrollerBounds = sv.bounds;

  CGPoint scrollViewOffset = sv.contentOffset;

  if ( innerFrame.size.width < scrollerBounds.size.width )
    scrollViewOffset.x = tuneView.center.x - ( scrollerBounds.size.width / 2 );
  if ( innerFrame.size.height < scrollerBounds.size.height )
    scrollViewOffset.y = tuneView.center.y - ( scrollerBounds.size.height / 2 )-44;
  sv.contentOffset = scrollViewOffset;
}
#else
- (void)scrollViewDidEndZooming:(UIScrollView *)sv withView:(UIView *)view atScale:(float)scale
{
  float oldScale = tuneView.scale;
  scale *= oldScale;

#if !TARGET_IPAD
  // Prior to 3.2 the only way to get the UIScrollView to zoom properly is to destroy and recreate the tune view
  [tuneView removeFromSuperview];
  [tuneView release];

  tuneView = [[TuneView alloc] initWithFrame:CGRectMake(0.0, 0.0, 1.0, 1.0)];
  tuneView.delegate = self;
  tuneView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  tuneView.tune = [self currentDots];
  tuneView.contentMode = UIViewContentModeRedraw;
#endif
  tuneView.scale = scale;

  CGSize contentSize;
  contentSize.width = PaperWidth * scale;
  contentSize.height = PaperHeight * scale * numPages;

  CGSize scrollFrameSize = sv.bounds.size;
  CGPoint frameOrigin;
  frameOrigin.x = (contentSize.width < scrollFrameSize.width) ? ((scrollFrameSize.width - contentSize.width) / 2) : 0;
  frameOrigin.y = (contentSize.height < scrollFrameSize.height) ? ((scrollFrameSize.height - contentSize.height) / 2) : 0;

  [tuneView setTransform:CGAffineTransformIdentity];
  [tuneView setFrame:CGRectMake(frameOrigin.x, frameOrigin.y, contentSize.width, contentSize.height)];
  [sv setContentSize:contentSize];
  [sv setNeedsDisplay];
#if !TARGET_IPAD
  [sv addSubview:tuneView];
#endif

  [tuneView setNeedsDisplay];
}
#endif

- (IBAction)startPlaying:(id)sender
{
  if ( !tunePlayer )
    tunePlayer = [TunePlayer tunePlayer];

  if ( [tunePlayer playing] ) {
    toolbar.items = stopItems;
    [tunePlayer stop];
  } else {
    toolbar.items = playItems;
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSString *directory = NSTemporaryDirectory();
    NSString *abcPath = [directory stringByAppendingPathComponent:@"tune.abc"];
    NSString *midiPath = [directory stringByAppendingPathComponent:@"tune.mid"];

    NSError *error = NULL;
    if ( [[tune dots] writeToFile:abcPath atomically:false encoding:NSUTF8StringEncoding error:&error] ) {
      const char *argv[] = {
        "abc2midi",
        [abcPath UTF8String],
        appDelegate.guitarChords ? "-GC" : "-NGC",
        "-quiet",
        "-o",
        [midiPath UTF8String],
        NULL
      };
      extern int abc2midi_main(int argc, const char *argv[]);
      abc2midi_main( 6, argv );
      tunePlayer.filePath = midiPath;
      [tunePlayer play];
    }
  }
}

- (void)playerFinished:(id)sender
{
  toolbar.items = stopItems;
}

- (IBAction)stopPlaying:(id)sender
{
  toolbar.items = stopItems;
  [tunePlayer stop];
}

- (IBAction)pausePlaying:(id)sender
{
  [self startPlaying:sender];
}

- (IBAction)rewindPlaying:(id)sender
{
  [tunePlayer stop];
  [tunePlayer play];
}

- (void)finishedPlaying:(id)sender
{
  toolbar.items = stopItems;
}

- (IBAction)showSettings:(id)sender
{
  if ( showingSettings ) {
    [self hideSettings];
  } else {
    if ( !tunePlayer )
      tunePlayer = [TunePlayer tunePlayer];

    if ( !settingsController )
      settingsController = [[TuneSettingsController alloc] initWithPlayer:tunePlayer];
#if TARGET_IPAD
    settingsController.contentSizeForViewInPopover = settingsController.view.bounds.size;
    [self presentPopoverController:sender controller:settingsController];
#else
    CGSize s1 = self.view.bounds.size;
    CGSize s2 = settingsController.view.bounds.size;
    [settingsController.view setFrame:CGRectOffset(settingsController.view.bounds, (s1.width-s2.width)/2, (s1.height-s2.height-44)/2)];
		settingsController.view.alpha = 0.0;
    [self.view addSubview:settingsController.view];
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.40];
		settingsController.view.alpha = 1.0;
    [UIView commitAnimations];
#endif
    showingSettings = YES;
  }
}

- (void)hideSettings
{
#if !TARGET_IPAD
  if ( settingsController && showingSettings ) {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.40];
		settingsController.view.alpha = 0.0;
    [UIView commitAnimations];
  }
#endif
  showingSettings = NO;
}

- (IBAction)printTune:(id)sender
{
#if TARGET_IPAD
  [self dismissPopoverController];
#endif
  Class uipc = NSClassFromString(@"UIPrintInteractionController");
  Class uipi = NSClassFromString(@"UIPrintInfo");
  UIPrintInteractionController *pic = [uipc sharedPrintController];
  NSData *pdf = [Tune pdfDataForTune:tune.dots];
  if ( pic && [uipc canPrintData:pdf] ) {
      pic.delegate = self;

      UIPrintInfo *printInfo = [uipi printInfo];
      printInfo.outputType = UIPrintInfoOutputGeneral;
      printInfo.jobName = tune.title;
      printInfo.duplex = UIPrintInfoDuplexLongEdge;
      pic.printInfo = printInfo;
      pic.showsPageRange = YES;
      pic.printingItem = pdf;

      void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) =
         ^(UIPrintInteractionController *pic, BOOL completed, NSError *error) {
//           self.content = nil;
           if (!completed && error)
                NSLog(@"FAILED! due to error in domain %@ with error code %u",
                error.domain, error.code);
      };
      if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
        [pic presentFromBarButtonItem:printButton animated:YES
          completionHandler:completionHandler];
      } else {
      [pic presentAnimated:YES completionHandler:completionHandler];
    }
  }
}

- (UIPrintPaper *)printInteractionController:(UIPrintInteractionController *)printInteractionController choosePaper:(NSArray *)paperList
{
  Class uipp = NSClassFromString(@"UIPrintPaper");
  UIPrintPaper *paper;
  // Choose A4 paper if possible
  for (paper in paperList) {
    CGSize size = paper.paperSize;
    if ( fabs( size.width - 595.275574f ) < 1.0f && fabs( size.height - 841.889771f ) < 1.0f )
      return paper;
  }
  return [uipp bestPaperForPageSize:CGSizeMake(PaperWidth, PaperHeight) withPapersFromArray:paperList];
}

- (IBAction)emailTune:(id)sender
{
	Class mailClass = NSClassFromString(@"MFMailComposeViewController");
	if (mailClass != nil)
	{
		if ([mailClass canSendMail])
			[self displayComposerSheet];
		else
			[self launchMailAppOnDevice];
	}
	else
		[self launchMailAppOnDevice];
}

#if TARGET_IPAD
- (IBAction)tunesPressed:(id)sender
{
#if 1
  static MainViewController *controller;
  static UINavigationController *navigationController;
  if ( !controller ) {
    controller = [[MainViewController alloc] initWithNibName:@"MainView" bundle:nil];
    controller.playlist = nil;
    navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
  }
  navigationController.contentSizeForViewInPopover = CGSizeMake(320,480);
  [self presentPopoverController:sender controller:navigationController];
#else
  MainViewController *controller = [[MainViewController alloc] initWithNibName:@"MainView" bundle:nil];
  controller.playlist = nil;
  UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
  navigationController.contentSizeForViewInPopover = CGSizeMake(320,480);
  [self presentPopoverController:sender controller:navigationController];
#endif
}

- (IBAction)tunebooksPressed:(id)sender
{
#if 1
  static PlaylistViewController *controller;
  static UINavigationController *navigationController;
  if ( !controller ) {
    controller = [[PlaylistViewController alloc] initWithNibName:@"PlaylistView" bundle:nil];
    [controller databaseChanged:self];
    navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
  }
  navigationController.contentSizeForViewInPopover = CGSizeMake(320,480);
  [self presentPopoverController:sender controller:navigationController];
#else
  PlaylistViewController *controller = [[PlaylistViewController alloc] initWithNibName:@"PlaylistView" bundle:nil];
  [controller databaseChanged:self];
  UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
  navigationController.contentSizeForViewInPopover = CGSizeMake(320,480);
  [self presentPopoverController:sender controller:navigationController];
  [navigationController release];
  [controller release];
#endif
}

- (IBAction)searchPressed:(id)sender
{
  static SearchViewController *controller;
  static UINavigationController *navigationController;
  if ( !controller ) {
    controller = [[SearchViewController alloc] initWithNibName:@"SearchView" bundle:nil];
    navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
  }
  navigationController.contentSizeForViewInPopover = CGSizeMake(320,480);
  [self presentPopoverController:sender controller:navigationController];
}

//FIXME - chgrid test
#if 0
- (IBAction)searchPressed:(id)sender
{
  TuneGridViewController *controller = [[TuneGridViewController alloc] initWithNibName:nil bundle:nil];
  controller.contentSizeForViewInPopover = CGSizeMake(640,960);
  [self presentPopoverController:sender controller:controller];
  [controller release];
}
#endif

- (IBAction)managePressed:(id)sender
{
  ManageTunesController *controller = [[ManageTunesController alloc] initWithNibName:@"ManageTunes" bundle:nil];
  controller.contentSizeForViewInPopover = CGSizeMake(320,480);
  [self presentPopoverController:sender controller:controller];
  [controller release];
}

- (IBAction)settingsPressed:(id)sender
{
#if FIXME
  SettingsController *controller = [[SettingsController alloc] initWithNibName:@"Settings" bundle:nil];
  controller.contentSizeForViewInPopover = CGSizeMake(320,480);
  [self presentPopoverController:sender controller:controller];
  [controller release];
#else
  UINavigationController *navigationController;
  SettingsController *controller = [[SettingsController alloc] initWithNibName:@"Settings" bundle:nil];
  navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
  navigationController.contentSizeForViewInPopover = CGSizeMake(320,480);
  [self presentPopoverController:sender controller:navigationController];
#endif
}

- (IBAction)editPressed:(id)sender
{
  [self dismissPopoverController];
  [self setEditing:YES animated:YES];
}
#endif

#pragma mark -
#pragma mark Compose Mail

-(void)displayComposerSheet 
{
	MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
	picker.mailComposeDelegate = self;
	
	[picker setSubject:[@"Tune: " stringByAppendingString:tune.title]];

	NSString *emailBody = tune.dots;
	[picker setMessageBody:emailBody isHTML:NO];
  NSData *attachment = [Tune pdfDataForTune:tune.dots];
	[picker addAttachmentData:attachment mimeType:@"application/pdf" fileName:@"tune.pdf"];
	
	[self presentModalViewController:picker animated:YES];
  [picker release];
}

- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error 
{	
	switch (result)
	{
  case MFMailComposeResultCancelled:
    break;
  case MFMailComposeResultSaved:
    break;
  case MFMailComposeResultSent:
    {
    UIAlertView *alert = [[UIAlertView alloc] 
      initWithTitle: @"Success" 
      message:@"Your tune has been sent." 
      delegate:nil
      cancelButtonTitle:nil 
      otherButtonTitles:@"OK", nil];
    [alert show];
    }
    break;
  case MFMailComposeResultFailed:
  default:
    {
    UIAlertView *alert = [[UIAlertView alloc] 
      initWithTitle: @"Unable to send email" 
      message:@"Please make sure that email is configured correctly." 
      delegate:nil
      cancelButtonTitle:nil 
      otherButtonTitles:@"OK", nil];
    [alert show];
    }
    break;
	}
	[self dismissModalViewControllerAnimated:YES];
}


#pragma mark -
#pragma mark Workaround

-(void)launchMailAppOnDevice
{
	NSString *recipients = @"mailto:?&subject=Tune: ";
	NSString *body = [@"&body=" stringByAppendingString:tune.dots];
	
	NSString *email = [NSString stringWithFormat:@"%@%@%@", recipients, tune.title, body];
	email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
	
	[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];
}

#if TARGET_IPAD
- (void)actionSheet:(UIActionSheet*)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex
{
  if( activeActionSheet == actionSheet ) {
    activeActionSheet = nil;
    activeBarButtonItem = nil;
  }
}

- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController*)popoverController
{
  if( activePopoverController == popoverController ) {
    activePopoverController = nil;
    activeBarButtonItem = nil;
  }
  return YES;
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
  [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
  if( activePopoverController )
  {
    if( activeBarButtonItem )
    {
      rotateViewController = [activePopoverController contentViewController];
      [rotateViewController retain];
      rotatePassthroughViews = [activePopoverController passthroughViews];
      [rotatePassthroughViews retain];
      rotateBarButtonItem = activeBarButtonItem;
    }
    [activePopoverController dismissPopoverAnimated:YES];
    activePopoverController = nil;
  }
  else if( activeActionSheet )
  {
    if( activeBarButtonItem )
    {
      rotateActionSheet = activeActionSheet;
      [rotateActionSheet retain];
      rotateBarButtonItem = activeBarButtonItem;
    }
    [activeActionSheet dismissWithClickedButtonIndex:-1 animated:YES];
    activeActionSheet = nil;
  }
  activeBarButtonItem = nil;
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
  [super didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation];
  if( rotateBarButtonItem )
  {
    activeBarButtonItem = rotateBarButtonItem;
    rotateBarButtonItem = nil;
  }
  if( rotateViewController )
  {
    if( activeBarButtonItem )
    {
      activePopoverController = [[UIPopoverController alloc] initWithContentViewController:rotateViewController];
      if( rotatePassthroughViews )
      {
        [activePopoverController setPassthroughViews:rotatePassthroughViews];
      }
      [activePopoverController presentPopoverFromBarButtonItem:activeBarButtonItem
          permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    }
    [rotateViewController release];
    rotateViewController = nil;
    [rotatePassthroughViews release];
    rotatePassthroughViews = nil;
  }
  else if( rotateActionSheet )
  {
    if( activeBarButtonItem )
    {
      activeActionSheet = rotateActionSheet;
      [rotateActionSheet showFromBarButtonItem:activeBarButtonItem animated:YES];
    }
    [rotateActionSheet release];
    rotateActionSheet = nil;
  }
  [self zoomToFit:self];
}

- (void)presentPopoverController:(UIBarButtonItem *)item controller:(UIViewController*)controller
{
  [self dismissPopoverController];
  activePopoverController = [[UIPopoverController alloc] initWithContentViewController:controller];
  [activePopoverController setDelegate:self];
  activeBarButtonItem = item;
  [activePopoverController setPassthroughViews:[NSArray arrayWithObjects:toolbar, nil]];
  [activePopoverController presentPopoverFromBarButtonItem:item
      permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}

- (IBAction)dismissPopoverController
{
  if ( activePopoverController ) {
    [activePopoverController dismissPopoverAnimated:YES];
    activePopoverController = nil;
  }
}
#else
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
  [super didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation];
  [self zoomToFit:self];
}
#endif

- (IBAction)prevTune:(id)sender
{
  if ( !tune.temporary ) {
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSArray *tunes = playlist ? [playlist loadTunes] : appDelegate.tunes;
    int index = [tunes indexOfObject:tune];
    if ( index != NSNotFound && index > 0 ) {
      Tune *t = [tunes objectAtIndex:index-1];
      [t hydrate];
      [self showTune:t fromPlayList:playlist direction:-1];
      [self showMessage:[NSString stringWithFormat:@"Tune %d of %d", index, [tunes count]]];
    }
  }
}

- (IBAction)nextTune:(id)sender
{
  if ( !tune.temporary ) {
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSArray *tunes = playlist ? [playlist loadTunes] : appDelegate.tunes;
    int index = [tunes indexOfObject:tune];
    if ( index != NSNotFound && index < [tunes count]-1 ) {
      Tune *t = [tunes objectAtIndex:index+1];
      [t hydrate];
      [self showTune:t fromPlayList:playlist direction:1];
      [self showMessage:[NSString stringWithFormat:@"Tune %d of %d", index+2, [tunes count]]];
    }
  }
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
  UITouch * t = [touches anyObject];
  if ( [t tapCount] == 1 ) {
    CGPoint location = [t locationInView:mainView];
    CGRect r = mainView.bounds;
    int page = (int)((scrollView.contentOffset.y+1)/(PaperHeight * tuneView.scale));
    int newPage = page;

    if ( location.y < r.size.height/5.0f )
      newPage = MAX( 0, newPage-1 );
    else if ( location.y > r.size.height*4.0f/5.0f )
      newPage = MIN( numPages-1, newPage+1 );
    else if ( location.x < r.size.width/5.0f )
      [self prevTune:self];
    else if ( location.x > r.size.width*4.0f/5.0f )
      [self nextTune:self];
    else
      [self viewTapped:tuneView];

    if ( page != newPage ) {
      CGPoint offset = scrollView.contentOffset;
      offset.y = newPage * PaperHeight * tuneView.scale;
      [scrollView setContentOffset:offset animated:YES];

      [self showMessage:[NSString stringWithFormat:@"Page %d of %d", newPage+1, numPages]];
    }
  } else if( [t tapCount] == 2 )
    [self viewDoubleTapped:tuneView];
}

-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
}

- (void)showMessage:(NSString*)message
{
  [CATransaction begin];
  [CATransaction setAnimationDuration:0];
  messageLayer.position = CGPointMake( self.view.bounds.size.width/2, self.view.bounds.size.height/2 );
  [messageLayer setValue:message forKey:@"text"];
  [messageLayer setNeedsDisplay];
  [CATransaction commit];

  float alpha = 0.9f;
  CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath: @"opacity"];
  alphaAnimation.beginTime = 0.0;
  alphaAnimation.fromValue = [NSNumber numberWithFloat: 0.0f];
  alphaAnimation.toValue = [NSNumber numberWithFloat: alpha];
  alphaAnimation.duration = 0.2;
  CABasicAnimation *alphaAnimation2 = [CABasicAnimation animationWithKeyPath: @"opacity"];
  alphaAnimation2.beginTime = 0.2;
  alphaAnimation2.duration = 0.4;
  alphaAnimation2.fromValue = [NSNumber numberWithFloat: alpha];
  alphaAnimation2.toValue = [NSNumber numberWithFloat: alpha];
  CABasicAnimation *alphaAnimation3 = [CABasicAnimation animationWithKeyPath: @"opacity"];
  alphaAnimation3.beginTime = 0.6;
  alphaAnimation3.duration = 0.2;
  alphaAnimation3.fromValue = [NSNumber numberWithFloat: alpha];
  alphaAnimation3.toValue = [NSNumber numberWithFloat: 0.0f];

  CAAnimationGroup *fadeAnimation = [CAAnimationGroup animation];
  fadeAnimation.delegate = self;
  fadeAnimation.animations = [NSArray arrayWithObjects:alphaAnimation, alphaAnimation2, alphaAnimation3, nil];
  fadeAnimation.duration = 1.0;
  [CATransaction begin];
  [messageLayer addAnimation:fadeAnimation forKey:@"animateOpacity"];
  [CATransaction commit];
}

@end

@implementation TextLayer

@synthesize alignment;

- (id) init
{
  self = [super init];
  if ( self ) {
    alignment = UITextAlignmentCenter;
  }
  return self;
}

- (void)drawInContext:(CGContextRef)theContext
{
  CGContextSaveGState( theContext );
  NSString *text = [self valueForKey:@"text"];
  UIFont *font = [self valueForKey:@"font"];
  CGContextSetRGBFillColor( theContext, 0.8f, 0.8f, 0.8f, 1 );
  UIGraphicsPushContext( theContext );
  CGSize size = [text sizeWithFont:font constrainedToSize:CGSizeMake(0, 0) lineBreakMode:UILineBreakModeWordWrap];
  CGRect r = self.bounds;
  r = CGRectInset( r, 5, 0 );
  r.origin.y += (r.size.height-size.height)/2;
  [text drawInRect:r withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:alignment];
  UIGraphicsPopContext();
  CGContextRestoreGState( theContext );
}

@end
