var first_time = true;
var ninja, ropes = [];
var running;
var ctx;
var gravity = v_(0, 0.4);
var balls_begin, balls_end, winds_begin, winds_end;
var origin = v_(0, 0);
var rope_ndx;
var images = [];

// UI ///////////////////////////////////////////////////////////////////////  

function return_from_help() {
    xhide("help-screen");
}

function return_from_horrible() {
    xhide("horrible-info");
}

function xshow(e)
{
    _(e).style.opacity = 0;
    _(e).style.display = "block";
    $("#"+e).animate( { opacity: 1 }, 600 );
}

function xhide(e)
{
    $("#"+e).animate( { opacity: 0 }, 600, function() {
        _(e).style.display = "none";
    } );
}

function show_top20() {
    _("top-list-content").innerHTML = "<div style='position: absolute; top: 120px; left: 110px; width: 100px;'><img src=\"img/progress4.gif\"/></div>";
    read_top_ninjas();
    xshow("top-list");
}

function show_horrible_info() {
    xshow("horrible-info");
}

function return_from_score() {
    xhide("top-list");
}

function help() {
    stop_game();
    xshow("startmenu");
}

function game_over() {
    stop_game();
    if (origin.x < 0) origin.x = 0;
    var fscore = origin.x/100;
    _("final-score").innerHTML = fscore.toFixed(2);
    _("top-score").style.display = "block";
    _("top-list-content").style.display = "block";
    xshow("game-over");
}

function retry_game() {
    var name = _("score-input").value;
    if ((name.length > 0) && (name.length < 15))
    {
        createCookie("ninja-extreme-name", name);
        update_top_scores(name, origin.x);
        initialize_game();
        xhide("game-over");
    }
    else
        alert("Name should have length from 1 to 15 symbols.");
}

function play_game() {
    if (first_time) { xshow("help-screen"); first_time = false; }
    xhide("startmenu");
}

// Main /////////////////////////////////////////////////////////////////////

function do_click(e) {
    if (!running) return;
    var m = mouse_rel_pos(this, e);
    m = v_add(m, origin);
    var r = ropes[rope_ndx];
    if (r.state == 0 && r.step == 0) {
        throw_rope(r, m);
        rope_ndx = 1 - rope_ndx;
    }
}

function initialize_game() {
    _("score-input").value = readCookie("ninja-extreme-name");

    window.scrollTo(0, 1);
    _("ft").innerHTML = "0.00 YD";
    origin = v_(0, 0);
    rope_ndx = 0;
    winds_begin = balls_begin = 0;
    winds_end = balls_end = 3;

    for (var i = 0; i < balls.length; ++i) {
        balls[i].xdx = i % 8;
    }
    
    ninja = create_ninja(20, 150);
    ropes[0] = create_rope(ninja.hands[1]);
    ropes[1] = create_rope(ninja.hands[0]);
    paint_game(ctx);
}

function run_game() {
    if (running) stop_game();
    running = setInterval(process_game, 50);
}

function process_game() {
    update_game();
    paint_game(ctx);
}

function stop_game() {
    clearInterval(running);
    running = null;
}

function collide_rope(rope) {
    if (!rope.binded && rope.state == 1) {
        for (var i = balls_begin; i < balls_end; ++i) {
            var p = balls[i];
            var d = v_dist(rope.last.pos, p.pos);
            if (d <= p.r) {
                bind_rope(rope);
            }
        }
    }
}

var wdirs =
[
    v_(0, -1),
    v_(0.7, -0.7),
    v_(1, 0),
    v_(0.7, 0.7),
    v_(0, 1),
    v_(-0.7, 0.7),
    v_(-1, 0),
    v_(-1, -0.7)
];

function collide_wind() {

    for (var i = winds_begin; i < winds_end; ++i) {
        var p = windirs[i];
        var d = v_dist(ninja.shoulder.pos, p.pos);
        if (d <= p.r) {
            v_add_ref(ninja.shoulder.force, v_scale(wdirs[p.i], 8));
        }
    }
}

