//
//  Lynkeos
//  $Id: MyImageViewSelection.m,v 1.6 2005/01/27 23:09:32 j-etienne Exp $
//
//  Created by Jean-Etienne LAMIAUD on Sat Nov 01 2003.
//  Copyright (c) 2003-2005. Jean-Etienne LAMIAUD
//
// 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 "MyImageView.h"

#define K_CURSRECT_SIZE 10

#define max(a,b) ((a) > (b) ? a : b )
#define min(a,b) ((a) > (b) ? b : a )

static const NSString
*crossImage = @"cross",
*leftImage = @"left",
*rightImage = @"right",
*topImage = @"top",
*bottomImage = @"bottom",
*topLeftImage = @"topLeft",
*topRightImage = @"topRight",
*bottomLeftImage = @"bottomLeft",
*bottomRightImage = @"bottomRight",
*insideImage = @"hand";

static const NSPoint
crossSpot = {8,8},
leftSpot = {1,7},
rightSpot = {14,7},
topSpot = {7,1},
bottomSpot = {7,14},
topLeftSpot = {1,1},
topRightSpot = {14,1},
bottomLeftSpot = {1,14},
bottomRightSpot = {14,14},
insideSpot = {8,8};

// Utility function to construct the new selection
static short processDrag( long mouse, u_short origin, u_short maximum, 
                          bool upright )
{
   long val;
   
   if ( upright )
      val = mouse > origin ? mouse : origin;
   else
      val = mouse > origin ? origin : mouse;
   
   if ( val < 0.0 )
      val = 0.0;
   if ( val > maximum )
      val = maximum;
   
   return( val );
}

@interface MyImageView(SelectionPrivate)
- (void) initCursorRectangles;

- (void) processCursorRect :(NSRect*)r cursor:(NSCursor*)cur
                          x:(float)x y:(float)y w:(float)w h:(float)h visible:(NSRect)v ;
@end

@implementation MyImageView(SelectionPrivate)

- (void) initCursorRectangles
{
   NSRect vide = { {0.0,0.0}, {0.0,0.0} };
   
   _left = vide;
   _right = vide;
   _top = vide;
   _bottom = vide;
   _topLeft = vide;
   _topRight = vide;
   _bottomLeft = vide;
   _bottomRight = vide;
   _inside = vide;
}

- (void) processCursorRect :(NSRect*)r cursor:(NSCursor*)cur
                          x:(float)x y:(float)y w:(float)w h:(float)h visible:(NSRect)v
{
   r->origin.x = x;
   r->origin.y = y;
   r->size.width = w;
   r->size.height = h;
   
   *r = NSIntersectionRect(*r,v);
   if ( r->size.width > 0.0 && r->size.height > 0.0 )
      [self addCursorRect:NSIntersectionRect(*r,v) cursor:cur];
}

@end

@implementation MyImageView(Selection)

- (void) initCursors
{
   _resizable = YES;	// In a perfect world it should not be initialized here

   [self initCursorRectangles];

   _crossCursor = [[[NSCursor alloc] initWithImage:
                                     [NSImage imageNamed: (NSString*)crossImage]
                                           hotSpot:crossSpot] retain];
   _leftCursor = [[[NSCursor alloc] initWithImage:
                                       [NSImage imageNamed:(NSString*)leftImage]
                                          hotSpot:leftSpot] retain];
   _rightCursor = [[[NSCursor alloc] initWithImage:
                                      [NSImage imageNamed:(NSString*)rightImage]
                                           hotSpot:rightSpot] retain];
   _topCursor = [[[NSCursor alloc] initWithImage:
                                        [NSImage imageNamed:(NSString*)topImage]
                                         hotSpot:topSpot] retain];
   _bottomCursor = [[[NSCursor alloc] initWithImage:
                                     [NSImage imageNamed:(NSString*)bottomImage]
                                            hotSpot:bottomSpot] retain];
   _topLeftCursor = [[[NSCursor alloc] initWithImage:
                                    [NSImage imageNamed:(NSString*)topLeftImage]
                                             hotSpot:topLeftSpot] retain];
   _topRightCursor = [[[NSCursor alloc] initWithImage:
                                   [NSImage imageNamed:(NSString*)topRightImage]
                                              hotSpot:topRightSpot] retain];
   _bottomLeftCursor = [[[NSCursor alloc] initWithImage:
                                 [NSImage imageNamed:(NSString*)bottomLeftImage]
                                                hotSpot:bottomLeftSpot] retain];
   _bottomRightCursor = [[[NSCursor alloc] initWithImage:
                                [NSImage imageNamed:(NSString*)bottomRightImage]
                                               hotSpot:bottomRightSpot] retain];
   _insideCursor = [[[NSCursor alloc] initWithImage:
                                    [NSImage imageNamed:(NSString*)insideImage]
                                            hotSpot:insideSpot] retain];
}

