/* 
   Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016,
   2017, 2020, 2023 German A. Arias

   This file is part of FísicaLab application

   FísicaLab 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 3 of the License, or (at your option) any later version.
 
   This application 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
   Library General Public License for more details.
 
   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#import "ChalkboardView.h"
#import "FLSolverWithCells.h"
#import "ObjectData.h"

/* These group of methods allow some mathematical operations at entries
   of table view. */
@implementation ObjectData

- (void) awakeFromNib
{
  // Init instance variables.
  referenceCount = 0;
  system = 0;
  [dataViewer setDataSource: self];
  [dataViewer setDelegate: self];
  objectsList = [NSMutableDictionary dictionary];
  [objectsList retain];

  numberId = nil;
  selectedGroup = @"kinematics";

  // Build the tree dictionary.
  tree =
    [[NSMutableDictionary dictionaryWithObjects:
		      [NSArray arrayWithObjects: @"FLKinematics",
			       @"FLStatics",
			       @"FLDynamics",
			       @"FLCalorimetry",
			       nil]
					forKeys:
		      [NSArray arrayWithObjects: @"kinematics",
			       @"statics",
			       @"dynamics",
			       @"heat",
			       nil]] retain];

  // Build the conversion factor dictionary.
  NSBundle *mainBundle = [NSBundle mainBundle];
  NSString *path = [mainBundle pathForResource: @"conversionFactors"
					ofType: @"plist"];
  conversionDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:
						path];
  [conversionDictionary retain];
}

- (void) awakeFromGSMarkup
{
  [self awakeFromNib];
}

- (void) unitsSystem: (id)sender
{
  /* If user change the units system, update the variable and the menu of
     conversion factors. */
  if (system != [[sender selectedCell] tag])
    {
      system = [[sender selectedCell] tag];
    }
}

- (NSString *) selectedModule
{
  return [tree objectForKey: selectedGroup];
}

- (void) calculate
{
  id flModule;
  
  [dataViewer deselectAll: self];
  
  // Delete the content of results viewer if needed.
  if ([cleanResultsViewer state] == NSOnState)
    {
      [resultsViewer setString: @""];
    }
  
  /* Instantiate the class for the problem and pass all the
     information to solve it. */
  flModule = [NSClassFromString([chalkboard module]) new];
  [flModule setSystem: system];
  [flModule setConversions: conversionDictionary];
  [flModule setViewer: self];

  // If the module use cells, pass the corresponding information.
  if ([flModule isKindOfClass: [FLSolverWithCells class]])
    {
      [flModule setCells: [chalkboard cellsInfo]];
      [flModule withChalkboardWidth: [chalkboard chalkboardWidth]
			     height: [chalkboard chalkboardHeight]];
    }
  
  [flModule solveProblemWithData: objectsList];
  [flModule release];
}

/* Build the corresponding dictionary to the new element and add this to
   the objectsList dictionary. */
- (void) addObject: (id)new
{
  NSString *imageName;
  NSArray *moduleData;
  NSArray *titleList = nil, *unitsList = nil;
  NSMutableArray *dataList = nil;
  NSMutableArray *valueList = [NSMutableArray array];
  NSMutableDictionary *data;

  NSNumber *type;
  referenceCount += 1;
  [dataViewer deselectRow: [dataViewer selectedRow]];
  [dataViewer setMenu: nil];
  
  // Get the corresponding data.
  moduleData =
    [NSClassFromString([chalkboard module]) dataForElementWithTag: [new tag]
							forSystem: system];

  titleList = [moduleData objectAtIndex: 0];
  dataList = [moduleData objectAtIndex: 1];
  unitsList = [moduleData objectAtIndex: 2];
  type = [moduleData objectAtIndex: 3];
  imageName = [moduleData objectAtIndex: 4];

  if ([titleList count] != [dataList count] ||
      [dataList count] != [unitsList count])
    {
      NSLog(@"Error in number of entries.");
    }
  else
    {
      data = [NSMutableDictionary dictionaryWithObjectsAndKeys: type, @"Type",
				  titleList, @"Titles",
				  dataList, @"Data",
				  unitsList, @"Dimensions",
				  valueList, @"Values",
				  imageName, @"Image", nil];
      ASSIGN(numberId, [NSNumber numberWithInt: referenceCount]);
      [objectsList setObject: data forKey: numberId];
      [new setTag: [numberId intValue]];
      [dataViewer reloadData];
    }
}

