<<<<----- Return to Wes Mantooth
/**
 * a Javascript implementation of the k-nearest algorithm
 *
 * https://www.youtube.com/watch?v=AoeEHqVSNOw
 *
 * Following these tutorials I decided to write a javascript version of the algorithm
 * ( not based on the sample code but based on the teachers visual explaination about
 * how the agorithm worked). 
 *
 * @author Jeremy Aaron Heminger <j.heminger13@gmail.com>
 *
 * */
window.onload = function() {
   
    'use strict';
    
    $w.boolLog = true;
    
    // initialize
    $w.log('initilizing');
    
    var i = $w.canvas.init(document.getElementById('target'),1000,1000),
        r,
        NUMBEROFTESTS = 30,
        NUMBEROFSAMPLES = 5, // the number of samples to test against ( <= (red || green) length)
        QUARTERSIZE = 500, // the size of the grid quarter (for drawing)
        GREEN = '#008040',
        BLUE = '#0013ff',
        SAMPLEDOTSIZE = 5,
        TESTDOTSIZE = 8,
        LABELOFFSET = 8;
        
    // get the canvas object
    $w.draw.grid(i,1000,1000,10);
    
    // create our sample data
    var green = [
        [-433,-400],
        [-200,-133],
        [10,-60],
        [-100,10],
        [-200,-200]
    ];
    //
    var blue = [
        [100,200],
        [322,233],
        [127,88],
        [357,276],
        [430,227]
    ];
    
    //
    var greenl = green.length, bluel = blue.length;
    
    $w.log("Sample:");
    $w.log("- green: "+greenl);
    $w.log("- blue: "+bluel);
    $w.log("- total: "+greenl+bluel);
    
    var TEST = new WhatIsit(green,blue,QUARTERSIZE,TESTDOTSIZE,GREEN,BLUE);
    
    // build some random tests
    var tests = [], j = 0;
    
    for(j;j<NUMBEROFTESTS;j++) {
        tests[j] = [];
        tests[j][0] = $w.math.frandom(1000) - 1000; // random x
        tests[j][1] = $w.math.frandom(1000) - 1000; // random y
        tests[j] = TEST.dtogrid(tests[j][0],tests[j][1],QUARTERSIZE);
    }
    
    
    $w.log("drawing sample data");
    
    // setup the visible part of the tests
    for(var j=0; j<greenl; j++) { 
        green[j] = TEST.dtogrid(green[j][0],green[j][1]);
        $w.canvas.circle(i,green[j][0],green[j][1],SAMPLEDOTSIZE,GREEN);
        $w.canvas.text(i,green[j][0],green[j][1]-LABELOFFSET,'{'+(green[j][0]-QUARTERSIZE)+','+(green[j][1]-QUARTERSIZE)+'}');
    }
    // setup the visible part of the tests
    for(var j=0; j<bluel; j++) {
        blue[j] = TEST.dtogrid(blue[j][0],blue[j][1],QUARTERSIZE);
        $w.canvas.circle(i,blue[j][0],blue[j][1],SAMPLEDOTSIZE,BLUE);
        $w.canvas.text(i,blue[j][0],blue[j][1]-LABELOFFSET,'{'+(blue[j][0]-QUARTERSIZE)+','+(blue[j][1]-QUARTERSIZE)+'}');
    }
    
    $w.log("run test");
    
    // run everything 
    for (var jj=0;jj<NUMBEROFTESTS;jj++) {
        tests[jj] = TEST.dtogrid(tests[jj][0],tests[jj][1],QUARTERSIZE);
        r = TEST.knearest(tests[jj][0],tests[jj][1],green,blue,NUMBEROFSAMPLES);
        TEST.drawnearest(i,tests[jj][0],tests[jj][1],r);
    }
}
/**
 * WhatIsit
 * @param {Array}
 * @param {Array}
 * @returns {Void}
 * */
var WhatIsit = function(green,blue,qsize,tdotsize,gcolor,bcolor) {
    this.green = green;
    this.blue = blue;
    this.greenl = green.length;
    this.bluel = blue.length;
    this.alength = this.bluel + this.greenl;
    this.qsize = qsize;
    this.tdotsize = tdotsize;
    this.gcolor = gcolor;
    this.bcolor = bcolor;
}
/**
 * knearest
 * @param {Number}
 * @param {Number}
 * @param {Array}
 * @param {Array}
 * @param {Number}
 * @returns {Array}
 * */
WhatIsit.prototype.knearest = function(x,y,green,blue,th) {
    
    var ro = {},re = [],c=0;
    
    green = this.merge(green,blue);
    
    for(var h=0;h<this.alength;h++) {
        
        // get the current distance
        cnow=$w.motion.distance_to_point(x,y,green[h][0],green[h][1]);
        
        // if the current distance to target is less than the closest target
        if (h<(this.alength-this.bluel)) {
            green[h][2] = 0; // green
        }else{
            green[h][2] = 1; // blue
        }
        // flatten the distance and make it a key in the object
        ro[Math.floor(cnow)] = green[h];
    }
    // @todo maybe there is a better way to get these without looping through them all...(maybe not)
    // the object will be ordered by distance
    for(var h in ro) {
        // so I can grab the sample size requested by th
        // if th is met then we are done
        if (c>=th) continue;
        // move the count forward
        c++;
        // add the result to the array
        re.push(ro[h]);
    }
    //
    return re;
}
/**
 * merge
 * merges two arrays
 * @param {Array}
 * @param {Array}
 * @returns {Array}
 * */
WhatIsit.prototype.merge = function(a,b) {
    // get a length
    var al = a.length;
    // get b length
    var bl = al+b.length;
    // 
    var hh=0,jj = 0;
    // start the loop where a ends
    for (hh=al;hh<bl;hh++) {
        // add b to a with a the key jj
        a[hh] = b[jj++];
    }
    return a;
}
/**
 * drawnearest
 * determines what the test subject is based on a vote of the nearest n samples
 * @param {Number} the ctx reference
 * @param {Number} x
 * @param {Number} y
 * @param {Array} the samples
 * @returns {Void}
 * */
WhatIsit.prototype.drawnearest = function(i,x,y,r) {
    
    var green = 0, blue = 0
    
    l = r.length;
    
    for(j=0;j<l;j++) {
        // check the 3rd param
        if (r[j][2] == 0) {
            green++; // increment green
        }else{
            blue++; // increment blue
        }
        // draw a line to the sample from the test
        $w.canvas.line(i,r[j][0],r[j][1],x,y,'#000000');
    }
    var color;
    if (green > blue) {
        // It's a Green!!
        $w.log("It's a Green!!!");
        color = this.gcolor;
    }else{
        // It's a Blue!!
        $w.log("It's a Blue!!!");
        color = this.bcolor;
    }
    // draw a dot where the test is base don the winning color
    $w.canvas.circle(i,x,y,this.tdotsize,color);
}
/**
 * dtogrid
 * converts a coord to a location on the canvas for drawing
 * @param {Number} x
 * @param {Number} x
 * @returns {Array}
 * */
WhatIsit.prototype.dtogrid = function(x,y) {
    x+=this.qsize;
    y+=this.qsize;
    return [x,y];       
}