function update_game() {
    apply_force(ninja.points, gravity);
    collide_wind();

    integrate(ninja.points, 0.025);

    for (var i = 0; i < ropes.length; ++i) integrate(ropes[i].points, 0.1);

    for (var k = 0; k < 1; ++k ) for (var i = 0; i < ropes.length; ++i)
        if (ropes[i].state != 0) satisfy_inv(ropes[i].constraints, 0.055);

    for (var k = 0; k < 3; ++k ) satisfy(ninja.constraints);

    for (var k = 0; k < 1; ++k ) for (var i = 0; i < ropes.length; ++i)
        if (ropes[i].state == 0) satisfy2(ropes[i].constraints, 0.0, 1.0-(ropes[i].step/10.0));

    for (var i = 0; i < ropes.length; ++i) {
        collide_rope(ropes[i]);
        update_rope(ropes[i]);
    }

    clear_forces(ninja.points);
    for (var i = 0; i < ropes.length; ++i) clear_forces(ropes[i].points);

    var mx = ninja.shoulder.pos.x - 160;
    origin.x += 1;
    if (mx > origin.x) origin.x = mx;

    balls_begin = regions[Math.floor(origin.x/200)].begin;
    balls_end = regions[Math.floor((origin.x+320)/200)].end;
    winds_begin = regions[Math.floor(origin.x/200)].wbegin;
    winds_end = regions[Math.floor((origin.x+320)/200)].wend;

    _("ft").innerHTML = (origin.x/100).toFixed(2) + " YD";

    var fin = false;
    fin = fin || (ninja.shoulder.pos.x < origin.x-100);
    fin = fin || (ninja.shoulder.pos.x > origin.x+320+100);
    fin = fin || (ninja.shoulder.pos.y > origin.y+280+100);
    fin = fin || (ninja.shoulder.pos.y < origin.y-100);

    if (fin) game_over();
}

function paint_game(ctx) {
    ctx.clearRect(0, 0, 320, 280);
    ctx.save();
    ctx.translate(-origin.x, origin.y);

    ctx.strokeStyle = '#08f' ;
    for (var i = winds_begin; i < winds_end; ++i) {
        var p = windirs[i];
        ctx.beginPath();
        ctx.arc(p.pos.x, p.pos.y, p.r, 0, Math.PI*2, 0);
        ctx.stroke();
        ctx.drawImage(images[7 + p.i], p.pos.x-20, p.pos.y-20);
    }

    ctx.fillStyle = '#fff' ;
    ctx.strokeStyle = '#fff' ;
    for (var i = balls_begin; i < balls_end; ++i) {
        var p = balls[i];
        ctx.drawImage(images[6], p.pos.x-p.r, p.pos.y-p.r);
    }

    paint_constraints(ninja.constraints, ctx, "#000", 3);
    var cc = "#fff";
    for (var i = 0; i < ropes.length; ++i) {
        paint_constraints(ropes[i].constraints, ctx, cc, 1);
    }
    ctx.restore();
}

// Ninja ////////////////////////////////////////////////////////////////////

function create_ninja(x, y) {
    var s = 2;

    var ninja = {};
    ninja.points = [];
    ninja.constraints = [];
    ninja.arms = [];
    ninja.hands = [];
    ninja.legs = [];
    ninja.foots = [];

    function create_ninja_side(ninja, i) {
        var n = i > 0 ? 1 : -1;
        ninja.points.push(ninja.arms[i] = create_point_xy(x+n*s*4, y+s*4));
        ninja.constraints.push(create_rigid_constraint(ninja.shoulder, ninja.arms[i]));
        ninja.points.push(ninja.hands[i] = create_point_xy(x+n*s*8, y+s*4));
        ninja.constraints.push(create_rigid_constraint(ninja.arms[i], ninja.hands[i]));
        ninja.points.push(ninja.legs[i] = create_point_xy(x+n*s*3, y+s*12));
        ninja.constraints.push(create_rigid_constraint(ninja.pelvis, ninja.legs[i]));
        ninja.points.push(ninja.foots[i] = create_point_xy(x+n*s*4, y+s*16));
        ninja.constraints.push(create_rigid_constraint(ninja.legs[i], ninja.foots[i]));
    }

    ninja.points.push(ninja.head = create_point_xy(x, y));
    ninja.points.push(ninja.shoulder = create_point_xy(x, y+s*4));
    ninja.constraints.push(create_rigid_constraint(ninja.head, ninja.shoulder));
    ninja.points.push(ninja.pelvis = create_point_xy(x, y+s*8));
    ninja.constraints.push(create_rigid_constraint(ninja.shoulder, ninja.pelvis));
    create_ninja_side(ninja, 0);
    create_ninja_side(ninja, 1);

    ninja.constraints.push(create_rigid_constraint(ninja.head, ninja.pelvis));
    ninja.constraints[ninja.constraints.length-1].visible = false;

    ninja.constraints.push(create_rigid_constraint(ninja.legs[0], ninja.foots[1]));
    ninja.constraints[ninja.constraints.length-1].type = 1;
    ninja.constraints[ninja.constraints.length-1].visible = false;

    return ninja;
}