// Delete an element in objectsList dictionary.
- (void) deleteObject: (int)code
{
  ASSIGN(numberId, [NSNumber numberWithInt: code]);
  [dataViewer deselectAll: self];
  [objectsList removeObjectForKey: numberId];
  [dataViewer setMenu: nil];
  [dataViewer reloadData];
}

// Update the table view with the info of new selected element.
- (void) selectObject: (int)code
{
  [dataViewer deselectRow: [dataViewer selectedRow]];
  ASSIGN(numberId, [NSNumber numberWithInt: code]);
  [dataViewer setMenu: nil];
  [dataViewer reloadData];
}

// Delete all elements at objectsList dictionary.
- (void) deleteAllObjects: (BOOL)value
{
  referenceCount = 0;
  [dataViewer deselectAll: self];
  [objectsList removeAllObjects];
  [dataViewer setMenu: nil];
  [dataViewer reloadData];
}

// Build a string with the element's info, to display in a tooltip.
- (NSString *) dataOfObject: (NSNumber *)aNumber
{
  int x;
  NSString *field;
  NSString *objectData = @"";
  NSMutableArray *keys, *values;
  
  keys = [[objectsList objectForKey: aNumber] objectForKey: @"Titles"];
  values = [[objectsList objectForKey: aNumber] objectForKey: @"Data"];

  for (x = 0; x < [keys count]; x++)
    {
      if (x < [keys count] - 1)
	{
	  field = [NSString stringWithFormat: @"%@  =  %@ \n",
			[[keys objectAtIndex: x] description],
		      [[values objectAtIndex: x] description]];
	}
      else
	{
	  field = [NSString stringWithFormat: @"%@  =  %@",
			[[keys objectAtIndex: x] description],
		      [[values objectAtIndex: x] description]];
	}
      
      objectData = [objectData stringByAppendingString: field];
    }

  return objectData;
}

// Add the selected conversion factor in contextual menu.
- (void) insertFactor: (id)sender
{
  NSInteger y = [dataViewer selectedRow];

  if (y >= 0)
    {
      NSString *newValue;
      NSCell *cell = [[dataViewer tableColumnWithIdentifier: @"Data"]
		       dataCellForRow: y];
      NSArray *text = [[cell stringValue] componentsSeparatedByString: @"@"];
      NSMutableArray *list = [[objectsList objectForKey: numberId]
				           objectForKey: @"Data"];
      
      newValue = [[text objectAtIndex: 0] stringByTrimmingSpaces];
      newValue = [newValue stringByAppendingString: @" @ "];
      newValue = [newValue stringByAppendingString: [sender title]];
      [list replaceObjectAtIndex: y withObject: newValue];
      [dataViewer reloadData];
      [dataViewer deselectAll: self];
    }
}

// Write a message
- (void) writeMessage: (NSString *)data
{
  NSInteger length = [[resultsViewer textStorage] length];
  [resultsViewer replaceCharactersInRange: NSMakeRange(length, 0)
			       withString: data];
}

// Data to save the problem
- (NSString *) problemInfo
{
  NSDictionary *dic;
  NSNumber *width = [NSNumber numberWithUnsignedInteger:
				[chalkboard chalkboardWidth]];
  NSNumber *height = [NSNumber numberWithUnsignedInteger:
				 [chalkboard chalkboardHeight]];

  dic = [NSMutableDictionary dictionaryWithObjectsAndKeys:
			       width, @"width",
			       height, @"height",
			       [chalkboard cellsInfo], @"cells",
			       objectsList, @"info", nil];

  return [dic description];
}

// Delegate and data source methods for table view.
- (NSInteger) numberOfRowsInTableView: (NSTableView*)aTableView
{
  return [[[objectsList objectForKey: numberId] objectForKey: @"Titles"] count];
}

- (id) tableView: (NSTableView*)aTableView
       objectValueForTableColumn: (NSTableColumn*)aTableColumn
       row: (NSInteger)rowIndex
{
  id print;
  NSMutableDictionary *object = [objectsList objectForKey: numberId];
  NSMutableArray *list = [object objectForKey: [aTableColumn identifier]];
  print = [list objectAtIndex: rowIndex];
  
  if ([[aTableColumn identifier] isEqualToString: @"Titles"])
    {
      print = [print description];
    }
  
  return print;
}

