//
//  CurveGraph.m
//  ellipticcurve
//
//  Created by itsme on 2009-07-26.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "CurveGraph.h"


@implementation CurveGraph

@synthesize touchDelegate;

- (id)initWithFrame:(CGRect)frame {
    printf("initwframe: (%f %f) x (%f %f)\n", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
    if (self = [super initWithFrame:frame]) {
        // Initialization code
    }
    return self;
}

- (void)moveTo:(CGRect)bounds inContext:(CGContextRef)context point:(CGPoint)point {
    if (!CGRectContainsPoint(bounds, point)) {
        return;
    }

    CGContextMoveToPoint(context, point.x, point.y);
}
- (void)drawTo:(CGRect)bounds inContext:(CGContextRef)context point:(CGPoint)point {
    if (!CGRectContainsPoint(bounds, point)) {
        return;
    }

    CGContextAddLineToPoint(context, point.x, point.y);
}
- (void)drawLine:(CGRect)bounds inContext:(CGContextRef)context p0:(CGPoint)p0  p1:(CGPoint)p1 {
    [self moveTo:bounds inContext:context point:p0];
    [self drawTo:bounds inContext:context point:p1];
}
- (void)drawLineThrough:(CGRect)bounds inContext:(CGContextRef)context p1:(CGPoint)p1 p2:(CGPoint)p2 {
//	printf("b: (%f,%f) x (%f,%f)\n", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
//	printf("p1=(%f,%f) p2=(%f,%f)\n", p1.x, p1.y, p2.x, p2.y);
    if (fabs(p1.x-p2.x)<0.001) {
        [self drawLine:bounds inContext:context p0:CGPointMake(p1.x, bounds.origin.y) p1:CGPointMake(p1.x, bounds.origin.y+bounds.size.height-1)];
		printf("drawing vertical x=%f\n", p1.x);
    }
    else if (fabs(p1.y-p2.y)<0.001) {
        [self drawLine:bounds inContext:context p0:CGPointMake(bounds.origin.x, p1.y) p1:CGPointMake(bounds.origin.x+bounds.size.width-1, p1.y)];
//		printf("drawing horizontal y=%f\n", p1.y);
    }
    else {
        // p = p1*t+(1-t)*p2  = t*(p1-p2)+p2
        //
        // x=t*(p1.x-p2.x)+p2.x  -> t=(x-p2.x)/(p1.x-p2.x)
        // y=t*(p1.y-p2.y)+p2.y  -> y=(x-p2.x)*(p1.y-p2.y)/(p1.x-p2.x)+p2.y
        //
        //  (y-p2.y)*(p1.x-p2.x)=(x-p2.x)*(p1.y-p2.y)
        //
        double x1= bounds.origin.x;
        double x2= bounds.origin.x+bounds.size.width-1;
        double y1= (x1-p2.x)*(p1.y-p2.y)/(p1.x-p2.x)+p2.y;
        double y2= (x2-p2.x)*(p1.y-p2.y)/(p1.x-p2.x)+p2.y;
        [self drawLine:bounds inContext:context p0:CGPointMake(x1,y1) p1:CGPointMake(x2,y2)];
//		printf("drawing (%f,%f) - (%f,%f)\n", x1, y1, x2, y2);
    }
}

- (void)drawAxis:(CGRect)bounds inContext:(CGContextRef)context {
    CGContextBeginPath(context);
    float x0= bounds.origin.x;
    float y0= bounds.origin.y;
    float xmiddle= bounds.origin.x+bounds.size.width/2;
    float ymiddle= bounds.origin.y+bounds.size.height/2;
    float x1= bounds.origin.x+bounds.size.width-1;
    float y1= bounds.origin.y+bounds.size.height-1;
    [self drawLine:bounds inContext:context p0:CGPointMake(x0,ymiddle) p1:CGPointMake(x1,ymiddle)];
    [self drawLine:bounds inContext:context p0:CGPointMake(xmiddle,y0) p1:CGPointMake(xmiddle,y1)];
    CGContextSetLineWidth(context, 2.0);
    CGContextStrokePath(context);
}
- (void)drawCurve:(CGRect)bounds inContext:(CGContextRef)context curve:(NSArray*)curve {
    CGContextBeginPath(context);
    for (int ii=0 ; ii<[curve count] ; ii++) {
        NSArray *part= [curve objectAtIndex:ii];
        if ([part count]==1) {
            // case where part->count==1 : draw dot with circle
            [self drawPoint:bounds inContext:context point:[[part objectAtIndex:0] CGPointValue]];
        }
        else {
            [self moveTo:bounds inContext:context point:[[part objectAtIndex:0] CGPointValue]];
            for (int i=1 ; i<[part count] ; i++) {
                [self drawTo:bounds inContext:context  point:[[part objectAtIndex:i] CGPointValue]];
            }
        }
    }
    CGContextSetLineWidth(context, 2.0);
    CGContextStrokePath(context);
}

// draws a small square around the point
- (void)drawPoint:(CGRect)bounds inContext:(CGContextRef)context point:(CGPoint)point
{
    CGContextBeginPath(context);
	[self moveTo:bounds inContext:context point:CGPointMake(point.x-2, point.y-2)];
	[self drawTo:bounds inContext:context point:CGPointMake(point.x-2, point.y+2)];
	[self drawTo:bounds inContext:context point:CGPointMake(point.x+2, point.y+2)];
	[self drawTo:bounds inContext:context point:CGPointMake(point.x+2, point.y-2)];
	[self drawTo:bounds inContext:context point:CGPointMake(point.x-2, point.y-2)];
    CGContextSetLineWidth(context, 1.0);
	CGContextSetRGBFillColor(context, 1, 0, 0, 0);
    CGContextStrokePath(context);
}

- (void)drawPoints:(CGRect)bounds inContext:(CGContextRef)context
{
	CGPoint px[3];
    for (int i=0 ; i<3 ; i++) {
		if (_flags&(1<<i)) {
			px[i]= [_view translate:bounds point:_p[i]];
			[self drawPoint:bounds inContext:context point:px[i]];
			//		printf("p[%d]: (%f,%f) -> (%f,%f)\n", i, _p[i].x, _p[i].y, px[i].x, px[i].y);
		}
	}

	if ((_flags&3)==3) {
		CGContextBeginPath(context);
		[self drawLineThrough:bounds inContext:context p1:px[0] p2:px[1]];
		CGContextSetRGBFillColor(context, 0, 1, 0, 0);
		CGContextSetLineWidth(context, 0.2);
		CGContextStrokePath(context);
	}

	if (_flags&4) {
		CGContextBeginPath(context);
		[self drawLineThrough:bounds inContext:context p1:px[2] p2:CGPointMake(px[2].x, 0)];
		CGContextSetRGBFillColor(context, 0, 1, 0, 0);
		CGContextSetLineWidth(context, 0.5);
		CGContextStrokePath(context);	
	}
}
- (void)drawRect:(CGRect)rect {
//    printf("drawing %s\n", [[self description] UTF8String]);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect bounds = CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height);
    
    CGContextSaveGState(context);
    [self drawAxis:bounds inContext:context];
    
    if (_curve)
        [self drawCurve:bounds inContext:context curve:[_view translateCurve:bounds curve:_curve]];
    else
        printf("NO CURVE!\n");

	[self drawPoints:bounds inContext:context];

    printf("done\n");
    
    CGContextRestoreGState(context);
}


- (void)dealloc {
    printf("graph dealloc\n");
    [_curve release];
    [_view release];
    [super dealloc];
}
- (void)setCurve:(NSArray*)curve view:(CurveView*)view
{
    printf("graph.setcurve\n");
    [_curve release];
    [_view release];
    _curve= [curve retain];
    _view= [view retain];
    [self setNeedsDisplay];
}
- (void)markPoint:(CGPoint)p view:(CurveView*)view n:(int)n
{
 //   printf("graph.markPoint\n");
    _p[n]= p;
	_flags |= (1<<n);
    [_view release];
    _view= [view retain];
    [self setNeedsDisplay];
}
- (void)clearPoint:(int)n
{
	_flags &= ~(1<<n);
}

// forward touch events to controller delegate
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
    [touchDelegate touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    [touchDelegate touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
    [touchDelegate touchesEnded:touches withEvent:event];
}

@end
