//
//  Lynkeos 
//  $Id: MyObjectImageList.m,v 1.5 2005/01/27 23:11:25 j-etienne Exp $
//
//  Created by Jean-Etienne LAMIAUD on Wed Sep 24 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.
//

#include "MyObjectImageList.h"
#include "MyDocumentData.h"

#define K_LIST_KEY		@"images"
#define K_SEARCH_ORIGIN_KEY	@"sorigin"
#define K_SEARCH_SIDE_KEY	@"sside"
#define K_ANALYZE_ORIGIN_KEY	@"aorigin"
#define K_ANALYZE_SIDE_KEY	@"aside"
#define K_SELECT_THRESHOLD_KEY	@"selectthr"
#define K_CROP_RECTANGLE_KEY	@"crop"
#define K_SIZE_FACTOR_KEY	@"sizef"
#define K_RAW_STACK_KEY		@"stack"
#define K_UNSHARP_RADIUS_KEY	@"uradius"
#define K_UNSHARP_GAIN_KEY	@"ugain"
#define K_DECONV_RADIUS_KEY	@"dradius"
#define K_DECONV_THRESHOLD_KEY	@"dthreshold"

@implementation MyObjectImageList

//==============================================================================
// Coding
//==============================================================================

- (void)encodeWithCoder:(NSCoder *)encoder
{
   // Encode inherited members
   [super encodeWithCoder:encoder];
   // Align data (reference is saved later)
   [encoder encodePoint:NSPointFromIntegerPoint(_searchSquareOrigin)
                 forKey:K_SEARCH_ORIGIN_KEY];
   [encoder encodeInt:_searchSquareSide forKey:K_SEARCH_SIDE_KEY];
   // Analyze data (min and max quality will be recalculated at load)
   [encoder encodePoint:NSPointFromIntegerPoint(_analyzeSquareOrigin)
                 forKey:K_ANALYZE_ORIGIN_KEY];
   [encoder encodeInt:_analyzeSquareSide forKey:K_ANALYZE_SIDE_KEY];
   [encoder encodeDouble:_selectThreshold forKey:K_SELECT_THRESHOLD_KEY];
   // Stack data
   [encoder encodeRect:NSRectFromIntegerRect(_cropRectangle)
                forKey:K_CROP_RECTANGLE_KEY];
   [encoder encodeInt:_sizeFactor forKey:K_SIZE_FACTOR_KEY];
   // Process data
   [encoder encodeDouble:_dRadius forKey:K_DECONV_RADIUS_KEY];
   [encoder encodeDouble:_dThreshold forKey:K_DECONV_THRESHOLD_KEY];
   [encoder encodeDouble:_uRadius forKey:K_UNSHARP_RADIUS_KEY];
   [encoder encodeDouble:_uGain forKey:K_UNSHARP_GAIN_KEY];
   
   [encoder encodeConditionalObject:_referenceItem forKey:K_REFERENCE_ITEM_KEY];
}

- (id)initWithCoder:(NSCoder *)decoder
{
   [self init];

   // Get inherited members
   if (    [super initWithCoder:decoder] != nil )
   {
      // Align data
      _searchSquareOrigin = MyIntegerPointFromNSPoint([decoder decodePointForKey:K_SEARCH_ORIGIN_KEY]);
      _searchSquareSide = [decoder decodeIntForKey:K_SEARCH_SIDE_KEY];
      // Analyze data
      _analyzeSquareOrigin = MyIntegerPointFromNSPoint([decoder decodePointForKey:K_ANALYZE_ORIGIN_KEY]);
      _analyzeSquareSide = [decoder decodeIntForKey:K_ANALYZE_SIDE_KEY];
      _selectThreshold = [decoder decodeDoubleForKey:K_SELECT_THRESHOLD_KEY];
      // Stack data
      _cropRectangle = MyIntegerRectFromNSRect([decoder decodeRectForKey:K_CROP_RECTANGLE_KEY]);
      _sizeFactor = [decoder decodeIntForKey:K_SIZE_FACTOR_KEY];
      if ( _sizeFactor == 0 )
         _sizeFactor = 1;
      // Process data
      _uRadius = [decoder decodeDoubleForKey:K_UNSHARP_RADIUS_KEY];
      _uGain = [decoder decodeDoubleForKey:K_UNSHARP_GAIN_KEY];
      _dRadius = [decoder decodeDoubleForKey:K_DECONV_RADIUS_KEY];
      _dThreshold = [decoder decodeDoubleForKey:K_DECONV_THRESHOLD_KEY];
      
      _referenceItem = [decoder decodeObjectForKey:K_REFERENCE_ITEM_KEY];
      if ( _referenceItem == nil )
         _referenceItem = [self firstItem];
      
      // Rescan the images quality to get min and max
      [self updateMinMaxQuality];
      
      return( self );
   }
   else
   {
      // File format is not compatible, abort loading
      [self release];
      return( nil );
   }
}

