/*!
  @header FTGenericTransactionOptimizer
  @abstract Module of FT
 
  @availability OS X, GNUstep
  @copyright 2004, 2005, 2006 Free Software Foundation, Inc.
  
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA

  <pre>
  -------------------------------------------------------------------------
  Modification history

  24.07.05 ola     initial version
  23.08.06 ola     license changed
  -------------------------------------------------------------------------
  </pre>
*/

#include <FT/FTGenericTransactionOptimizer.h>
#include <FT/FTTransactionImpl.h>
#include <FT/FTGraphImplTransactions.h>
#include <FT/FTLogging.h>

/********************************************** __FTAnalyseTransactionSteps */
@interface __FTAnalyseTransactionSteps : FTObject {
  @private
    FTTransactionImpl *transaction;
    
    /**
     * Maps nodeId's to arrays of FTGraphImplTransactions
     * So this dictionary may be used to find out redundant
     * FTGraphImplTransactions for each node
     */
    NSMutableDictionary *nodeIdToArrayOfSteps;
}

- initForTransaction: (FTTransactionImpl *) transaction;
- (void) dealloc;


/** 
 * This method creates internal structures used to optimize 
 * the transactions.
 * Called by initForTransaction.
 */
- buildIndex;


/**
 * starts the optimization
 */
- (FTTransactionImpl *) optimize;


/*!
 * @method removeDeleteUpdates
 * @abstract Internal methods which removes all operations related to a node
 *   if there exists a delete operation for this node
 * @result self
 */
- removeDeleteUpdates;


/**
 * Called by optimize in order to remove redundant 
 * FTGraphImplTransactions for each node
 */
- removeRedundantGraphTransactions;
@end


@implementation __FTAnalyseTransactionSteps
- initForTransaction: (FTTransactionImpl *) aTransaction {
  self = [super init];
  
  self->transaction = [aTransaction retain];
  self->nodeIdToArrayOfSteps = [[NSMutableDictionary alloc] init];
  
  [self buildIndex];
  return self;  
}

- (void) dealloc {
  [self->transaction release];
  [self->nodeIdToArrayOfSteps release];
  
  [super dealloc];
}

- buildIndex {
  NSEnumerator *steps;
  id currentStep;
  steps = [[transaction transactionSteps] objectEnumerator];

  while( (currentStep = [steps nextObject]) ) {
    if( [currentStep isKindOfClass: [FTTransactionStepAndContext class]] ) {
      id <FTTransactionStep> step;
      FTTransactionContext *context;
    
      step = [((FTTransactionStepAndContext *) currentStep) transactionStep];
      context = [((FTTransactionStepAndContext *) currentStep) 
        transactionContext];
      
      if( [step isKindOfClass: [FTGraphImplTransactions class]] ) {
        /**
         * Extract node and map node --> operation in order to find 
         * redundancies.
         */
        FTNodeImpl *node = [((FTGraphImplTransactions *) step) 
          nodeFromContext: context];
        if( nil != node ) {
          /* find node in dictionary. If found, then add the step to the
           * existing list. Otherwise create the list: */
          NSMutableArray *stepsOfNode = 
            [self->nodeIdToArrayOfSteps objectForKey: [node nodeId]];
            
          if( nil == stepsOfNode ) {
            stepsOfNode = [[NSMutableArray alloc] init];
            [self->nodeIdToArrayOfSteps 
              setObject: stepsOfNode
              forKey: [node nodeId]];
            [stepsOfNode release];
          }
          
          [stepsOfNode addObject: currentStep];
        }
      }      
    }
  }

  return self;
}


- (FTTransactionImpl *) optimize {
  [self removeRedundantGraphTransactions];
  [self removeDeleteUpdates];
  
  return self->transaction;
}


