/* 
   NSLevelIndicatorCell.m

   The level indicator cell class

   Copyright (C) 2007 Free Software Foundation, Inc.

   Author:  H. Nikolaus Schaller
   Date: 2006
   
   This file is part of the GNUstep GUI Library.

   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 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; see the file COPYING.LIB.
   If not, see <http://www.gnu.org/licenses/> or write to the 
   Free Software Foundation, 51 Franklin Street, Fifth Floor, 
   Boston, MA 02110-1301, USA.
*/ 

#import <Foundation/NSException.h>

#import "AppKit/NSBezierPath.h"
#import "AppKit/NSGraphics.h"
#import "AppKit/NSImage.h"
#import "AppKit/NSLevelIndicatorCell.h"

@implementation NSLevelIndicatorCell

- (id) init
{
  return [self initWithLevelIndicatorStyle: NSRatingLevelIndicatorStyle];
}

- (id) initWithLevelIndicatorStyle: (NSLevelIndicatorStyle)style
{
  self = [super init];
  if (self)
    {
      [self setAlignment: NSCenterTextAlignment];
      [self setLevelIndicatorStyle: style];
      //_minValue = 0.0;
      if ((style == NSContinuousCapacityLevelIndicatorStyle) ||
          (style == NSRelevancyLevelIndicatorStyle))
        {
          _maxValue = 100.0;
        }
      else
        {
          _maxValue = 5.0;
        }
      [self setDoubleValue: 0.0];
    }

  return self;
}

- (id) copyWithZone:(NSZone *) zone
{
  NSLevelIndicatorCell *c = [super copyWithZone: zone];

  return c;
}

- (void) dealloc
{
  [super dealloc];
}

- (NSLevelIndicatorStyle) style
{
  return _style; 
}


- (double) maxValue
{
  return _maxValue;
}

- (void) setMaxValue: (double)val
{
  _maxValue = val;
}

- (double) minValue 
{
  return _minValue;
}

- (void) setMinValue:(double) val
{
  _minValue = val;
}

- (double) criticalValue
{ 
  return _criticalValue; 
}

- (void) setCriticalValue: (double)val
{
  _criticalValue = val;
}

- (double) warningValue
{
  return _warningValue;
}

- (void) setWarningValue: (double)val
{
  _warningValue = val;
}

- (void) setLevelIndicatorStyle: (NSLevelIndicatorStyle)style
{
  _style = style;
}

- (NSInteger) numberOfMajorTickMarks
{
  return _numberOfMajorTickMarks;
}

- (void) setNumberOfMajorTickMarks: (NSInteger)count
{
  _numberOfMajorTickMarks = count;
}

- (NSInteger) numberOfTickMarks
{
  return _numberOfTickMarks;
}

- (void) setNumberOfTickMarks: (NSInteger)count
{
  _numberOfTickMarks = count;
}

- (NSTickMarkPosition) tickMarkPosition
{
  return _tickMarkPosition;
}

- (void) setTickMarkPosition: (NSTickMarkPosition)pos
{
  _tickMarkPosition = pos;
}

- (double) tickMarkValueAtIndex: (NSInteger)index
{
  if ((index < 0) || (index >= _numberOfTickMarks))
    {
      [NSException raise: NSRangeException
                   format: @"tick mark index invalid"];
    }

  return _minValue + index * (_maxValue - _minValue) / _numberOfTickMarks;
}