//==============================================================================
// Initializers, Constructors and destructors
//==============================================================================
- (id)init
{
   MyIntegerRect nowhere = {{0,0},{0,0}};
   
   self = [super init];
   
   if (self)
   {
      // Align data
      _referenceItem = nil;
      _searchSquareOrigin = nowhere.origin;
      _searchSquareSide = 0;
      // Analyze data
      _analyzeSquareOrigin = nowhere.origin;
      _analyzeSquareSide = 0;
      _minQuality = HUGE;
      _maxQuality = -1;
      _selectThreshold = 5.0;
      // Stack data
      _cropRectangle = nowhere;
      _sizeFactor = 1;
      // Process data
      _dRadius = 2.5;
      _dThreshold = 1.0;
      _uRadius = 5.0;
      _uGain = 0.0;
   }
   
   return self;
}

// Initialize with V0 file content
- (id) initWithImageListData :(MyImageListData*)data
{
   // Store it in our attributes
   [self initWithArray:[NSMutableArray arrayWithArray :data->_imageList] ];
   [data->_imageList release];
   _searchSquareOrigin = MyIntegerPointFromNSPoint(data->_searchSquareOrigin);
   _searchSquareSide = data->_searchSquareSide;
   _analyzeSquareOrigin = MyIntegerPointFromNSPoint(data->_analyzeSquareOrigin);
   _analyzeSquareSide = data->_analyzeSquareSide;
   _selectThreshold = data->_selectThreshold;
   _cropRectangle = MyIntegerRectFromNSRect(data->_cropRectangle);
   _sizeFactor = (data->_doubleSize ? 2 : 1 );
   _referenceItem = data->_referenceItem;
   if ( _referenceItem == nil )
      _referenceItem = [self firstItem];
   if ( data->_rawStack != nil )
   {
      u_long l = [data->_rawStack length]
#ifdef FLOAT_PIXELS
                 / (sizeof(double)/sizeof(float))
#endif
                 ;
      RGB *tmpStack = (RGB*)malloc( l );
#ifdef FLOAT_PIXELS
      REAL *buf = (REAL*)tmpStack;
      double *source = (double*)[data->_rawStack bytes];
      u_long i;

      for( i = 0; i < l/sizeof(REAL); i++ )
         buf[i] = (REAL)source[i];
#else
      [data->_rawStack getBytes:tmpStack];
#endif
      [data->_rawStack release];
      [self setStack:tmpStack size:l];
   }
   _dRadius = data->_dRadius;
   _dThreshold = data->_dThreshold;
   _uRadius = data->_uRadius;
   _uGain = data->_uGain;
   [self setBlackLevel:data->_blackLevel whiteLevel:data->_whiteLevel];

   // Rescan the images quality to get min and max
   [self updateMinMaxQuality];

   return( self );
}

//==============================================================================
// Read accessors
//==============================================================================
   // Align data
- (MyImageListItem*) referenceItem { return(_referenceItem); }
- (MyIntegerPoint) searchSquareOrigin { return( _searchSquareOrigin ); }
- (u_short) searchSquareSide { return( _searchSquareSide ); }
   // Analyze data
- (MyIntegerPoint) analyzeSquareOrigin { return( _analyzeSquareOrigin ); }
- (u_short) analyzeSquareSide { return( _analyzeSquareSide ); }
- (double) minQuality { return( _minQuality ); }
- (double) maxQuality { return( _maxQuality ); }
- (double) qualityThreshold { return( _selectThreshold ); }
   // Stack data
- (MyIntegerRect) cropRectangle { return( _cropRectangle ); }
- (u_short) sizeFactor { return(_sizeFactor); }
   // Process data
- (double) dRadius { return(_dRadius); }
- (double) dThreshold { return(_dThreshold); }
- (double) uRadius { return(_uRadius); }
- (double) uGain { return(_uGain); }

//==============================================================================
// Write accessors
//==============================================================================
- (BOOL) setReferenceItem :(MyImageListItem*)item
{
   MyImageListItem *refBefore = _referenceItem;
   
   if ( item != nil )
      _referenceItem = item;
   else
      _referenceItem = [self firstItem];
   
   return( _referenceItem != refBefore );
}

- (BOOL) setSearchSquareOrigin :(MyIntegerPoint)o
{
   if ( _searchSquareOrigin.x == o.x && _searchSquareOrigin.y == o.y )
      return( NO );
   
   _searchSquareOrigin = o;
   
   return( YES );
}