// Rope ////////////////////////////////////////////////////////////////////

function create_rope(point) {
    var dir = v_(0, -1);
    var rope = {};
    // 0 - free, 1 - throwing, 2 - binded
    rope.state = 0;
    rope.step = 0;
    rope.points = [];
    rope.constraints = [];
    rope.binded = false;
    var p0, p = point;
    for (var i = 1; i < 8; ++i) {
        p0 = p;
        rope.points.push(p = create_point(v_mad(point.pos, dir, 10*i/7)));
        if (!rope.first) rope.first = p;
        rope.constraints.push(create_rigid_constraint(p0, p, 1, 0, 1.0));
    }
    rope.last = p;
    rope.last_constraint = rope.constraints[rope.constraints.length-1];

    rope.constraints[0].a = 0;
    rope.last_constraint.a = 0.5;

    return rope;
}

function bind_rope(rope) {
    rope.constraints[0].a = 0.6;
    rope.last_constraint.a = 1;
    rope.binded = true;
    rope.bind_pos = v_c(rope.last.pos);
    rope.state = 2;
    rope.step = 20;
}

function update_rope(rope) {
    if (rope.state == 1) {
        if (rope.step == 0) {
//          bind_rope(rope);
            rope.binded = false;
            rope.constraints[0].a = 0;
            rope.last_constraint.a = 0.5;
            rope.state = 2;
            rope.step = 15;
        }
        else --rope.step;
    }
    if (rope.state == 2) {
        for (var i = 0; i < rope.constraints.length; ++i) {
            if (rope.constraints[i].distance > 1) rope.constraints[i].distance*=0.45;
            rope.constraints[i].type = 2;
        }
        if (rope.binded == true) rope.last.pos = v_c(rope.bind_pos);
        if (rope.step == 0) {
            rope.state = 0;
            rope.constraints[0].a = 0;
            rope.last_constraint.a = 0.5;
            rope.binded = false;
            rope.step = 10;
        }
        else --rope.step;
    }
    if (rope.state == 0) {
        if (rope.step == 0) {
        }
        else --rope.step;
    }
}

function throw_rope(rope, target) {
    rope.state = 1;
    rope.step = 12;
    rope.binded = false;
    for (var i = 0; i < rope.constraints.length; ++i) {
        rope.constraints[i].distance = 15;
        rope.constraints[i].type = 2;
    }
    rope.constraints[0].a = 0;
    rope.last_constraint.a = 1;
    rope.last.prev_pos = v_c(rope.last.pos);
    v_mad_ref(rope.last.force, v_nor(v_sub(target, rope.last.pos)), 20);
}

// Verlet ///////////////////////////////////////////////////////////////////

function apply_force(points, force) {
    for (var i = 0; i < points.length; ++i) {
        var p = points[i];
        v_mad_ref(p.force, force, p.mass);
    }
}

function clear_forces(points) {
    for (var i = 0; i < points.length; ++i) {
        var p = points[i];
        p.force.x = p.force.y = 0.0;
    }
}

function integrate(points, friction) {
    var f = friction || 0.02;
    for (var i = 0; i < points.length; ++i) {
        var p = points[i];
        var tx = p.pos.x; var ty = p.pos.y;
        p.pos.x = (2-f)*p.pos.x - (1-f)*p.prev_pos.x + p.force.x*p.inv_mass;
        p.pos.y = (2-f)*p.pos.y - (1-f)*p.prev_pos.y + p.force.y*p.inv_mass;
        p.prev_pos.x = tx; p.prev_pos.y = ty;
    }
}

function satisfy(constraints) {
    for (var i = 0; i < constraints.length; ++i) satisfy_rigid_constraint(constraints[i]);
}