- (NSRect) rectOfTickMarkAtIndex: (NSInteger)index
{
  NSRect cellRect = NSZeroRect;
  float frameWidth = _cellFrame.size.width;

  if ((index < 0) || (index >= _numberOfTickMarks))
    {
      [NSException raise: NSRangeException
        format: @"tick mark index invalid"];
    }

  // Create default minor tickmark size in cellRect
  cellRect.size.width = 1;
  cellRect.size.height = 4;
  
  // If all tick marks are major:
  if (_numberOfTickMarks <= _numberOfMajorTickMarks)
    {
      // Use major tick mark size
      cellRect.size.width = 3;
      cellRect.size.height = 7;
    }
  // If major tick marks fit with even spacing
  else if ((_numberOfTickMarks -1) % (_numberOfMajorTickMarks - 1) == 0)
    {
      NSInteger minorTicksPerMajor = (_numberOfTickMarks - 1) / (_numberOfMajorTickMarks - 1);

      // If index is a major tick mark
      if (index % minorTicksPerMajor == 0)
        {
          // Use major tick mark size
          cellRect.size.width = 3;
          cellRect.size.height = 7;
        }
    }
  // FIXME: Extra tick mark code, when all major tick marks don't fit but a lesser amount will
        
  // Last tick mark
  if (index == (_numberOfTickMarks - 1))
    {
      cellRect.origin.x = (frameWidth - cellRect.size.width);
    }
  // Not the first tick mark. (First tick mark will use 0,0 default values already set)
  else if (index != 0)
    {
      float spacing = (frameWidth / (_numberOfTickMarks - 1)) * index;
      cellRect.origin.x = spacing - (cellRect.size.width / 2);
    }
        
  // Set origins if tick marks are above the indicator
  if (_tickMarkPosition == NSTickMarkAbove)
    {
      switch (_style)
        {
          case NSContinuousCapacityLevelIndicatorStyle:
            {
              cellRect.origin.y = 16;
              break;
            }
          case NSRelevancyLevelIndicatorStyle:
            {
              cellRect.origin.y = 12;
              break;
            }
          case NSRatingLevelIndicatorStyle:
            {
              cellRect.origin.y = 13;
              break;
            }
          case NSDiscreteCapacityLevelIndicatorStyle:
            {
              cellRect.origin.y = 18;
              break;
            }
        }
    }
  // If tick mark is minor and below indicator, use y origin of 3. If not, default value of 0 is used
  else if (cellRect.size.width == 1)
    {
      cellRect.origin.y = 3;
    }
        
  return NSIntegralRect(cellRect);
}

- (NSSize) cellSize
{
  // Sizes are the same as those from OSX
  NSSize cellSize = NSMakeSize(400000, 25);

  // Change cellSize to the correct size:
  switch (_style)
    {
      case NSContinuousCapacityLevelIndicatorStyle:
        {
          cellSize.height = 23;
          break;
        }
      case NSRelevancyLevelIndicatorStyle:
        {
          cellSize.height = 19;
          break;
        }
      case NSRatingLevelIndicatorStyle:
        {
          cellSize.height = 20;
          cellSize.width = 13 * _maxValue;
          break;
        }
      case NSDiscreteCapacityLevelIndicatorStyle:
        {
          cellSize.height = 25;
          break;
        }
    }
    
  return cellSize;
}

