// Copyright (C) 2013 Massachusetts Institute of Technology
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 2,
// as published by the Free Software Foundation.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

// Scratch HTML5 Player
// Primitives.js
// Tim Mickel, July 2011

// Provides the basic primitives for the interpreter and loads in the more
// complicated primitives, e.g. MotionAndPenPrims.

'use strict';

var Primitives = function() {}

Primitives.prototype.addPrimsTo = function(primTable) {
    // Math primitives
    primTable["+"]        = function(b) { return interp.numarg(b, 0) + interp.numarg(b, 1) };
    primTable["-"]        = function(b) { return interp.numarg(b, 0) - interp.numarg(b, 1) };
    primTable["*"]        = function(b) { return interp.numarg(b, 0) * interp.numarg(b, 1) };
    primTable["/"]        = function(b) { return interp.numarg(b, 0) / interp.numarg(b, 1) };
    primTable["%"]        = function(b) { return interp.numarg(b, 0) % interp.numarg(b, 1) };
    primTable["randomFrom:to:"] = this.primRandom;
    primTable["<"]        = function(b) { return (interp.numarg(b, 0) < interp.numarg(b, 1)) };
    primTable["="]        = function(b) { return (interp.arg(b, 0) == interp.arg(b, 1)) };
    primTable[">"]        = function(b) { return (interp.numarg(b, 0) > interp.numarg(b, 1)) };
    primTable["&"]        = function(b) { return interp.boolarg(b, 0) && interp.boolarg(b, 1) };
    primTable["|"]        = function(b) { return interp.boolarg(b, 0) || interp.boolarg(b, 1) };
    primTable["not"]      = function(b) { return !interp.boolarg(b, 0) };
    primTable["abs"]      = function(b) { return Math.abs(interp.numarg(b, 0)) };
    primTable["sqrt"]     = function(b) { return Math.sqrt(interp.numarg(b, 0)) };

    primTable["\\\\"]               = this.primModulo;
    primTable["rounded"]            = function(b) { return Math.round(interp.numarg(b, 0)) };
    primTable["computeFunction:of:"] = this.primMathFunction;
    
    // String primitives
    primTable["concatenate:with:"]  = function(b) { return "" + interp.arg(b, 0) + interp.arg(b, 1) };
    primTable["letter:of:"]         = this.primLetterOf;
    primTable["stringLength:"]      = function(b) { return interp.arg(b, 0).length };
    
    new VarListPrims().addPrimsTo(primTable);
    new MotionAndPenPrims().addPrimsTo(primTable);
    new LooksPrims().addPrimsTo(primTable);
    new SensingPrims().addPrimsTo(primTable);
    new SoundPrims().addPrimsTo(primTable);
}
  
Primitives.prototype.primRandom = function(b) {
    var n1 = interp.numarg(b, 0);
    var n2 = interp.numarg(b, 1);
    var low = (n1 <= n2) ? n1 : n2;
    var hi = (n1 <= n2) ? n2 : n1;
    if(low == hi) return low;
    // if both low and hi are ints, truncate the result to an int
    if ((Math.floor(low) == low) && (Math.floor(hi) == hi)) {
        return low + Math.floor(Math.random() * ((hi + 1) - low));
    }
    return (Math.random() * (hi - low)) + low;
}
  
Primitives.prototype.primLetterOf = function(b) {
    var s = interp.arg(b, 1);
    var i = interp.numarg(b, 0) - 1;
    if ((i < 0) || (i >= s.length)) return "";
    return s.charAt(i);
}
  
Primitives.prototype.primModulo = function(b) {
    var modulus = interp.numarg(b, 1);
    var n = interp.numarg(b, 0) % modulus;
    if (n < 0) n += modulus;
    return n;
}

Primitives.prototype.primMathFunction = function(b) {
    var op = interp.numarg(b, 0);
    var n = interp.numarg(b, 1);
    switch(op) {
        case "abs": return Math.abs(n);
        case "sqrt": return Math.sqrt(n);
        case "sin": return Math.sin((Math.PI * n) / 180);
        case "cos": return Math.cos((Math.PI * n) / 180);
        case "tan": return Math.tan((Math.PI * n) / 180);
        case "asin": return (Math.asin(n) * 180) / Math.PI;
        case "acos": return (Math.acos(n) * 180) / Math.PI;
        case "atan": return (Math.atan(n) * 180) / Math.PI;
        case "ln": return Math.log(n);
        case "log": return Math.log(n) / Math.LN10; 
        case "e ^": return Math.exp(n);
        case "10 ^": return Math.exp(n * Math.LN10);
    }
    return 0;
}