function satisfy2(constraints, a, s) {
    for (var i = 0; i < constraints.length; ++i) satisfy_rigid_constraint2(constraints[i], a, s);
}

function satisfy2_inv(constraints, a, s) {
    for (var i = constraints.length-1; i >= 1; --i) satisfy_rigid_constraint2(constraints[i], a, s);
}

function satisfy_inv(constraints) {
    for (var i = constraints.length-1; i >= 0; --i) satisfy_rigid_constraint(constraints[i]);
}

function paint_points(points, ctx, color) {
    ctx.fillStyle = color || '#aaa' ;
    for (var i = 0; i < points.length; ++i) {
        var p = points[i];
        ctx.beginPath();
        ctx.arc(p.pos.x, p.pos.y, 4, 0, Math.PI*2, 0);
        ctx.fill();
    }
}

function paint_constraints(constraints, ctx, color, w) {
    ctx.strokeStyle = color || '#fff';
    ctx.lineWidth = w || 3;
    for (var j = 0; j < constraints.length; ++j) {
        var c = constraints[j];
        if (!c.visible) continue;
        ctx.beginPath();
        ctx.moveTo(c.point1.pos.x, c.point1.pos.y);
        ctx.lineTo(c.point2.pos.x, c.point2.pos.y);
        ctx.stroke();
    }
}

// Point ///////////////////////////////////////////////////////////////

function create_point_xy(x, y, mass) {
    return create_point(v_(x, y), mass);
}

function create_point(pos, mass) {
    mass = mass || 1.0;
    return { pos: pos, prev_pos: v_c(pos), force: v_(0.0, 0.0), mass: mass, inv_mass: 1.0/mass };
}

// Constraint ///////////////////////////////////////////////////////////////

function create_rigid_constraint(point1, point2, distance, type, stiffness) {
    var c = {};
    c.point1 = point1; c.point2 = point2;
    c.distance = distance || v_dist(point1.pos, point2.pos);
    c.type = type || 0.0;
    c.stiffness = stiffness || 1.0;
    c.a = point2.mass/(point1.mass+point2.mass);
    c.visible = true;
    return c;
}

function satisfy_rigid_constraint(constraint) {
    var delta = v_sub(constraint.point2.pos, constraint.point1.pos);
    var deltalength = v_len(delta);
    if (deltalength < 0.001) return;
    if (constraint.type == 1 && deltalength > constraint.distance ) return;
    if (constraint.type == 2 && deltalength < constraint.distance ) return;
    var diff = (deltalength-constraint.distance)/deltalength*constraint.stiffness;
    v_mad_ref(constraint.point1.pos, delta, diff*constraint.a);
    v_mad_ref(constraint.point2.pos, delta, -diff*(1-constraint.a));
}

function satisfy_rigid_constraint2(constraint, a, stiffness) {
    var delta = v_sub(constraint.point2.pos, constraint.point1.pos);
    var deltalength = v_len(delta);
    if (deltalength < 0.001) return;
    if (constraint.type == 1 && deltalength > constraint.distance ) return;
    if (constraint.type == 2 && deltalength < constraint.distance ) return;
    var diff = (deltalength-constraint.distance)/deltalength*stiffness;
    v_mad_ref(constraint.point1.pos, delta, diff*a);
    v_mad_ref(constraint.point2.pos, delta, -diff*(1-a));
}

////////////////////////////////////////////////////////////////////////////////
// sql.js
////////////////////////////////////////////////////////////////////////////////

function get_top_list()
{
    $.getJSON(
      'top_scores.php',
      { params: "text" },
      onAjaxSuccess
    );

    function onAjaxSuccess(obj)
    {
        show_top_ninjas(obj.top_score);
    }
}

function update_top_scores(name, score)
{
    $.post(
      'update_top_scores.php',
      { name: name, score: score.toFixed(0) },
      onAjaxSuccess
    );

    function onAjaxSuccess(obj)
    {                          
//      alert(obj);
    }
}

function read_top_ninjas()
{
    get_top_list();
}

function show_top_ninjas(toplist)
{
    _("top-list-content").style.display = "none";

    for (var i = 0; i < 15; ++i) {
        $("#top-name"+(i+1)).text(toplist[i].name);
        $("#top-score"+(i+1)).text((toplist[i].score/100).toFixed(2));
    }
}