- (void) drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
{
  NSColor *fillColor;
  double value = [self doubleValue];
  double val = (value -_minValue) / (_maxValue -_minValue);
  BOOL vertical = (cellFrame.size.height > cellFrame.size.width);
        
  _cellFrame = cellFrame;
  if (value < _warningValue)
    fillColor = [NSColor greenColor];
  else if (value < _criticalValue)
    fillColor = [NSColor yellowColor];
  else
    fillColor = [NSColor redColor];

  if (_numberOfTickMarks != 0)
    {
      // FIXME: this code only works for horizontal frames
      float x;
      float y0, y1;
      float step = _numberOfTickMarks > 1 ? 
          (cellFrame.size.width - 1.0) / (_numberOfTickMarks - 1) : 
          1.0;
      int tick;

      if (_tickMarkPosition == NSTickMarkBelow)
        {
          cellFrame.origin.y += 8.0;
          cellFrame.size.height -= 8.0;
          y0 = 4.0;
          y1 = 8.0;
        }
      else
        {
          cellFrame.size.height -= 8.0;
          y0 = cellFrame.size.height;
          y1 = y0 + 4.0;
        }

      [[NSColor darkGrayColor] set];
      for(x = 0.0, tick = 0; tick <= _numberOfTickMarks; x += step, tick++)
        {
          [NSBezierPath strokeLineFromPoint: NSMakePoint(x, y0) toPoint: NSMakePoint(x, y1)];
          // FIXME: draw _numberOfMajorTickMarks thick ticks (ignore if more than _numberOfTickMarks)
        }
    }

  switch(_style)
    {
      case NSDiscreteCapacityLevelIndicatorStyle:
        {
          int segments = (int)(_maxValue - _minValue);
          // width of one segment
          float step = (segments > 0) ? 
              ((vertical ? cellFrame.size.height : cellFrame.size.width) / segments) : 
              10.0;
          int i;
          int ifill = val * segments + 0.5;

          for( i = 0; i < segments; i++)
            {
              // draw segments
              NSRect seg = cellFrame;

              if (vertical)
                {
                  seg.size.height = step - 1.0;
                  seg.origin.y += i * step;
                }
              else
                {
                  seg.size.width = step - 1.0;
                  seg.origin.x += i * step;
                }

              if (i < ifill)
                  [fillColor set];
              else
                  // FIXME: Should not be needed.
                  [[NSColor controlBackgroundColor] set];

              // we could also fill with a scaled horizontal/vertical image
              NSRectFill(seg);
              // draw border
              [[NSColor lightGrayColor] set];
              NSFrameRect(seg);
            }
          break;
        }
      case NSContinuousCapacityLevelIndicatorStyle:
        {
          NSRect ind, fill;

          if (vertical)
            NSDivideRect(cellFrame, &ind, &fill, cellFrame.size.height * val, NSMinYEdge);
          else
            NSDivideRect(cellFrame, &ind, &fill, cellFrame.size.width * val, NSMinXEdge);

          [fillColor set];
          // we could also fill with a scaled horizontal/vertical image
          NSRectFill(ind);
          // FIXME: Not needed
          [[NSColor controlBackgroundColor] set];
          NSRectFill(fill);
          // draw border
          [[NSColor lightGrayColor] set];
          NSFrameRect(cellFrame);
          break;
        }
      case NSRelevancyLevelIndicatorStyle:
        {
          // FIXME: Not needed
          [[NSColor controlBackgroundColor] set];
          NSRectFill(cellFrame);

          [[NSColor darkGrayColor] set];
          if (vertical)
            {
              float y;
              float yfill = val * cellFrame.size.height + 0.5;

              for (y = 0.0; y < yfill; y += 2.0)
                {
                  [NSBezierPath strokeLineFromPoint: NSMakePoint(0.0, y) 
                                toPoint: NSMakePoint(cellFrame.size.width, y)];
                }
            }
          else
            {
              float x;
              float xfill = val * cellFrame.size.width + 0.5;
              for (x = 0.0; x < xfill; x += 2.0)
                {
                  [NSBezierPath strokeLineFromPoint: NSMakePoint(x, 0.0) 
                                toPoint: NSMakePoint(x, cellFrame.size.height)];
                }
            }
          break;
        }
      case NSRatingLevelIndicatorStyle:
        {
          NSImage *indicator = [self image];
          NSSize isize;

          if (!indicator)
              indicator = [NSImage imageNamed: @"NSRatingLevelIndicator"];

          isize = [indicator size];

          // FIXME: Not needed
          [[NSColor controlBackgroundColor] set];
          NSRectFill(cellFrame);

          if (vertical)
            {
              int y;

              for (y = 0.0; y < (val + 0.5); y++)
                {
                  NSPoint pos = NSMakePoint(0, y * isize.height + 2.0);

                  if (pos.y >= cellFrame.size.height)
                    break;
                        
                  // here we can strech the image as needed by using drawInRect:
                  [indicator drawAtPoint: pos 
                             fromRect: (NSRect){NSZeroPoint, isize} 
                             operation: NSCompositeCopy 
                             fraction:1.0];
                }

              // FIXME: Should draw place holder for the rest of the cell frame
            }
          else
            {
              int x;

              for (x = 0.0; x < (val + 0.5); x++)
                {
                  NSPoint pos = NSMakePoint(x * isize.width + 2.0, 0.0);

                  if(pos.x >= cellFrame.size.width)
                    break;
                        
                  [indicator drawAtPoint: pos 
                             fromRect: (NSRect){NSZeroPoint, isize} 
                             operation: NSCompositeCopy 
                             fraction: 1.0];
                }

              // FIXME: Should draw place holder for the rest of the cell frame
            }
        }
    }
}