- (MyIntegerRect) getSelection ;
{
   if ( _selection.size.width != 0 && _selection.size.height != 0 )
      return( _selection );
   else
      return( MyIntegerRectFromNSRect([self bounds]) );
}

- (void) setSelection :(MyIntegerRect)selection resizable:(bool)resize
{
   _selection = selection;
   _resizable = resize;
   [self setNeedsDisplay:YES];
   [[self window] resetCursorRects];
}

-  (void) mouseDown:(NSEvent *)theEvent
{
   NSPoint o = [self convertPoint:[theEvent locationInWindow] fromView:nil];
   
   if( [self mouse:o inRect:_bottomLeft] )
   {
      _selectionOrigin.x = _selection.origin.x + _selection.size.width;
      _selectionOrigin.y = _selection.origin.y + _selection.size.height;
      _lastPoint = _selection.origin;
      _selectMode = SelNormal;
   }
   else if( [self mouse:o inRect:_bottomRight] )
   {
      _selectionOrigin.x = _selection.origin.x;
      _selectionOrigin.y = _selection.origin.y + _selection.size.height;
      _lastPoint.x = _selection.origin.x + _selection.size.width;
      _lastPoint.y = _selection.origin.y;
      _selectMode = SelNormal;
   }
   else if( [self mouse:o inRect:_topLeft] )
   {
      _selectionOrigin.x = _selection.origin.x + _selection.size.width;
      _selectionOrigin.y = _selection.origin.y;
      _lastPoint.x = _selection.origin.x;
      _lastPoint.y = _selection.origin.y + _selection.size.height;
      _selectMode = SelNormal;
   }
   else if( [self mouse:o inRect:_topRight] )
   {
      _selectionOrigin = _selection.origin;
      _lastPoint.x = _selection.origin.x + _selection.size.width;
      _lastPoint.y = _selection.origin.y + _selection.size.height;
      _selectMode = SelNormal;
   }
   else if( [self mouse:o inRect:_left] )
   {
      _selectionOrigin.x = _selection.origin.x + _selection.size.width;
      _selectionOrigin.y = _selection.origin.y;
      _lastPoint.x = _selection.origin.x;
      _lastPoint.y = _selection.origin.y + _selection.size.height;
      _selectMode = SelH;
   }
   else if( [self mouse:o inRect:_right] )
   {
      _selectionOrigin = _selection.origin;
      _lastPoint.x = _selection.origin.x + _selection.size.width;
      _lastPoint.y = _selection.origin.y + _selection.size.height;
      _selectMode = SelH;
   }
   else if( [self mouse:o inRect:_bottom] )
   {
      _selectionOrigin.x = _selection.origin.x;
      _selectionOrigin.y = _selection.origin.y + _selection.size.height;
      _lastPoint.x = _selection.origin.x + _selection.size.width;
      _lastPoint.y = _selection.origin.y;
      _selectMode = SelV;
   }
   else if( [self mouse:o inRect:_top] )
   {
      _selectionOrigin = _selection.origin;
      _lastPoint.x = _selection.origin.x + _selection.size.width;
      _lastPoint.y = _selection.origin.y + _selection.size.height;
      _selectMode = SelV;
   }
   else if( [self mouse:o inRect:_inside] )
   {
      _selectionOrigin = _selection.origin;
      _lastPoint = MyIntegerPointFromNSPoint(o);
      _selectMode = SelMove;
   }
   else if ( _resizable )
   {
      _selectionOrigin = MyIntegerPointFromNSPoint(o);
      _lastPoint = _selectionOrigin;
      _selection.origin = _selectionOrigin;
      _selection.size.width = 0;
      _selection.size.height = 0;
      _selectMode = SelNormal;
      [self setNeedsDisplay:YES];
   }
}

