var phpjsCompiler = Base.extend({
    functionUrl:  'http://phpjs.org/functions/definitions.json',
    mainDivId:    'cmpMain',
    logDivId:     'cmpLog',
    dbgPreId:     'cmpDebug',
    infoDivId:    'cmpInfo',
    hashDivId:    'cmpHash',
    jsonInpId:    'cmpJson',
    hashInpId:    'PackageHash',
    logOptionsId: 'cmpOptions',
    packSuffixId: 'cmpPackSuffix',

    debug:           false,
    selection:       {},
    selectionByHand: {},
    isSupporting:    {},
    options:         {},

    presets:             {},
    functionDefinitions: {},
    functionCnt:         0,
    categoryDefinitions: {},
    mainDiv:             '',
    logDiv:              '',
    dbgPre:              '',
    infoDiv:             '',
    hashDiv:             '',
    hashInp:             '',
    jsonInp:             '',
    optionsDiv:          '',
    packSuffixSpan:       '',
    columns:             3,
    
    flash: function(functionName, bgColor, fgColor) {
        this.log('flash:' +functionName);

        if (!bgColor) {bgColor = '#BDE5F8';}
        if (!fgColor) {fgColor = '#00529B';}
        
        $j('#'+functionName+'').next().css({
            'color': fgColor,
            'background-color': bgColor,
            'font-weight': 'bolder'
        });
        
        $j('#'+functionName+'').next().fadeOut(800).fadeIn(800).fadeOut(400).fadeIn(400).fadeOut(400).fadeIn(400).css({
            'color': '#000000',
            'background-color': '#ffffff',
            'font-weight': 'normal'
        });
    },
    
    constructor: function() {
        this.mainDiv        = $j('#'+this.mainDivId);
        this.logDiv         = $j('#'+this.logDivId);
        this.dbgPre         = $j('#'+this.dbgPreId);
        this.infoDiv        = $j('#'+this.infoDivId);
        this.hashDiv        = $j('#'+this.hashDivId);
        this.hashInp        = $j('#'+this.hashInpId);
        this.jsonInp        = $j('#'+this.jsonInpId);
        this.optionsDiv     = $j('#'+this.logOptionsId);
        this.packSuffixSpan = $j('#'+this.packSuffixId);

    },

    setOption: function(key, val) {
        this.log('option '+key+' = '+val);
        this.options[key] = val;
        this.calcShowHash();
    },
    
    calcShowHash: function() {
        this.hashInp.val(this.calcHash());
        this.packSuffixSpan.html(this.calcSuffix());
    },

    calcSuffix: function() {
        var suffix = '';

        if (this.options['namespaced'] == 'yes') {
            suffix += '.namespaced';
        }
        if (this.options['compression'] == 'minified') {
            suffix += '.min';
        }
        if (this.options['compression'] == 'packed') {
            suffix += '.pack';
        }

        suffix += '.js';

        return suffix;

    },

    calcHash: function() {
        var json;
        this.options['protected'] = 'no';
        
        ksort(this.options);
        ksort(this.selection);

        var config = {
            options: this.options,
            selection: this.selection
        };

        json = json_encode(config);

        if (this.debug){
            this.jsonInp.val(json);
        }
        
        return md5(json);
    },

    setPresets: function(presets) {
        this.presets = presets;
    },

    setDebug: function(debug) {
        this.debug = debug;
    },

    activatePreset: function(presetCode) {
        if (!this.presets[presetCode]) {
            this.info('Preset '+presetCode+' not found');
            return false;
        }
        this.setSelection(this.presets[presetCode].selection);
        return true;
    },

    setDefinitions: function(definitions) {
        var categoryCode, categoryName, functionName;
        
        this.functionDefinitions = definitions;
        ksort(this.functionDefinitions);

        this.functionCnt = 0;
        for (functionName in this.functionDefinitions) {
            this.selection[functionName] = 0;
            this.isSupporting[functionName] = {};

            categoryCode = this.functionDefinitions[functionName]['category'];
            categoryName = categoryCode;

            if (!this.categoryDefinitions[categoryCode]) {
                this.categoryDefinitions[categoryCode] = new Array();
            }
            this.categoryDefinitions[categoryCode]['categoryName'] = categoryName;
            if (!this.categoryDefinitions[categoryCode]['functions']) {
                this.categoryDefinitions[categoryCode]['functions'] = {};
            }
            this.categoryDefinitions[categoryCode]['functions'][functionName] = true;
            this.functionCnt++;
        }
        ksort(this.categoryDefinitions);


        // Bind
        $j('input.function').click(function(){
            var functionName = $j(this).attr('id');
            if (this.checked) {
                Compiler.select(functionName, false);
            } else {
                Compiler.unselect(functionName, false);
            }
        });
        // Bind
        $j('input.option').click(function(){
            Compiler.setOption($j(this).attr('optname'), $j(this).val());
        });
        // Bind
        $j('#preset').change(function(){
            Compiler.activatePreset($j(this).val());
        });
        // Bind
        $j('#makeJson').click(function(){
            Compiler.showJson();
        });

        // Hack to calc hash for options
        $j('input.option:checked').trigger('click');
    },

    showJson: function() {
        this.jsonInp.val(json_encode(this.selection));
    },

    toggle: function(functionName, automatic, action) {
        // Setup
        var depFunctionName;
        var checkbox;
        var stillSupporting = [];
        if (!this.functionDefinitions[functionName]) {
            this.log('Function: "' + functionName + '" not found!');
            return false;
        }
        checkbox = $j('#' + functionName);

        // Are we manually unselecting a supporting function? Not allowed!
        if (action != 'select') {
            for (depFunctionName in this.isSupporting[functionName]) {
                stillSupporting.push(depFunctionName);
                this.flash(depFunctionName, 'red');
            }
            
            if (stillSupporting.length > 0) {
                if (!automatic) {
                    this.info('Please remove: '+ stillSupporting.join(' &amp; ')+ ', before: '+functionName);
                }
                if (!checkbox.is(':checked')){
                    // Manage UI
                    this._select(functionName, 1);
                }
                return false;
            }
        }

        if (automatic) {
            this.log('auto'+action+ 'ing ' + functionName)
        }
        
        // Trigger dependencies
        if (false !== this.functionDefinitions[functionName]['dependencies']) {
            for (depFunctionName in this.functionDefinitions[functionName]['dependencies']) {
                if (action != 'select' && this.selectionByHand[depFunctionName]) {
                    this.info('Will not NOT auto'+action+ ' ' + depFunctionName + ' for '+functionName+'. selected by hand.');
                    continue;
                }

                this.log('auto'+action+ 'ing ' + depFunctionName + ' for '+functionName)
                if (action == 'select') {
                    this.isSupporting[depFunctionName][functionName] = 1;
                } else {
                    if (this.isSupporting[depFunctionName][functionName]) {
                        delete(this.isSupporting[depFunctionName][functionName]);
                    }
                }
                this.toggle(depFunctionName, true, action);
                
            }
        }

        // Update User interface, if automatic
        if (action == 'select') {
            if (!automatic) this.selectionByHand[functionName] = 1;
            this._select(functionName, 1);
        } else {
            if (!automatic) delete(this.selectionByHand[functionName]);
            this._select(functionName, 0);
        }

        if (!automatic) {
            // User interface call. You can recalc HASH now.
            this.calcShowHash();
        }

        this.doDebug();

        return true;
    },

    select: function(functionName, automatic) {
        this.toggle(functionName, automatic, 'select');
    },

    unselect: function(functionName, automatic) {
        this.toggle(functionName, automatic, 'unselect');
    },

    _select: function(functionName, on) {
        var checkbox = $j('#'+functionName);
        if (on == 1) {
            this.selection[functionName] = 1;
            if (!checkbox.is(':checked')){
                $j('#'+functionName).attr('checked', true);
            }
        } else {
            delete (this.selection[functionName]);
            if (checkbox.is(':checked')){
                $j('#'+functionName).attr('checked', false);
            }
        }
    },

    buildInitialSupporting: function () {
        var depFunctionName, functionName;

        // Reset
        for (functionName in this.functionDefinitions) {
            this.isSupporting[functionName] = {};
        }

        // Add currently selected dependencies
        for (functionName in this.functionDefinitions) {
            if (false !== this.functionDefinitions[functionName]['dependencies']) {
                for (depFunctionName in this.functionDefinitions[functionName]['dependencies']) {
                    if (1 == this.selection[functionName]) {
                        this.isSupporting[depFunctionName][functionName] = 1;
                    }
                }
            }
        }
    },

    setSelection: function(selection) {
        var functionName, functionName2;

        // Uncheck everything
        this.selection = {};
        this.selectionByHand = {};

        for (functionName in this.functionDefinitions) {
            this._select(functionName, 0);
        }

        for (functionName in selection) {
            if (functionName == '*') {
                for (functionName2 in this.functionDefinitions) {
                    this._select(functionName2, selection[functionName]);
                }
                continue;
            }

            this._select(functionName, selection[functionName]);
        }
        this.calcShowHash();
        this.buildInitialSupporting();
    },

    doDebug: function() {
        return true;
        var args = Array.prototype.slice.call(arguments);
        var i = 0;

        if (args.length == 0) {
            //var dat = {'isSupporting': this.isSupporting, 'selectionByHand': this.selectionByHand};
            var dat = {'selection': this.selection, 'options': this.options};
            this.doDebug(dat);
            return true;
        }

        this.dbgPre.html('');
        for (i = 0; i < args.length; i++) {
            this.dbgPre.append('<hr />' + print_r(args[i], true) + '');
        }
        return true;
    },

    info: function(str) {
        this.infoDiv.prepend('<li>'+str+'</li>');
    },

    log: function(str) {
        this.logDiv.prepend('<li> - '+str+'</li>');
    }
});

