//
//  CurveController.m
//  ellipticcurve
//
//  Created by itsme on 2009-07-25.
//  Copyright 2009 itsapps. All rights reserved.
//

#import "CurveController.h"

@implementation CurveController

-(id)init
{
    printf("CurveController init\n");
    if (![super init])
    return nil;
    model= [[CurveModel alloc] init];
    view= [[CurveView alloc] init];

    _state= 0;

    [self redraw:nil];

    return self;
}
-(IBAction)redraw:(id)sender
{
    model->a=[slide_a value];
    model->b=[slide_b value];
    [value_a setText:[NSString stringWithFormat:@"%5.2f", model->a]];
    [value_b setText:[NSString stringWithFormat:@"%5.2f", model->b]];
    printf("controller : redraw  %f  %f\n", model->a, model->b);

    // todo: re calc points when a/b have changed
    [graph clearPoint:0];
    [graph clearPoint:1];
    [graph clearPoint:2];
    
    [graph setCurve:[model makeCurve:view] view:view];

}

double distance2(CGPoint p, CGPoint q)
{
    return (p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y);
}
double pinchdistance(NSSet *touches)
{
    NSArray *t = [touches allObjects];
    UITouch *t0= [t objectAtIndex:0];
    UITouch *t1= [t objectAtIndex:1];
    CGPoint p0= [t0 locationInView:t0.view];
    CGPoint p1= [t1 locationInView:t1.view];

    return sqrt(distance2(p0, p1));
}
-(void)pinchEvent:(double)pinch
{
    if (view) {
        [view rescale:pinch/200+1];
        [self redraw:nil];
    }
}
-(void)touchEvent:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *tch= [touches anyObject];

    CGRect bounds= CGRectMake(0,0,[tch.view bounds].size.width, [tch.view bounds].size.height);
    CGPoint p= [view translateBack:bounds point:[tch locationInView:tch.view]];

//    printf("touch(%f,%f) , state=%d\n", p.x, p.y, _state);
    p= [model projectoncurve:p];
    if (isnan(p.y))
        return;
    int n=0;
    switch(_state) {
        case 3:
            // mult
        case 0:  // marking p
            n=0;
            _p= p;
            if (_state==0)
                _state= 1;
            printf("   newstate=%d, setting _p=(%f,%f)\n", _state, p.x, p.y);
            break;
        case 1:  // marking q
            n=1;
            _q= p;
            _state= 2;
            printf("   newstate=%d, setting _q=(%f,%f)\n", _state, p.x, p.y);
            break;
        case 2:
            if (distance2(_p, p)<distance2(_q,p)) {
                printf("   setting _p=(%f,%f)\n", p.x, p.y);
                _p= p;
                n=0;
            }
            else {
                printf("   setting _q=(%f,%f)\n", p.x, p.y);
                _q= p;
                n=1;
            }
            break;
    }

    [graph markPoint:p view:view n:n];
    if (_state!=3) {
        printf("update addition point\n");
        [graph markPoint:[model add:_p q:_q] view:view n:2];
    }
    else {
        printf("update mult point\n");
        [graph markPoint:[model scalarmultiply:_p c:_mult] view:view n:1];
        [graph clearPoint:2];
    }
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    printf("begin: n=%d : t=%p, e=%p\n", [touches count], touches, event);
    _initialTouchesCount=[touches count];
    gettimeofday(&_initialTouchTime,0);
    _initialPinchWidth= 0;
    if ([touches count]==2) {
        _initialPinchWidth= pinchdistance(touches);
        [view startrescale];
    }
}
double tvdiff(const struct timeval* t0, const struct timeval* t1)
{
    return (double)(t1->tv_sec-t0->tv_sec)+(t1->tv_usec-t0->tv_usec)/1000000.0;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    struct timeval now;
    gettimeofday(&now,0);

    if ([touches count]==2 && _initialTouchesCount==1) {
        // 2nd touch followed later
        _initialTouchesCount=[touches count];
        _initialPinchWidth= pinchdistance(touches);
        [view startrescale];
    }
    else if ([touches count]==1 && _initialTouchesCount==2) {
        // ignore touch which started as pinch, but has lost 2nd touch
        return;
    }
    double touchtime= tvdiff(&_initialTouchTime, &now);
    printf("moved: n=%d : t=%p, e=%p\n", [touches count], touches, event);
    if (_initialTouchesCount==1 && touchtime>0.2) {
        [self touchEvent:touches withEvent:event];
    }
    else if (_initialTouchesCount==2) {
        [self pinchEvent:pinchdistance(touches)-_initialPinchWidth];
    }
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    printf("ended: n=%d : t=%p, e=%p\n", [touches count], touches, event);
    if ([touches count]==1) {
        [self touchEvent:touches withEvent:event];
    }
    else if ([touches count]==2) {
        [self pinchEvent:pinchdistance(touches)-_initialPinchWidth];
        [view endrescale];
    }
}


-(IBAction)select_p:(UISwitch*)sender { _state= 0; }
-(IBAction)select_q:(UISwitch*)sender { _state= 1; }
-(IBAction)select_mult:(UISwitch*)sender { _state= 3; }
-(IBAction)setmultiplier:(UISlider*)sender { 
    _mult= [sender value];
    [value_multiplier setText:[NSString stringWithFormat:@"%4.1f", _mult]];
    [graph clearPoint:2];
    [graph markPoint:[model scalarmultiply:_p c:_mult] view:view n:1];
}

-(void)dealloc {
    printf("CurveController dealloc\n");
    [view release];
    [model release];
    [super dealloc];
}
@end