- removeDeleteUpdates {
  NSEnumerator *keyEnumerator;
  id nextKey;
  
  if( [[FTLogging coreLog] isTraceEnabled] ) {    
    [[FTLogging coreLog]
      trace: @"__FTAnalyseTransactionSteps::removeDeleteUpdates" ];
  }
  
  keyEnumerator = [self->nodeIdToArrayOfSteps keyEnumerator];
  while( (nextKey = [keyEnumerator nextObject]) ) {
    NSArray *steps 
      = (NSArray *) [self->nodeIdToArrayOfSteps objectForKey: nextKey];
    if( [steps count] > 1 ) {
      int i;
      BOOL deletionFound = NO;
      
      for( i = 0; i < [steps count]; i++ ) {
        FTTransactionStepAndContext *step
          = (FTTransactionStepAndContext *) [steps objectAtIndex: i];
          
        if( !deletionFound ) {
          /* look for deletion operation: */
          if( __FTGRAPHIMPL_TX_NODE_REMOVE
             == [FTGraphImplTransactions 
                operationIdFromContext: [step transactionContext]] ) {
             int j;

             deletionFound = YES;
             
             // now disable all previous visited operations
             for( j = 0; j < i; j++ ) {
               FTGraphImplTransactions *graphTransactionStep 
                 = (FTGraphImplTransactions *) [step transactionStep];
              
              [graphTransactionStep enableOperation: NO];
             }
          }
        } else {
          /* disable operation since a deletion operation has been 
           * detected before
           */
          FTGraphImplTransactions *graphTransactionStep 
            = (FTGraphImplTransactions *) [step transactionStep];
              
          [graphTransactionStep enableOperation: NO]; 
        }
      }
    }
  }
  
  return self;
}


- removeRedundantGraphTransactions {
  NSEnumerator *keyEnumerator;
  id nextKey;
  
  if( [[FTLogging coreLog] isTraceEnabled] ) {    
    [[FTLogging coreLog]
      trace: @"__FTAnalyseTransactionSteps::removeRedundantGraphTransactions" ];
  }
  
  keyEnumerator = [self->nodeIdToArrayOfSteps keyEnumerator];
  
  while( (nextKey = [keyEnumerator nextObject]) ) {
    NSArray *steps 
      = (NSArray *) [self->nodeIdToArrayOfSteps objectForKey: nextKey];
    if( [steps count] > 1 ) {
      int i;
      BOOL updateFound = NO;
      
      for( i = 0; i < [steps count]; i++ ) {
        FTTransactionStepAndContext *step
          = (FTTransactionStepAndContext *) [steps objectAtIndex: i];
        
        if( __FTGRAPHIMPL_TX_NODE_UPDATE
           == [FTGraphImplTransactions 
                operationIdFromContext: [step transactionContext]] ) {
          if( !updateFound ) {
            updateFound = YES;
          } else {
            /**
             * We don't need more then one update, so disable the current
             * one:
             */
            FTGraphImplTransactions *graphTransactionStep 
              = (FTGraphImplTransactions *) [step transactionStep];
              
            [graphTransactionStep enableOperation: NO];
          }
        }
      }
    }
  }
  return self;
}
@end
/********************************************** __FTAnalyseTransactionSteps */


@implementation FTGenericTransactionOptimizer

- init {
  self = [super init];
  self->lock = [[NSLock alloc] init];
  
  return self;
}


- (void) dealloc {
  [self->lock release];
  [super dealloc];
}


- (id <FTTransaction>) optimizeTransaction: (id <FTTransaction>) toOptimize {
  if( ![toOptimize isKindOfClass: [FTTransactionImpl class]] ) {
    return toOptimize;
  }
  
  [self->lock lock];
  
  if( [[FTLogging coreLog] isTraceEnabled] ) {    
    [[FTLogging coreLog]
      trace: @"FTGenericTransactionOptimizer::optimizeTransaction: BEGIN" ];
  }
  
  NS_DURING
    __FTAnalyseTransactionSteps * steps = 
      [[__FTAnalyseTransactionSteps alloc]
        initForTransaction: (FTTransactionImpl *) toOptimize];
    
    [steps optimize];
    [steps release];
    
  NS_HANDLER
  NS_ENDHANDLER
  
  if( [[FTLogging coreLog] isDebugEnabled] ) {    
    [[FTLogging coreLog]
      trace: @"FTGenericTransactionOptimizer::optimizeTransaction: FINISHED" ];
  }
  
  [self->lock unlock];
  
  return toOptimize;
}


- analyseTransaction {
  return self;
}
@end
