if (typeof XMLHttpRequest === 'undefined') {{
    var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
}}

var {class_name} = function(jsonFile) {{
    this.forest = undefined;

    var promise = new Promise(function(resolve, reject) {{
        var httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = function() {{
            if (httpRequest.readyState === 4) {{
                if (httpRequest.status === 200) {{
                    resolve(JSON.parse(httpRequest.responseText));
                }} else {{
                    reject(new Error(httpRequest.statusText));
                }}
            }}
        }};
        httpRequest.open('GET', jsonFile, true);
        httpRequest.send();
    }});

    var imax = function(nums) {{
        var index = 0;
        for (var i=0, l=nums.length; i < l; i++) {{
            index = nums[i] > nums[index] ? i : index;
        }}
        return index;
    }};

    var predict = function(tree, features, node) {{
        if (tree['thresholds'][node] != -2) {{
            if (features[tree['indices'][node]] <= tree['thresholds'][node]) {{
                return predict(tree, features, tree['childrenLeft'][node]);
            }} else {{
                return predict(tree, features, tree['childrenRight'][node]);
            }}
        }}
        return tree['classes'][node].slice();
    }};

    this.{method_name} = function(features) {{
        return new Promise(function(resolve, reject) {{
            promise.then(function(forest) {{
                if (typeof this.forest === 'undefined') {{
                    this.forest = forest;
                    this.nEstimators = this.forest.length;
                    this.nClasses = this.forest[0]['classes'][0].length;
                }}

                var preds = new Array(this.nEstimators).fill(new Array(this.nClasses).fill(0.));
                var i, j;
                for (i=0; i < this.nEstimators; i++) {{
                    preds[i] = predict(this.forest[i], features, 0);
                }}
                var classes = new Array(this.nClasses).fill(0.);
                for (i=0; i < this.nEstimators; i++) {{
                    var normalizer = 0.;
                    for (j=0; j < this.nClasses; j++) {{
                        normalizer += preds[i][j];
                    }}
                    if (normalizer == 0.) {{
                        normalizer = 1;
                    }}
                    for (j = 0; j < this.nClasses; j++) {{
                        preds[i][j] = preds[i][j] / normalizer;
                        if (preds[i][j] <= 2.2204460492503131e-16) {{
                            preds[i][j] = 2.2204460492503131e-16;
                        }}
                        preds[i][j] = Math.log(preds[i][j]);
                    }}
                    var sum = 0.;
                    for (j = 0; j < this.nClasses; j++) {{
                        sum += preds[i][j];
                    }}
                    for (j = 0; j < this.nClasses; j++) {{
                        preds[i][j] = (this.nClasses - 1) * (preds[i][j] - (1. / this.nClasses) * sum);
                    }}

                    for (j = 0; j < this.nClasses; j++) {{
                        classes[j] += preds[i][j];
                    }}
                }}
                resolve(imax(classes));
            }}, function(error) {{
                reject(error);
            }});
        }});
    }};

}};

if (typeof process !== 'undefined' && typeof process.argv !== 'undefined') {{
    if (process.argv[2].trim().endsWith('.json')) {{

        // Features:
        var features = process.argv.slice(3);

        // Parameters:
        var json = process.argv[2];

        // Estimator:
        var clf = new {class_name}(json);

        // Prediction:
        clf.{method_name}(features).then(function(prediction) {{
            console.log(prediction);
        }}, function(error) {{
            console.log(error);
        }});

    }}
}}