-  (void) mouseDragged:(NSEvent *)theEvent
{
   NSPoint where = [self convertPoint:[theEvent locationInWindow] fromView:nil];
   NSRect inval;
   MyIntegerRect prevSelection;
   float delta;
   
   if ( _selectMode != SelNone  &&
        ((u_short)where.x != _lastPoint.x || (u_short)where.y != _lastPoint.y) )
   {
      //Adjust scroll to follow mouse if needed
      [self autoscroll:theEvent];
      
      prevSelection = _selection;
      
      switch ( _selectMode )
      {
         case SelNormal :
            _selection.origin.x = processDrag( where.x, _selectionOrigin.x, 
                                               _imageSize.width, false );
            _selection.size.width = processDrag( where.x, _selectionOrigin.x, 
                                                 _imageSize.width, true )
                                    - _selection.origin.x;
            _selection.origin.y = processDrag( where.y, _selectionOrigin.y, 
                                               _imageSize.height, false );
            _selection.size.height = processDrag( where.y, _selectionOrigin.y, 
                                                  _imageSize.height, true )
                                     - _selection.origin.y;
            break;
         case SelH :
            _selection.origin.x = processDrag( where.x, _selectionOrigin.x, _imageSize.width, false );
            _selection.size.width = processDrag( where.x, _selectionOrigin.x, _imageSize.width, true )
               - _selection.origin.x;
            break;
         case SelV :
            _selection.origin.y = processDrag( where.y, _selectionOrigin.y, _imageSize.height, false );
            _selection.size.height = processDrag( where.y, _selectionOrigin.y, _imageSize.height, true )
               - _selection.origin.y;
            break;
         case SelMove :
            delta = where.x - _lastPoint.x;
            if ( delta < - _selection.origin.x )
               delta = -_selection.origin.x;
            else if ( delta > _imageSize.width - _selection.origin.x - _selection.size.width )
               delta = _imageSize.width - _selection.origin.x - _selection.size.width;
               _selection.origin.x += delta;
            delta = where.y - _lastPoint.y;
            if ( delta < -_selection.origin.y )
               delta = -_selection.origin.y;
            else if ( delta > _imageSize.height - _selection.origin.y - _selection.size.height )
               delta = _imageSize.height - _selection.origin.y - _selection.size.height;
               _selection.origin.y += delta;
            break;
         default:
            break;
      }
      
      // Invalidate what needs to be redrawn
      if ( _selection.origin.x != prevSelection.origin.x )
      {
         inval.origin.x = min( _selection.origin.x, prevSelection.origin.x ) - 1;
         inval.size.width = max( _selection.origin.x, prevSelection.origin.x ) + 1
            - inval.origin.x;
         inval.origin.y = min( _selection.origin.y, prevSelection.origin.y ) - 1;
         inval.size.height = max( _selection.origin.y + _selection.size.height,
                                  prevSelection.origin.y + prevSelection.size.height ) + 1
            - inval.origin.y;
         [self setNeedsDisplayInRect:inval];
      }
      if ( _selection.origin.x + _selection.size.width != prevSelection.origin.x + prevSelection.size.width )
      {
         inval.origin.x = min( _selection.origin.x + _selection.size.width,
                               prevSelection.origin.x + prevSelection.size.width ) - 1;
         inval.size.width = max( _selection.origin.x + _selection.size.width,
                                 prevSelection.origin.x + prevSelection.size.width ) + 1
            - inval.origin.x;
         inval.origin.y = min( _selection.origin.y, prevSelection.origin.y ) - 1;
         inval.size.height = max( _selection.origin.y + _selection.size.height,
                                  prevSelection.origin.y + prevSelection.size.height ) + 1
            - inval.origin.y;
         [self setNeedsDisplayInRect:inval];
      }
      if ( _selection.origin.y != prevSelection.origin.y )
      {
         inval.origin.x = min( _selection.origin.x, prevSelection.origin.x ) - 1;
         inval.size.width = max( _selection.origin.x + _selection.size.width,
                                 prevSelection.origin.x + prevSelection.size.width ) + 1
            - inval.origin.x;
         inval.origin.y = min( _selection.origin.y, prevSelection.origin.y ) - 1;
         inval.size.height = max( _selection.origin.y, prevSelection.origin.y ) + 1
            - inval.origin.y;
         [self setNeedsDisplayInRect:inval];
      }
      if ( _selection.origin.y + _selection.size.height != prevSelection.origin.y + prevSelection.size.height )
      {
         inval.origin.x = min( _selection.origin.x, prevSelection.origin.x ) - 1;
         inval.size.width = max( _selection.origin.x + _selection.size.width,
                                 prevSelection.origin.x + prevSelection.size.width ) + 1
            - inval.origin.x;
         inval.origin.y = min( _selection.origin.y + _selection.size.height,
                               prevSelection.origin.y + prevSelection.size.height ) - 1;
         inval.size.height = max( _selection.origin.y + _selection.size.height,
                                  prevSelection.origin.y + prevSelection.size.height ) + 1
            - inval.origin.y;
         [self setNeedsDisplayInRect:inval];
      }
      _lastPoint = MyIntegerPointFromNSPoint(where);
   }
}

