/*
 * Copyright (C) 2005  Stefan Kleine Stegemann
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#import "SinglePageView.h"
#import "NSView+Scrolling.h"
#import "ExtendedScrollView.h"
#import "Preferences.h"
#include <float.h>

/**
 * Non-Public methods.
 */
@interface SinglePageView (Private)
- (NSSize) _zoomedSize;
- (void) _drawPageBoundaries;
- (void) _applyResizePolicy;
- (NSArray*) _buildZoomFactors;
- (void) _documentPageDidChange: (NSNotification*)aNotification;
@end


@implementation SinglePageView

- (id) initWithFrame: (NSRect)aFrame
{
   self = [super initWithFrame: aFrame];
   if (self)
   {
      document   = nil;
      zoom       = [[ZoomFactorRange alloc] initWithFactors: [self _buildZoomFactors]];
      [zoom setDelegate: self];
      resizePolicy = ResizePolicyNone;
      paperColor = nil;
      [self setPaperColor: [NSColor whiteColor]];
   }

   return self;
}

- (void) dealloc
{
   NSLog(@"dealloc SinglePageView");
   [self setDocument: nil];
   [self setPaperColor: nil];
   [zoom release];
   [super dealloc];
}

- (void) setDocument: (Document*)aDocument
{
   [[NSNotificationCenter defaultCenter] removeObserver: self];
   [document release];
   
   document = [aDocument retain];
   
   if (document)
   {
      [self setFrameSize: [self _zoomedSize]];
      [self displayContentTop];
      
      [[NSNotificationCenter defaultCenter]
            addObserver: self
               selector: @selector(_documentPageDidChange:)
                   name: kDocumentPageChangedNotification
                 object: document];
   }
}

- (NSSize) preferredSize
{
   return [self _zoomedSize];
}

- (void) setPaperColor: (NSColor*)aColor
{
   [paperColor release];
   paperColor = [aColor retain];
   [self setNeedsDisplay: YES];
}

- (NSColor*) paperColor
{
   return paperColor;
}

- (void) setZoom: (float)aFactor
{
   [zoom setFactor: [ZoomFactor factorWithValue: aFactor]];
}

- (void) zoomIn
{
   [zoom increment];
}

- (void) zoomOut
{
   [zoom decrement];
}

- (float) zoom
{
   return [[zoom factor] value];
}

- (NSSize) zoomContentToFit: (NSSize)aSize
{
   NSSize normalized = [document pageSize];

   float xFactor = aSize.width / normalized.width;
   float yFactor = aSize.height / normalized.height;
   
   float factor = (xFactor < yFactor ? xFactor : yFactor);
   [zoom setFactor: [ZoomFactor factorWithValue: 100.0 * factor]];

   return [self _zoomedSize];
}

- (void) setResizePolicy: (ResizePolicy)aPolicy
{
   resizePolicy = aPolicy;
   [self _applyResizePolicy];
}

- (ResizePolicy) resizePolicy
{
   return resizePolicy;
}

- (void) scrollUpOnePage
{
   if (![self scrollPageUp])
   {
      if ([document previousPage])
      {
         [self scrollToBottom];
      }
   }
}

- (void) scrollDownOnePage
{
   if (![self scrollPageDown])
   {
      [document nextPage];
   }
}

- (void) scrollUpOneLine
{
   [self scrollLineUp]; // NSView+Scrolling
}

- (void) scrollDownOneLine
{
   [self scrollLineDown]; // NSView+Scrolling
}

- (void) scrollLeftOneLine
{
   [self scrollLineLeft]; // NSView+Scrolling
}

- (void) scrollRightOneLine
{
   [self scrollLineRight]; // NSView+Scrolling
}

- (void) displayContentTop
{
   [self scrollToTop]; // NSView+Scrolling
}

- (void) displayContentBottom
{
   [self scrollToBottom]; // NSView+Scrolling
}

- (void) displayContentLeft
{
   [self scrollToLeftEdge]; // NSView+Scrolling
}

- (void) displayContentRight
{
   [self scrollToRightEdge]; // NSView+Scrolling
}

- (void) update
{
   [self setNeedsDisplay: YES];
   [[self enclosingScrollView] setNeedsDisplay: YES];
}

- (BOOL) isOpaque
{
   return ([self paperColor] != nil);
}

- (void) drawRect: (NSRect)aRect
{
   NSRect contentRect;
   
   contentRect = NSMakeRect(0,  0, [self frame].size.width, [self frame].size.height);

   // background
   if ([self isOpaque])
   {
      [[self paperColor] set];
      [NSBezierPath fillRect: contentRect];
   }

   // page   
   [document drawPageAtPoint: contentRect.origin scale: [[zoom factor] asScale]];

   // mark page boundaries
   if ([[Preferences sharedPrefs] markPageBoundaries])
   {
      [self _drawPageBoundaries];
   }
}

- (void) viewDidMoveToSuperview
{
   if ([self enclosingScrollView])
   {
      [[self enclosingScrollView] setVerticalPageScroll: 200];
      // TODO: possibly use a ratio that is proportional to
      //       the size of the page?
   }
}