- (void) encodeWithCoder: (NSCoder *) aCoder
{
  [super encodeWithCoder:aCoder];

  if ([aCoder allowsKeyedCoding])
    {
      [aCoder encodeDouble: _minValue forKey: @"NSMinValue"];
      [aCoder encodeDouble: _maxValue forKey: @"NSMaxValue"];
      [aCoder encodeDouble: _warningValue forKey: @"NSWarningValue"];
      [aCoder encodeDouble: _criticalValue forKey: @"NSCriticalValue"];
      [aCoder encodeDouble: [self doubleValue] forKey: @"NSValue"];
      [aCoder encodeInt: _style forKey: @"NSIndicatorStyle"];
      [aCoder encodeInt: _numberOfMajorTickMarks forKey: @"NSNumberOfMajorTickMarks"];
      [aCoder encodeInt: _numberOfTickMarks forKey: @"NSNumberOfTickMarks"];
      [aCoder encodeInt: _tickMarkPosition forKey: @"NSTickMarkPosition"];
    }
  else
    {
      [aCoder encodeValueOfObjCType: @encode(double) at: &_minValue];
      [aCoder encodeValueOfObjCType: @encode(double) at: &_maxValue];
      [aCoder encodeValueOfObjCType: @encode(double) at: &_warningValue];
      [aCoder encodeValueOfObjCType: @encode(double) at: &_criticalValue];
      [aCoder encodeValueOfObjCType: @encode(int) at: &_style];
      [aCoder encodeValueOfObjCType: @encode(int) at: &_numberOfMajorTickMarks];
      [aCoder encodeValueOfObjCType: @encode(int) at: &_numberOfTickMarks];
      [aCoder encodeValueOfObjCType: @encode(int) at: &_tickMarkPosition];
    }
}

- (id) initWithCoder:(NSCoder *) aDecoder
{
  self = [super initWithCoder:aDecoder];

  if ([aDecoder allowsKeyedCoding])
    {
      if ([aDecoder containsValueForKey: @"NSMinValue"])
        {
          _minValue = [aDecoder decodeDoubleForKey: @"NSMinValue"];
        }
      if ([aDecoder containsValueForKey: @"NSMaxValue"])
        {
          _maxValue = [aDecoder decodeDoubleForKey: @"NSMaxValue"];
        }
      if ([aDecoder containsValueForKey: @"NSWarningValue"])
        {
          _warningValue = [aDecoder decodeDoubleForKey: @"NSWarningValue"];
        }
      if ([aDecoder containsValueForKey: @"NSCriticalValue"])
        {
          _criticalValue = [aDecoder decodeDoubleForKey: @"NSCriticalValue"];
        }
       if ([aDecoder containsValueForKey: @"NSValue"])
        {
          [self setDoubleValue: [aDecoder decodeDoubleForKey: @"NSValue"]];
        }
      if ([aDecoder containsValueForKey: @"NSIndicatorStyle"])
        {
          _style = [aDecoder decodeIntForKey: @"NSIndicatorStyle"];
        }
      if ([aDecoder containsValueForKey: @"NSNumberOfMajorTickMarks"])
        {
          _numberOfMajorTickMarks = [aDecoder decodeIntForKey: @"NSNumberOfMajorTickMarks"];
        }
      if ([aDecoder containsValueForKey: @"NSNumberOfTickMarks"])
        {
          _numberOfTickMarks = [aDecoder decodeIntForKey: @"NSNumberOfTickMarks"];
        }
      if ([aDecoder containsValueForKey: @"NSTickMarkPosition"])
        {
          _tickMarkPosition = [aDecoder decodeIntForKey: @"NSTickMarkPosition"];
        }
    }
  else
    { 
      [aDecoder decodeValueOfObjCType: @encode(double) at: &_minValue];
      [aDecoder decodeValueOfObjCType: @encode(double) at: &_maxValue];
      [aDecoder decodeValueOfObjCType: @encode(double) at: &_warningValue];
      [aDecoder decodeValueOfObjCType: @encode(double) at: &_criticalValue];
      [aDecoder decodeValueOfObjCType: @encode(int) at: &_style];
      [aDecoder decodeValueOfObjCType: @encode(int) at: &_numberOfMajorTickMarks];
      [aDecoder decodeValueOfObjCType: @encode(int) at: &_numberOfTickMarks];
      [aDecoder decodeValueOfObjCType: @encode(int) at: &_tickMarkPosition];
    }
  
  return self;
}

@end