-  (void) mouseUp:(NSEvent *)theEvent
{
   if ( _selectMode != SelNone )
   {
      _selectMode = SelNone;
      
      // Inform the delegate
      [_delegate myImageView:self selectionRectDidChange:_selection :[theEvent modifierFlags]];
      
      // Set up all the cursor rectangles
      [[self window] invalidateCursorRectsForView:self];
      
      [self setNeedsDisplay:YES];
   }
}

- (void)resetCursorRects
{
   NSRect v = [self visibleRect];
   float s = K_CURSRECT_SIZE/_zoom;
   
   [self initCursorRectangles];
   
   if ( _selection.size.width != 0 && _selection.size.height != 0 )
   {
      if ( _resizable )
      {
         [self processCursorRect:&_bottomLeft cursor:_bottomLeftCursor
                               x:_selection.origin.x - s/2.0 y:_selection.origin.y - s/2.0
                               w:s h:s visible:v];
         [self processCursorRect:&_left cursor:_leftCursor
                               x:_selection.origin.x - s/2.0 y:_selection.origin.y + s/2.0
                               w:s h:_selection.size.height - s visible:v];
         [self processCursorRect:&_topLeft cursor:_topLeftCursor
                               x:_selection.origin.x - s/2.0
                               y:_selection.origin.y + _selection.size.height - s/2.0
                               w:s h:s visible:v];
         [self processCursorRect:&_top cursor:_topCursor
                               x:_selection.origin.x + s/2.0
                               y:_selection.origin.y + _selection.size.height - s/2.0
                               w:_selection.size.width - s h:s visible:v];
         [self processCursorRect:&_topRight cursor:_topRightCursor
                               x:_selection.origin.x + _selection.size.width - s/2.0
                               y:_selection.origin.y + _selection.size.height - s/2.0
                               w:s h:s visible:v];
         [self processCursorRect:&_right cursor:_rightCursor
                               x:_selection.origin.x + _selection.size.width - s/2.0
                               y:_selection.origin.y + s/2.0
                               w:s h:_selection.size.height - s visible:v];
         [self processCursorRect:&_bottomRight cursor:_bottomRightCursor
                               x:_selection.origin.x + _selection.size.width - s/2.0
                               y:_selection.origin.y - s/2.0
                               w:s h:s visible:v];
         [self processCursorRect:&_bottom cursor:_bottomCursor
                               x:_selection.origin.x + s/2.0 y:_selection.origin.y - s/2.0
                               w:_selection.size.width - s h:s visible:v];
      }
      [self processCursorRect:&_inside cursor:_insideCursor
                            x:_selection.origin.x + s/2.0 y:_selection.origin.y + s/2.0
                            w:_selection.size.width - s h:_selection.size.height - s visible:v];
   }
   
   if ( v.size.width != 0 && v.size.height != 0 )
      [self addCursorRect:v cursor:_crossCursor];
   
}

@end