- (void) tableView: (NSTableView*)aTableView setObjectValue: (id)anObject
         forTableColumn: (NSTableColumn*)aTableColumn row: (NSInteger)rowIndex
{
  NSRange range;
  NSString *factor = nil;
  NSString *entry = [[anObject description] stringByTrimmingSpaces];
  NSMutableDictionary *object = [objectsList objectForKey: numberId];
  NSMutableArray *list = [object objectForKey: [aTableColumn identifier]];

  range = [entry rangeOfString: @"@"];
  if (range.length > 0)
    {
      factor = [entry substringFromIndex: range.location];
      entry = [[entry substringToIndex: range.location]
		stringByTrimmingSpaces];
    }

  if ([[[[object objectForKey: @"Titles"] objectAtIndex: rowIndex]
	 description] hasPrefix: @"ang"])
    {
      entry = [self processAngleEntry: entry];
    }
  else
    {
      entry = [self processEntry: entry];
    }
  
  if (factor != nil)
    {
      entry = [NSString stringWithFormat: @"%@ %@",
			entry,
			factor];
    }

  [list replaceObjectAtIndex: rowIndex
		  withObject: entry];
  [dataViewer reloadData];
}

- (BOOL) tableView: (NSTableView*)aTableView
         shouldEditTableColumn: (NSTableColumn*)aTableColumn
			   row: (NSInteger)rowIndex
{
  [chalkboard controlCursor: self];
  return YES;
}

- (void) tableView: (NSTableView*)aTableView 
   willDisplayCell: (id)aCell 
    forTableColumn: (NSTableColumn*)aTableColumn 
	       row: (NSInteger)rowIndex
{
  [aCell setEditable: YES];
}

- (BOOL) tableView: (NSTableView*)aTableView
   shouldSelectRow: (NSInteger)rowIndex
{
  // Set a transient menu with the selected conversion factors.
  if (rowIndex > -1)
    {
      id factor;
      id<NSMenuItem> item;
      NSMenu *factorsMenu;
      NSEnumerator *conv = nil;
      NSArray *units;
      
      units = [[objectsList objectForKey: numberId]
		objectForKey: @"Dimensions"];
      
      // Select the appropriate list of conversion factors.
      conv =
	[[[conversionDictionary objectForKey: [units objectAtIndex: rowIndex]]
	   objectAtIndex: system] keyEnumerator];
      
      factorsMenu = [[[NSMenu alloc] initWithTitle: _(@"Factors")] autorelease];
      [factorsMenu setDelegate: self];
      
      while ((factor = [conv nextObject]))
	{
	  if ([[factor description] isEqualToString: @"degrees"])
	    {
	      item = [factorsMenu addItemWithTitle: _(@"degrees")
					    action: @selector(insertFactor:)
				     keyEquivalent: @""];
	    }
	  else
	    {
	      item = [factorsMenu addItemWithTitle: [factor description]
					    action: @selector(insertFactor:)
				     keyEquivalent: @""];
	    }
	  [item setTarget: self];
	}
      
      [dataViewer setMenu: factorsMenu];
    }
  else
    {
      [dataViewer setMenu: nil];
    }

  return YES;
}

- (NSCollectionViewItem *) collectionView: (NSCollectionView *)cView
      itemForRepresentedObjectAtIndexPath: (NSIndexPath *)indexPath
{
  return nil;
}

- (NSInteger) collectionView: (NSCollectionView *)cView
			   numberOfItemsInSection: (NSInteger)section
{
  return [[[objectsList objectForKey: numberId] objectForKey: @"Titles"] count];
}

// Delegate method for main tab view.
- (void) tabView: (NSTabView*)tabView
         didSelectTabViewItem: (NSTabViewItem*)tabViewItem
{
  NSString *ident = [[tabViewItem identifier] description];

  if ([ident hasPrefix: @"FL"])
    {
      [tree setObject: ident forKey: selectedGroup];
    }
  else
    {
      selectedGroup = ident;
    }

  //NSLog([tree objectForKey: selectedGroup]);
}

// Delegate method for contextual menu.
- (void) menuNeedsUpdate: (NSMenu*)menu
{
  [chalkboard controlCursor: self];
}

- (void) dealloc
{
  [objectsList release];
  [conversionDictionary release];
  [numberId release];
  [tree release];
  [super dealloc];
}

@end