- (BOOL) setSearchSquareSide :(u_short)side
{
   if ( side == _searchSquareSide )
      return( NO );
   
   // Search square side has changed, invalidate every living one
   MyImageListItem *item;
   NSEnumerator* list = [self imageEnumerator];
   while ( (item = [list nextObject]) != nil )
      [item invalidateSearchSquare];
   
   // And update the side length
   _searchSquareSide = side;
   
   return( YES );
}

- (BOOL) setAnalyzeSquareOrigin :(MyIntegerPoint)o
{
   if (  _analyzeSquareOrigin.x == o.x && _analyzeSquareOrigin.x == o.x )
      return( NO );
   
   _analyzeSquareOrigin = o;
   
   return( YES );
}

- (BOOL) setAnalyzeSquareSide :(u_short)side
{
   if ( _analyzeSquareSide == side )
      return( NO );
   
   _analyzeSquareSide = side;
   
   return( YES );
}

- (BOOL) setMinQuality :(double)q
{
   _minQuality = q;
   return( YES );
}

- (BOOL) setMaxQuality :(double)q ;
{
   _maxQuality = q;
   return( YES );
}

- (BOOL) setSelectThreshold :(double) t
{
   if ( _selectThreshold == t )
      return( NO );
   
   _selectThreshold = t;
   
   return( YES );
}

- (BOOL) setCropRectangle :(MyIntegerRect)rect
{
   if ( rect.origin.x == _cropRectangle.origin.x &&
        rect.origin.y == _cropRectangle.origin.y &&
        rect.size.width == _cropRectangle.size.width &&
        rect.size.height == _cropRectangle.size.height )
      return( NO );
   
   _cropRectangle = rect;
   
   // Free the stacked image, since its rectangle is lost
   [self setStack:NULL size:0];
   
   return( YES );
}

- (BOOL) setSizeFactor :(u_short)sf
{
   if ( _sizeFactor == sf )
      return( NO );
   
   _sizeFactor = sf;
   
   // Free the stacked image, since its size is lost
   [self setStack:NULL size:0];

   return( YES );
}

- (BOOL) setProcessParameters :(double)dRadius :(double)dThreshold
                              :(double)uRadius :(double)uGain 
{
   if ( _dRadius == dRadius && _dThreshold == dThreshold &&
        _uRadius == uRadius && _uGain == uGain )
      return( NO );
   
   _dRadius = dRadius;
   _dThreshold = dThreshold;
   _uRadius = uRadius;
   _uGain = uGain;
   
   return( YES );
}

//==============================================================================
// Actions
//==============================================================================

- (BOOL) addItem :(MyImageListItem*)item
{
   [super addItem:item];
      
   if ( _referenceItem == nil )
      _referenceItem = [self firstItem];
   
   return( YES );
}

- (BOOL) deleteItem :(MyImageListItem*)item
{
   if ( item == _referenceItem ||
        ( [_referenceItem isMemberOfClass: [MyMovieImage class]] &&
          item == [(MyMovieImage*)_referenceItem getParent]) )
      _referenceItem = nil;
   
   [super deleteItem:item];
   
   if ( _referenceItem == nil )
      _referenceItem = [self firstItem];
   
   return( YES );
}

- (BOOL) changeItemSelection :(MyImageListItem*)item value:(BOOL)v
{
   BOOL changed = [super changeItemSelection:item value:v];

   // Choose a new reference item if it's been deselected
   if ( changed && [_referenceItem getSelectionState] == NSOffState )
      _referenceItem = [self firstItem];
   
   return( YES );
}

- (BOOL) updateMinMaxQuality
{
   MyImageListEnumerator *iter;
   MyImageListItem *item;
   
   _minQuality = HUGE;
   _maxQuality = -1;
   iter = [self imageEnumerator];
   while ( (item = [iter nextObject]) != nil )
   {
      double q = [item getQuality];
      
      if ( q != -1 )
      {
         if ( q < _minQuality )
            _minQuality = q;
         if ( q > _maxQuality )
            _maxQuality = q;
      }
   }
   
   return( YES );
}

- (BOOL) autoSelect :(double)selectThreshold
{
   MyImageListEnumerator* list = [self imageEnumerator];
   MyImageListItem* item;
   BOOL changed = NO;
   
   if ( _selectThreshold == selectThreshold )
      return( NO );
   
   while ( (item = [list nextObject]) != nil )
   {
      if ( selectThreshold > _selectThreshold )
      {
         if ( [item getQuality] < selectThreshold )
         {
            [item setSelected:NO];
            changed = YES;
         }
         
      }
      else
      {
         if ( [item getQuality] >= selectThreshold )
         {
            [item setSelected:YES];
            changed = YES;
         }
      }
   }
   
   _selectThreshold = selectThreshold;
   
   return( changed );
}

@end