////////////////////////////////////////////////////////////////////////////////
// math.js
////////////////////////////////////////////////////////////////////////////////

function rndf(a, b)
{
    return Math.random()*(b-a) + a;
}

function rndi(a, b)
{
    return Math.floor(rndf(a,b));
}

function rand(RandSeed)
{
    var rnge = Math.pow(2, 32);
    RandSeed = ((134775813 * RandSeed + 1) % rnge);
    return [RandSeed, RandSeed/rnge];
}

function clampIndex(index, maxIndex)
{
    index += maxIndex; 
    return index % maxIndex; 
}


function mouse_rel_pos(o, ev)
{
    var x = 0, y = 0;
    while ( o != null )
    {
        x += o.offsetLeft;
        y += o.offsetTop;
        o = o.offsetParent;
    }
    return { x: ev.pageX-x, y: ev.pageY-y };
}


function _(id) { return document.getElementById(id); }

// Math
function v_(x, y) { return {x:x, y:y}; }
function v_c(a) { return {x:a.x, y:a.y}; }
function v_add(a, b) { return {x:a.x + b.x, y:a.y + b.y}; }
function v_add_ref(a, b) { a.x += b.x, a.y += b.y; }
function v_sub(a, b) { return {x:a.x - b.x, y:a.y - b.y}; }
function v_scale(a, b) { return {x:a.x * b, y:a.y * b}; }
function v_rshift(a, s) { return {x:a.x >> s, y:a.y >> s}; }
function v_mad(a, b, c) { return {x:a.x + b.x * c, y: a.y + b.y * c}; }
function v_mad_ref(a, b, c) { a.x += b.x * c; a.y += b.y * c; }
function v_dot(a, b) { return a.x * b.x + a.y * b.y; }
function v_len(a) { return Math.sqrt(a.x * a.x + a.y * a.y); }
function v_len2(a) { return a.x * a.x + a.y * a.y; }
function v_nor(a) { var inv_len = 1.0/Math.sqrt(a.x * a.x + a.y * a.y); return {x:a.x * inv_len, y:a.y * inv_len}; }
function v_norm2(a) { return a.x * a.x + a.y * a.y; }
function v_len(a) { return Math.sqrt(a.x * a.x + a.y * a.y); }
function v_crs(a) { return {x:-a.y, y:a.x}; }
function v_crs2(a) { return {x:a.y, y:a.x}; }

function v_dist2(a, b)
{
    var dx = b.x - a.x;
    var dy = b.y - a.y;
    return dx*dx + dy*dy;
}

function v_dist(a, b)
{
    var dx = b.x - a.x;
    var dy = b.y - a.y;
    return Math.sqrt(dx*dx + dy*dy);
}

////////////////////////////////////////////////////////////////////////////////
// cook.js
////////////////////////////////////////////////////////////////////////////////

function createCookie(name,value,days) {
  var expires = "";
 
  if (days) {}
  else days = 365;

  var date = new Date();
  date.setDate(date.getDate() + days);
  expires = "; expires=" + date.toGMTString();

  document.cookie = name + "=" + value + expires + "; path=/";
}
 
function readCookie(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
 
  for(var i = 0; i < ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0) == ' ') c = c.substring(1, c.length);
    if (c.indexOf(nameEQ) == 0) {
      var v = c.substring(nameEQ.length, c.length);
 
      return v;
    }
  }
  
  return "Ninja";
}

////////////////////////////////////////////////////////////////////////////////
// screenlayout.js
////////////////////////////////////////////////////////////////////////////////

// As seen on mediatemple account center. Thanks (mt)!

// The following code is courtesy of Joe Hewitt http://www.joehewitt.com

var currentWidth = 0;
    
function updateLayout()
{  
  if (window.innerWidth != currentWidth)
  {
    currentWidth = window.innerWidth;

    var agent = navigator.userAgent.toLowerCase();
    var isIphone = agent.indexOf('iphone') != -1;

    if (isIphone)
    {
      var orient = currentWidth == 320 ? "profile" : (currentWidth > 1000 ? "profile" : "landscape");
      if (orient=="landscape") { stop_game(); }
      document.body.setAttribute("orient", orient);
    } else {
      document.body.setAttribute("orient", "profile");      
    }
    window.scrollTo(0, 1);
  }
}

// end Joe Hewitt code. Thanks Joe!


