// // index.js // Should expose the additional browser functions on to the less object // var addDataAttr = require(“./utils”).addDataAttr,

browser = require("./browser");

module.exports = function(window, options) {

var document = window.document;
var less = require('../less')();

//module.exports = less;
less.options = options;
var environment = less.environment,
    FileManager = require("./file-manager")(options, less.logger),
    fileManager = new FileManager();
environment.addFileManager(fileManager);
less.FileManager = FileManager;

require("./log-listener")(less, options);
var errors = require("./error-reporting")(window, less, options);
var cache = less.cache = options.cache || require("./cache")(window, options, less.logger);
require('./image-size')(less.environment);

//Setup user functions
if (options.functions) {
    less.functions.functionRegistry.addMultiple(options.functions);
}

var typePattern = /^text\/(x-)?less$/;

function postProcessCSS(styles) { // deprecated, use a plugin for postprocesstasks
    if (options.postProcessor && typeof options.postProcessor === 'function') {
        styles = options.postProcessor.call(styles, styles) || styles;
    }
    return styles;
}

function clone(obj) {
    var cloned = {};
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            cloned[prop] = obj[prop];
        }
    }
    return cloned;
}

// only really needed for phantom
function bind(func, thisArg) {
    var curryArgs = Array.prototype.slice.call(arguments, 2);
    return function() {
        var args = curryArgs.concat(Array.prototype.slice.call(arguments, 0));
        return func.apply(thisArg, args);
    };
}

function loadStyles(modifyVars) {
    var styles = document.getElementsByTagName('style'),
        style;

    for (var i = 0; i < styles.length; i++) {
        style = styles[i];
        if (style.type.match(typePattern)) {
            var instanceOptions = clone(options);
            instanceOptions.modifyVars = modifyVars;
            var lessText = style.innerHTML || '';
            instanceOptions.filename = document.location.href.replace(/#.*$/, '');

            /*jshint loopfunc:true */
            // use closure to store current style
            less.render(lessText, instanceOptions,
                    bind(function(style, e, result) {
                        if (e) {
                            errors.add(e, "inline");
                        } else {
                            style.type = 'text/css';
                            if (style.styleSheet) {
                                style.styleSheet.cssText = result.css;
                            } else {
                                style.innerHTML = result.css;
                            }
                        }
                    }, null, style));
        }
    }
}

function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {

    var instanceOptions = clone(options);
    addDataAttr(instanceOptions, sheet);
    instanceOptions.mime = sheet.type;

    if (modifyVars) {
        instanceOptions.modifyVars = modifyVars;
    }

    function loadInitialFileCallback(loadedFile) {

        var data = loadedFile.contents,
            path = loadedFile.filename,
            webInfo = loadedFile.webInfo;

        var newFileInfo = {
            currentDirectory: fileManager.getPath(path),
            filename: path,
            rootFilename: path,
            relativeUrls: instanceOptions.relativeUrls};

        newFileInfo.entryPath = newFileInfo.currentDirectory;
        newFileInfo.rootpath = instanceOptions.rootpath || newFileInfo.currentDirectory;

        if (webInfo) {
            webInfo.remaining = remaining;

            var css = cache.getCSS(path, webInfo, instanceOptions.modifyVars);
            if (!reload && css) {
                webInfo.local = true;
                callback(null, css, data, sheet, webInfo, path);
                return;
            }

        }

        //TODO add tests around how this behaves when reloading
        errors.remove(path);

        instanceOptions.rootFileInfo = newFileInfo;
        less.render(data, instanceOptions, function(e, result) {
            if (e) {
                e.href = path;
                callback(e);
            } else {
                result.css = postProcessCSS(result.css);
                cache.setCSS(sheet.href, webInfo.lastModified, instanceOptions.modifyVars, result.css);
                callback(null, result.css, data, sheet, webInfo, path);
            }
        });
    }

    fileManager.loadFile(sheet.href, null, instanceOptions, environment, function(e, loadedFile) {
        if (e) {
            callback(e);
            return;
        }
        loadInitialFileCallback(loadedFile);
    });
}

function loadStyleSheets(callback, reload, modifyVars) {
    for (var i = 0; i < less.sheets.length; i++) {
        loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars);
    }
}

function initRunningMode() {
    if (less.env === 'development') {
        less.watchTimer = setInterval(function () {
            if (less.watchMode) {
                fileManager.clearFileCache();
                loadStyleSheets(function (e, css, _, sheet, webInfo) {
                    if (e) {
                        errors.add(e, e.href || sheet.href);
                    } else if (css) {
                        browser.createCSS(window.document, css, sheet);
                    }
                });
            }
        }, options.poll);
    }
}

//
// Watch mode
//
less.watch   = function () {
    if (!less.watchMode ) {
        less.env = 'development';
        initRunningMode();
    }
    this.watchMode = true;
    return true;
};

less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; };

//
// Synchronously get all <link> tags with the 'rel' attribute set to
// "stylesheet/less".
//
less.registerStylesheetsImmediately = function() {
    var links = document.getElementsByTagName('link');
    less.sheets = [];

    for (var i = 0; i < links.length; i++) {
        if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
            (links[i].type.match(typePattern)))) {
            less.sheets.push(links[i]);
        }
    }
};

//
// Asynchronously get all <link> tags with the 'rel' attribute set to
// "stylesheet/less", returning a Promise.
//
less.registerStylesheets = function() {
    return new Promise(function(resolve, reject) {
        less.registerStylesheetsImmediately();
        resolve();
    });
};

//
// With this function, it's possible to alter variables and re-render
// CSS without reloading less-files
//
less.modifyVars = function(record) {
    return less.refresh(true, record, false);
};

less.refresh = function (reload, modifyVars, clearFileCache) {
    if ((reload || clearFileCache) && clearFileCache !== false) {
        fileManager.clearFileCache();
    }
    return new Promise(function (resolve, reject) {
        var startTime, endTime, totalMilliseconds;
        startTime = endTime = new Date();

        loadStyleSheets(function (e, css, _, sheet, webInfo) {
            if (e) {
                errors.add(e, e.href || sheet.href);
                reject(e);
                return;
            }
            if (webInfo.local) {
                less.logger.info("loading " + sheet.href + " from cache.");
            } else {
                less.logger.info("rendered " + sheet.href + " successfully.");
            }
            browser.createCSS(window.document, css, sheet);
            less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms');
            if (webInfo.remaining === 0) {
                totalMilliseconds = new Date() - startTime;
                less.logger.info("less has finished. css generated in " + totalMilliseconds + 'ms');
                resolve({
                    startTime: startTime,
                    endTime: endTime,
                    totalMilliseconds: totalMilliseconds,
                    sheets: less.sheets.length
                });
            }
            endTime = new Date();
        }, reload, modifyVars);

        loadStyles(modifyVars);
    });
};

less.refreshStyles = loadStyles;
return less;

};