/** 
 * simulator.js
 * Hiper Ochinchin Time Simulator 
 * Author: at-akada 
 * nightly[atmark]at-akada.org
 * 2008.02.28-
 *
 */


var Config = {
    'fps':10,
    'amountOfAgent':10,
    'antiAgent': {
        'amount':4,/*0-9*/
        'frequentAgent': 2 /*0-9*/
    },
    'cooperateAgent': {
        'amount':6,
        'frequentAgent': 3 /*0-9*/
    },
    'word':'おちんちんタイム',
    'whenComplete': 'ハイパーオチンチンタイム',
    'baseInterval': 10,
    'appendProbability': 2/*0-9*/,
    'shortInterval': function(){
        return Math.round((Config.baseInterval) / 2);
    },
    'longInterval': function(){
        return Config.baseInterval;
    }
};
var Default = {};
for(var key in Config){
    if(typeof Config[key] == "object")
        Default[key] = Class.extend({},Config[key]);
    else
        Default[key] = Config[key];
}
var Simulator = new Class();
Class.extend(Simulator.prototype,{
    'initialize': function(){
        this.constructor.instance = this;
        this.taskMan = TaskManager;
        this.bbs = new BBS();
        this.controller = new HiperXXTimeController(this);
        this.painter = new Painter(this);
    },
    'start': function(){
        if(this.running)
            return;
        this.bbs.clear();
        this.painter.clear();
        TaskManager.initialize();
        this.agents = this.controller.createAgents();
        var interval = Math.round(1000 / Config.fps);
        this.loop = setInterval(Function.bind(this.mainLoop,this),interval);
        this.running = true;
    },
    'mainLoop': function(){
        TaskManager.execAll();
        for(var i=0,l=this.agents.length;i<l;i++){
            var meta = this.agents[i];
            if(meta.timer <= 1){
                meta.timer = meta.agent.interval;
                meta.agent.step.apply(meta.agent,[]);
            }
            else
                meta.timer--;
        }
    },
    'stop': function(success){
        clearInterval(this.loop);
        TaskManager.clear();
        this.running = false;
        this.painter.stop(success);
    }
});

var TaskManager = new Class();
Class.extend(TaskManager,{
    'initialize': function(){
        this.tasks = [];
    },
    'push': function(){
        this.tasks.push.apply(this.tasks,arguments);
    },
    'execAll': function(){
        while(this.tasks.length > 0)
            (this.tasks.shift())();
    },
    'clear': function(){
        this.tasks = [];
    }
});
var HiperXXTimeController = new Class();
Class.extend(HiperXXTimeController.prototype,{
    'initialize': function(simulator){
        this.constructor.instance = this;
        this.simulator = simulator;
        this.bbs = this.simulator.bbs;
    },
    'createAgents': function(){
        var agents = [];
        var odd = false,
            antiCnt = 0,
            antiData = Config.antiAgent,
            coopData = Config.cooperateAgent,
            antiAmount = Math.round((antiData.amount/10)*Config.amountOfAgent),
            antiFreqAmount = Math.round((antiData.frequentAgent/10)*antiAmount),
            antiFreqCnt = 0,
            coopAmount = Math.round((coopData.amount/10)*Config.amountOfAgent),
            coopCnt = 0,
            coopFreqAmount = Math.round((coopData.frequentAgent/10)*coopAmount),
            coopFreqCnt = 0;
        for(var i=0,l=Config.amountOfAgent;i<l;i++){
            var meta = {};
            var isAnti = false;
            var interval = Config.longInterval();
            var freqFlag = false;
            if(odd && antiCnt < antiAmount){
                isAnti = true;
                antiCnt++;
                var amount = Config.amountOfAgent;
                if(antiFreqCnt++ < antiFreqAmount){
                    interval = Config.shortInterval();
                    freqFlag = true;
                }
            }
            else if(coopCnt < coopAmount){
                var amount = Config.amountOfAgent;
                coopCnt++;
                if(coopFreqCnt++ < coopFreqAmount){
                    interval = Config.shortInterval();
                    freqFlag = true;
                }
            }
            else{
                odd = !odd;
                continue;
            }
            meta.agent = new Agent(isAnti,interval,freqFlag);
            /* make timer different on agent */
            meta.timer = Math.floor(Math.random()*meta.agent.interval);
            agents.push(meta);
            odd = !odd;
        }
        return agents;
    },
    'checkBBS': function(){
        var word = Config.word,
            bbsState = this.bbs.getWords(),
            hit = false;
        while(!hit){
            var reg = new RegExp(word + "$");
            hit = reg.test(bbsState);
            if(hit)break;
            word = word.slice(0,word.length-1);
        }
        return word;
    },
    'judgeComplete': function(){
        if(this.bbs.messages.length >= 1000)
            this.simulator.stop(false); /* failure */
        var expected = Config.word;
        var now = this.checkBBS();
        if(now == expected)
            this.simulator.stop(true); /* success */
    },
    'nextChar': function(){
        var chrs = this.checkBBS();
        var word = Config.word;
        return word.charAt(chrs.length);
    },
    'nonNextChar': function(){
        var chrs = this.checkBBS();
        var word = Config.word;
        if(word.length <= 1)return this.nextChar();
        var fail = true;
        var n;
        while(fail){
            n = Math.floor(Math.random()*(word.length-1));
            fail = 
                n == chrs.length ? true : false;
        }
        return word.charAt(n);
    },
    'append': function(chr,agent){
        this.simulator.bbs.append(chr,agent);
        this.simulator.painter.updateBoard();
        this.judgeComplete();
    }
});


