var contexts = require(“../contexts”),

Visitor = require("./visitor"),
ImportSequencer = require("./import-sequencer");

var ImportVisitor = function(importer, finish) {

this._visitor = new Visitor(this);
this._importer = importer;
this._finish = finish;
this.context = new contexts.Eval();
this.importCount = 0;
this.onceFileDetectionMap = {};
this.recursionDetector = {};
this._sequencer = new ImportSequencer(this._onSequencerEmpty.bind(this));

};

ImportVisitor.prototype = {

isReplacing: false,
run: function (root) {
    try {
        // process the contents
        this._visitor.visit(root);
    }
    catch(e) {
        this.error = e;
    }

    this.isFinished = true;
    this._sequencer.tryRun();
},
_onSequencerEmpty: function() {
    if (!this.isFinished) {
        return;
    }
    this._finish(this.error);
},
visitImport: function (importNode, visitArgs) {
    var inlineCSS = importNode.options.inline;

    if (!importNode.css || inlineCSS) {

        var context = new contexts.Eval(this.context, this.context.frames.slice(0));
        var importParent = context.frames[0];

        this.importCount++;
        if (importNode.isVariableImport()) {
            this._sequencer.addVariableImport(this.processImportNode.bind(this, importNode, context, importParent));
        } else {
            this.processImportNode(importNode, context, importParent);
        }
    }
    visitArgs.visitDeeper = false;
},
processImportNode: function(importNode, context, importParent) {
    var evaldImportNode,
        inlineCSS = importNode.options.inline;

    try {
        evaldImportNode = importNode.evalForImport(context);
    } catch(e) {
        if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
        // attempt to eval properly and treat as css
        importNode.css = true;
        // if that fails, this error will be thrown
        importNode.error = e;
    }

    if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) {

        if (evaldImportNode.options.multiple) {
            context.importMultiple = true;
        }

        // try appending if we haven't determined if it is css or not
        var tryAppendLessExtension = evaldImportNode.css === undefined;

        for (var i = 0; i < importParent.rules.length; i++) {
            if (importParent.rules[i] === importNode) {
                importParent.rules[i] = evaldImportNode;
                break;
            }
        }

        var onImported = this.onImported.bind(this, evaldImportNode, context),
            sequencedOnImported = this._sequencer.addImport(onImported);

        this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo,
            evaldImportNode.options, sequencedOnImported);
    } else {
        this.importCount--;
        if (this.isFinished) {
            this._sequencer.tryRun();
        }
    }
},
onImported: function (importNode, context, e, root, importedAtRoot, fullPath) {
    if (e) {
        if (!e.filename) {
            e.index = importNode.index; e.filename = importNode.currentFileInfo.filename;
        }
        this.error = e;
    }

    var importVisitor = this,
        inlineCSS = importNode.options.inline,
        isPlugin = importNode.options.plugin,
        isOptional = importNode.options.optional,
        duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector;

    if (!context.importMultiple) {
        if (duplicateImport) {
            importNode.skip = true;
        } else {
            importNode.skip = function() {
                if (fullPath in importVisitor.onceFileDetectionMap) {
                    return true;
                }
                importVisitor.onceFileDetectionMap[fullPath] = true;
                return false;
            };
        }
    }

    if (!fullPath && isOptional) {
        importNode.skip = true;
    }

    if (root) {
        importNode.root = root;
        importNode.importedFilename = fullPath;

        if (!inlineCSS && !isPlugin && (context.importMultiple || !duplicateImport)) {
            importVisitor.recursionDetector[fullPath] = true;

            var oldContext = this.context;
            this.context = context;
            try {
                this._visitor.visit(root);
            } catch (e) {
                this.error = e;
            }
            this.context = oldContext;
        }
    }

    importVisitor.importCount--;

    if (importVisitor.isFinished) {
        importVisitor._sequencer.tryRun();
    }
},
visitRule: function (ruleNode, visitArgs) {
    if (ruleNode.value.type === "DetachedRuleset") {
        this.context.frames.unshift(ruleNode);
    } else {
        visitArgs.visitDeeper = false;
    }
},
visitRuleOut : function(ruleNode) {
    if (ruleNode.value.type === "DetachedRuleset") {
        this.context.frames.shift();
    }
},
visitDirective: function (directiveNode, visitArgs) {
    this.context.frames.unshift(directiveNode);
},
visitDirectiveOut: function (directiveNode) {
    this.context.frames.shift();
},
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
    this.context.frames.unshift(mixinDefinitionNode);
},
visitMixinDefinitionOut: function (mixinDefinitionNode) {
    this.context.frames.shift();
},
visitRuleset: function (rulesetNode, visitArgs) {
    this.context.frames.unshift(rulesetNode);
},
visitRulesetOut: function (rulesetNode) {
    this.context.frames.shift();
},
visitMedia: function (mediaNode, visitArgs) {
    this.context.frames.unshift(mediaNode.rules[0]);
},
visitMediaOut: function (mediaNode) {
    this.context.frames.shift();
}

}; module.exports = ImportVisitor;