- (void) scrollViewDidResize: (NSScrollView*)aScrollView
{
   NSLog(@"scrollViewDidResize");
   [self _applyResizePolicy];
}

- (void) zoomFactorChanged: (ZoomFactorRange*)aRange
             withOldFactor: (ZoomFactor*)anOldFactor
{
   ZoomFactor* oldFactor = (anOldFactor != nil ? anOldFactor : [aRange factor]);
   
   NSRect oldVRect = [[self enclosingScrollView] documentVisibleRect];
   NSRect normRect = [oldFactor normalizeRect: oldVRect];

   [self setFrameSize: [self _zoomedSize]];

   NSRect scaledRect = [[zoom factor] translateRect: normRect];
   NSRect vRect = [[self enclosingScrollView] documentVisibleRect];

   NSPoint origin = NSMakePoint(NSMidX(scaledRect) - (NSWidth(vRect) / 2),
                                NSMidY(scaledRect) - (NSHeight(vRect) / 2));
   [self scrollPoint: origin];

   [[NSNotificationCenter defaultCenter]
         postNotificationName: kZoomFactorChangedNotification
                       object: self];
}

@end

/* ----------------------------------------------------- */
/*  Category Private                                     */
/* ----------------------------------------------------- */

@implementation SinglePageView (Private)

- (NSSize) _zoomedSize
{
   return [[zoom factor] translateSize: [document pageSize]];
}

- (void) _drawPageBoundaries
{
   // draw only if the enclosing clipview is bigger than
   // this view
   NSRect clipRect = [[[self enclosingScrollView] contentView] bounds];
   NSRect selfRect = [self frame];
   float diffWidth = NSWidth(clipRect) - NSWidth(selfRect);
   float diffHeight = NSHeight(clipRect) - NSHeight(selfRect);
   if ((diffWidth <= 2) && (diffHeight <= 2))
   {
      return;
   }
   
   // draw edges
   [[NSColor lightGrayColor] set];

   NSSize pageSize = [self _zoomedSize];
   float minX = 1.0;
   float minY = 1.0;
   float maxX = (minX + pageSize.width) - 2.0;
   float maxY = (minY + pageSize.height) - 2.0;

   NSBezierPath* path = [NSBezierPath bezierPath];
   [path setLineWidth: 1.0];

   // lower left edge
   [path moveToPoint: NSMakePoint(minX, minY + 10)];
   [path lineToPoint: NSMakePoint(minX, minY)];
   [path lineToPoint: NSMakePoint(minX + 10, minY)];
   // lower right edge
   [path moveToPoint: NSMakePoint(maxX - 10, minY)];
   [path lineToPoint: NSMakePoint(maxX, minY)];
   [path lineToPoint: NSMakePoint(maxX, minY + 10)];
   // upper left edge
   [path moveToPoint: NSMakePoint(minX, maxY - 10)];
   [path lineToPoint: NSMakePoint(minX, maxY)];
   [path lineToPoint: NSMakePoint(minX + 10, maxY)];
   // upper right edge
   [path moveToPoint: NSMakePoint(maxX, maxY - 10)];
   [path lineToPoint: NSMakePoint(maxX, maxY)];
   [path lineToPoint: NSMakePoint(maxX - 10, maxY)];

   [path stroke];
}

- (void) _applyResizePolicy
{
   NSSize contentSize = [[self enclosingScrollView] contentSize];

   switch (resizePolicy)
   {
      case ResizePolicyFitWidth:
         [self zoomContentToFit: NSMakeSize(contentSize.width, FLT_MAX)];
         break;
      case ResizePolicyFitHeight:
         [self zoomContentToFit: NSMakeSize(FLT_MAX, contentSize.height)];
         break;
      case ResizePolicyFitPage:
         [self zoomContentToFit: contentSize];
         break;
      case ResizePolicyNone:
      default:
         // do nothing
         break;
   }
}

- (NSArray*) _buildZoomFactors
{
   return [NSArray arrayWithObjects:
              [ZoomFactor factorWithValue: 10.0],
              [ZoomFactor factorWithValue: 25.0],
              [ZoomFactor factorWithValue: 33.0],
              [ZoomFactor factorWithValue: 50.0],
              [ZoomFactor factorWithValue: 66.0],
              [ZoomFactor factorWithValue: 75.0],
              [ZoomFactor factorWithValue: 88.0],
              [ZoomFactor factorWithValue: 100.0],
              [ZoomFactor factorWithValue: 115.0],
              [ZoomFactor factorWithValue: 125.0],
              [ZoomFactor factorWithValue: 150.0],
              [ZoomFactor factorWithValue: 175.0],
              [ZoomFactor factorWithValue: 200.0],
              [ZoomFactor factorWithValue: 250.0],
              [ZoomFactor factorWithValue: 300.0],
              [ZoomFactor factorWithValue: 350.0],
              nil];
}

- (void) _documentPageDidChange: (NSNotification*)aNotification
{
   // scroll to the top of the page whenever a new page
   // is displayed
   [self displayContentTop];
}

@end
