/**
 * QED GROUP a.s.
 * Copyright 2008
 * Data mining application.
 * Created by Simon Payne (simon.payne@qedgroup.cz)
 */
 
if (typeof(qg) == 'undefined') qg = {};
if (typeof(qg.DataMiner) != 'undefined') alert("QED GROUP - DataMiner is already set!");

/** Configuration */
qg.Config = {	
    SERVER: 'qed.portal',
    SECURED: false,
    SERVICE_FORMAT: '{protocol}://{server}/{lang}/research/service/',
    LABEL_FORMAT: '{protocol}://{server}/{lang}/research/label/{text}/{color}/',
    LANG: 'cs',
    DEF_COLOR: '#ababab',
    setLang: function(lang) {
        if ($defined(lang) && $type(lang) == 'string' && lang.length == 2) 
            qg.Config.LANG = lang;
    },
    setServer: function(server) {
        if($defined(server))
            qg.Config.SERVER = server;    
    },
    setSecured: function(is) {
        if($defined(is) && $type(is) == 'boolean')
            qg.Config.SECURED = is;    
    },
    getProtocol: function(server) {
        return (qg.Config.SECURED) ? 'https' : 'http';
    },
    TASK_ICON: '/static/design/miner/{state}.gif',
    imgUrl: function(txt, col){
        var pattern = qg.Config.LABEL_FORMAT;
        return pattern.substitute({
            protocol: qg.Config.getProtocol(),
            server: qg.Config.SERVER,
            lang: qg.Config.LANG,
            text: encodeURIComponent(txt.replace('/', '%2F')),
            color: col
        });
    }
};

/* Element IDs */
qg.ID = {
    miner: 'miner-content',
    sender: 'code-sender',
    login: 'login-code',
    lang: 'miner-lang',
    project: 'miner-project',
    minerName: 'miner-name',
    myName: 'miner-my-name',
    isLogged: 'miner-is-logged',
    logMeOut: 'miner-logout',
    info: 'miner-info',
    stLogin: 'miner-login',
    stLoad: 'miner-loading',
    stQuest: 'miner-quest',
    stError: 'miner-error',
    stEnd: 'miner-end',
    quest: 'quest-content',
    questCore: 'quest-itself',
    questTitle: 'quest-title',
    questComment: 'quest-comment',
    questIcon:  'quest-icon',
    questList: 'quest-list',
    prev: 'prev-quest',
    next: 'next-quest',
    finale: 'final-quest',
    prevT: 'prev-quest-top',
    nextT: 'next-quest-top',
    finaleT: 'final-quest-top',
    sideList: 'miner-sidelist',
    errorTitle: 'error-title',
    errorText: 'error-text'
};
/* Localization */
qg.i18n = {};
qg.m = function(code) {
    return $defined(qg.i18n[qg.Config.LANG][code]) ? decodeURIComponent(qg.i18n[qg.Config.LANG][code]) : code; 
};
Element.implement({
    setLoc: function(code) {
        this.set({
            'class': 'i18n_'+code,
            'html': qg.m(code)
        });
    },
    setLocF: function(code) {
        this.set({
            'class': 'i18nf_'+code,
            'value': qg.m(code)
        });
    },
    setLocT: function(code) {
        this.set({
            'class': 'i18nt_'+code,
            'alt': qg.m(code),
            'title': qg.m(code)
        });
    },
    setLocI: function(code, src) {
        this.set({
            'class': 'i18ni_'+code,
            'alt': qg.m(code),
            'title': qg.m(code),
            'src': src
        });
    }
});

/**
 * Main application - THE DATA MINER
 */
qg.DataMiner = new Class({
    Implements: Events,
	initialize: function(elementId, vars){    	
        this.root = $(elementId);
        this.states = [this.ST_LOGIN, this.ST_LOAD, this.ST_QUEST, this.ST_ERROR, this.ST_END];
        
        this.bank = new Element('div').setStyle('display', 'none');
        this.bank.inject(this.root, 'after');
            
        /* prepare states */
        this.states.each(function(item) {
            var el = $(item);
            this.stateCache[item] = el;
            this.bank.adopt(el);
        }, this);
        
        /* set options */
    	qg.Config.setSecured(vars.secure);
    	qg.Config.setServer(vars.server);
    	qg.Config.setLang(vars.lang);
    	
    	/* bind controls */
    	this.prevQuestB = this.prevQuest.bind(this);
        this.nextQuestB = this.nextQuest.bind(this);
        this.finalQuestB = this.finalQuest.bind(this);

        /* prepare events */
        $(qg.ID.lang).addEvent('change', this.switchLang.bind(this));
        $(qg.ID.lang).setStyle('visibility', 'visible');
        this.addEvent('stateChange', this.enterState);
        this.bindLogin = this.tryLogin.bind(this);

        /* test cookies */
        var cookies = false;
        var myCookie = Cookie.set('test', 'it works!');
        if (Cookie.get('test') == 'it works!') {
            myCookie.erase();
            cookies = true;
        }
        if (!cookies) {
            this.state(this.ST_ERROR);
            $(qg.ID.errorText).setLoc('cookies_disabled');
            alert(qg.m('cookies_disabled'));
            return;
        }

        /* initialize the webservice */
        this.portal = new qg.PortalService(vars);
        this.portal.addEvent('login', this.startQuest.bind(this));
        this.portal.addEvent('data', this.startQuest.bind(this));
        this.portal.addEvent('error', this.portalError.bind(this));
        this.portal.addEvent('loginError', this.portalLoginError.bind(this));
        this.portal.addEvent('fault', this.serviceError.bind(this));

        /* initial state */
        if (this.portal.logged) {
            this.state(this.ST_LOAD);
            this.portal.loadData();
        } else {
            this.state(this.ST_LOGIN);
        }
    },
    quest: null,
    timer: null,
    interval: 30000,
    stateCache: [],
    activeState: null,
    /* state variables */
    ST_LOGIN: qg.ID.stLogin,
    ST_LOAD: qg.ID.stLoad,
    ST_QUEST: qg.ID.stQuest,
    ST_ERROR: qg.ID.stError,
    ST_END: qg.ID.stEnd,
    /** Function for changin states */
    state: function(newState){
        if ($defined(this.stateCache[newState])) {
            if ($defined(this.activeState))
                this.bank.adopt(this.activeState);
            this.activeState = this.stateCache[newState];
            this.activeState.setStyle('display', 'block');
            $(qg.ID.miner).adopt(this.activeState);
            this.fireEvent('stateChange', [newState, this.activeState]);
        }
    },
    /** Changes the active language and all the language strings */
    activateLang: function(){
        // text
        $$('*[class^=i18n_]').each(function(el) {
            var code = el.get('class').substring(5);
            el.set('html', qg.m(code));
        });
        
        // value (of forms)
        $$('*[class^=i18nf_]').each(function(el) {
            var code = el.get('class').substring(6);
            el.set('value', qg.m(code));
        });
        
        // title
        $$('*[class^=i18nt_]').each(function(el) {
            var code = el.get('class').substring(6);
            el.set('alt', qg.m(code));
            el.set('title', qg.m(code));
        });
        
        // src (of image)
        $$('*[class^=i18ni_]').each(function(el) {
            var code = el.get('class').substring(6);
            el.set('src', qg.Config.imgUrl(qg.m(code), el.retrieve('color')));
            el.set('alt', qg.m(code));
            el.set('title', qg.m(code));
        });
        
        // select active language
        $(qg.ID.lang).getChildren().each(function(el) { 	
            if (el.get('value') == qg.Config.LANG)		
                el.set('selected', true); 
        });
    },
    /** Switch langugage by option selection */
    switchLang: function() {
        var lang = $(qg.ID.lang).get('value');
        qg.Config.setLang(lang);
        this.activateLang();
    },
    /** Bind some events to new state */
    enterState: function(newState){
        if (newState == this.ST_LOGIN) {
            $(qg.ID.sender).removeEvent('clik', this.bindLogin);
            $(qg.ID.sender).addEvent('click', this.bindLogin);
        }
    },
    /** Try login to webservice */
    tryLogin: function(){
        var input = $(qg.ID.login);
        var pass = input.get('value');
        this.state(this.ST_LOAD);
        this.portal.login(pass);
        input.erase('value');
    },
    /** Start of the questionnaire */
    startQuest: function(){
        this.state(this.ST_QUEST);

        this.quest = new qg.Quest(this.portal.data);
        this.quest.addEvent('progress', this.questAdvance.bind(this));
        
        $(qg.ID.project).setLoc('project_name');
        $(qg.ID.project).fade('hide');
        $(qg.ID.project).fade('in');
        
        $(qg.ID.myName).set('text', this.quest.info.myName);
        $(qg.ID.minerName).fade('hide');
        $(qg.ID.minerName).fade('in');
        $(qg.ID.logMeOut).addEvent('click', this.logout.bind(this));
        
        this.drawQuestList();
        
        // controls
        $(qg.ID.prev).removeEvent('click', this.prevQuestB);
        $(qg.ID.prev).addEvent('click', this.prevQuestB);
        $(qg.ID.next).removeEvent('click', this.nextQuestB);
        $(qg.ID.next).addEvent('click', this.nextQuestB);
        $(qg.ID.finale).removeEvent('click', this.finalQuestB);
        $(qg.ID.finale).addEvent('click', this.finalQuestB);
        $(qg.ID.prevT).removeEvent('click', this.prevQuestB);
        $(qg.ID.prevT).addEvent('click', this.prevQuestB);
        $(qg.ID.nextT).removeEvent('click', this.nextQuestB);
        $(qg.ID.nextT).addEvent('click', this.nextQuestB);
        $(qg.ID.finaleT).removeEvent('click', this.finalQuestB);
        $(qg.ID.finaleT).addEvent('click', this.finalQuestB);
        
        this.stopTimer();
        //this.timer = this.saveAnswers.periodical(this.interval, this);
		this.controlsVisibility();
		this.activateLang();
        this.root.setStyle('display', 'none');
        this.root.setStyle('display', 'block');
    },
    prevQuest: function(){
        this.quest.goToTask(this.quest.currentTask - 1);
    },
    nextQuest: function(){
        var myTask = this.quest.activeTasks[this.quest.currentTask]; 
        if (myTask.isValid()) {
            this.quest.goToTask(this.quest.currentTask + 1);
        } else {
            myTask.visited = true;
            myTask.drawIcon();
            this.drawQuestList();
            alert(qg.m('current_not_valid'));
        }
    },
    finalQuest: function(){
        var myTask = this.quest.activeTasks[this.quest.currentTask]; 
        myTask.visited = true;
        myTask.drawIcon();
        this.drawQuestList();
        if (this.quest.isCompleted()) {
            this.quest.complete = true;
            this.questDone();
        } else {
            alert(qg.m('not_completed'));
        }
    },
    drawQuestList: function() {
        var list = $(qg.ID.questList);
        var lst = $(qg.ID.sideList);
        if ($defined(lst)) lst.dispose();
        lst = new Element('ul', { 'id': qg.ID.sideList });
        list.adopt(lst);
        $each(this.quest.activeTasks, function(task){
            var code = task.getTitleCode();
            var ico = task.getIcon();
            var bullet = new Element('li');
            bullet.set('class', (task.no == this.quest.currentTask) ? 'miner-hi-item' : 'miner-item');
            var txt = new Element('span', {
                'text': task.getNumber()
            });
            var a = new Element('a');
            a.setLoc(code);
            if (this.quest.maxTask >= task.no) {
                a.set({
                    'href': '#',
                    'events': {
                        'click': function() {
                            this.quest.goToTask(task.no);
                            return false;
                        }.bind(this)
                    }
                });
            } else {
                a.setStyle('color', '#ababab');                
            }
            txt.grab(ico, 'top');
            txt.grab(a);
            bullet.adopt(txt);
            lst.adopt(bullet);
        }, this);
    },
    /** Better save it now than later */
    saveAnswers: function(){
        if ($defined(this.quest))
            this.portal.saveData(this.quest.getAllAnswers(), this.quest.complete);
	},
	/** Visibility of controls */
	controlsVisibility: function(){
		var first = (1 == this.quest.currentTask);
		var last = (this.quest.getTotalTasks() == this.quest.currentTask);
		$(qg.ID.prev).setStyle('display', (first) ? 'none' : 'block');
		$(qg.ID.prevT).setStyle('display', (first) ? 'none' : 'block');
        $(qg.ID.next).setStyle('display', (last) ? 'none' : 'block');
        $(qg.ID.nextT).setStyle('display', (last) ? 'none' : 'block');
	},
	/** Current task has changed */
	questAdvance: function(){
	    this.drawQuestList();
		this.saveAnswers();
		this.controlsVisibility();
	},
	/** Get rid of the damn question sheet */
	clearQuest: function(){
		if ($defined(this.quest)) this.quest = null;
		$(qg.ID.questCore).empty();
	},
	/** Logout from webservice */
	logout: function(){
	    this.stopTimer();
		this.saveAnswers();
		this.clearQuest();
		this.portal.logout.delay(1000, this.portal);
		this.state(this.ST_LOGIN);
		this.clearTextInfo();
	},
	/** Questionnaire is complete */
	questDone: function(){
		this.logout();
		this.state(this.ST_END);
	},
    /** An error occured! */
    portalError: function(){
        this.stopTimer();
        this.state(this.ST_LOGIN);
        this.clearTextInfo();
        var msg = qg.m('fatal_error');
        msg += '\n\n' + this.portal.message;
        alert(msg);
    },
    /** A login error occured! */
    portalLoginError: function(){
        this.stopTimer();
        this.state(this.ST_LOGIN);
        this.clearTextInfo();
        var msg = qg.m('fatal_error_login');
        msg += '\n\n' + this.portal.message;
        alert(msg);
    },
    /** An service error occured! */
    serviceError: function(){
        this.stopTimer();
        this.state(this.ST_LOGIN);
        this.cleartextInfo();
        var msg = qg.m('service_error');
        msg += '\n\n' + qg.m('check_connection');
        alert(msg);
    },
    stopTimer: function(){
        this.timer = $clear(this.timer);
    },
    clearTextInfo: function(){
        $(qg.ID.minerName).fade('out');
        $(qg.ID.project).fade('out');
    }
});

/**
 * PortalService object for keeping in touch with webservice
 * (provides login, logout, requests and saves user data)
 */
qg.PortalService = new Class({
    Implements: Events,
	initialize: function(vars){
    	this.logged = ($defined(vars.logged)) ? vars.logged : false;
    	this.safeKey = ($defined(vars.safeKey)) ? vars.safeKey : '';
    	this.expires = new Date(($chk(vars.expires)) ? vars.expires : 0);
    	this.data = {};
    	this.message = '';
    	this.request = null;
	},
    /** Sends request to portal webservice */
    makeRequest: function(method, data){
        switch (method) {
			case 'login':
			    this.showInfo('logging');
			    break;
			case 'logout':
			    this.showInfo('unlogging');
			    break;
			case 'quest':
			    this.showInfo('loading');
			    break;
			case 'save':
			    this.showInfo('saving');
				break;
			default:
			    // unknown method
				return;
		}

        if (!$defined(data)) data = {};
        
        var params = data;
        params.method = method;
        params.lang = qg.Config.LANG;
        params.safe_key = this.safeKey;
        
        if ($defined(this.request)) this.request.cancel();
        this.request = new Request.JSON({
            url: this.serviceUrl(),
            onFailure: this.fault.bind(this),
            onSuccess: this.result.bind(this)
        }).post(params);
    },
    /** Sucessful result of webservice request */
    result: function(response){
        this.status = response.status;
        this.method = response.method;
        this.expire = new Date(parseInt(response.expire));
        this.message = response.message;
        this.logged = response.logged;
        this.data = response.data;

        if (this.status == qg.PortalService.STAT_LOGIN_ERR) {
            this.fireEvent('loginError', [this.status, this.message]);
        } else if (this.status != qg.PortalService.STAT_OK && this.status != qg.PortalService.STAT_LOGIN_OK) {
            this.fireEvent('error', [this.status, this.message]);
        } else {
            switch (this.method) {
                case 'login':
                    this.showInfo('logged');
                	if ($defined(this.data.lang)) { qg.Config.setLang(this.data.lang) };
                    this.safeKey = this.data.safe_key;
                    this.fireEvent('login', [this.status, this.message]);
                	break;
                case 'logout':
                    this.showInfo('unlogged');
                	this.fireEvent('logout', [this.status, this.message]);
                	break;
                case 'quest':
                	this.showInfo('loaded');
                	this.fireEvent('data', [this.status, this.message]);
                    break;
                case 'save':
                	this.showInfo('saved');
                	this.fireEvent('save', [this.status, this.message]);
                	break;
            }
        }
    },
    /** Webservice request failed */
    fault: function(){
        this.fireEvent('fault');
    },
    /** Webservice url builder */
    serviceUrl: function(){
        var pattern = qg.Config.SERVICE_FORMAT;
        return pattern.substitute({
            protocol: qg.Config.getProtocol(),
            server: qg.Config.SERVER,
            lang: qg.Config.LANG
        });
    },
    login: function(pass){
        this.makeRequest('login', {mem_key: pass});
    },
    logout: function(){
        this.makeRequest('logout');
    },
    saveData: function(data, done){
        var state = done ? 'true' : 'false';
        this.makeRequest('save', {answers:JSON.encode(data), completed:state});
    },
    loadData: function(){
        this.makeRequest('quest');
    },
    delayFn: null,
    showInfo: function(code) {
        $clear(this.delayFn);
        $(qg.ID.info).setLoc(code);
        $(qg.ID.info).fade('in');
        this.delayFn = (function(){
            $(qg.ID.info).fade('out');
        }).delay(3000);
    }
});
/* status constants */
qg.PortalService.STAT_OK = 200;
qg.PortalService.STAT_LOGIN_OK = 201;
qg.PortalService.STAT_LOGIN_ERR = 401;
qg.PortalService.STAT_UNAUTH = 402;
qg.PortalService.STAT_NOT_FOUND = 451;
qg.PortalService.STAT_BAD_REQUEST = 452;
qg.PortalService.STAT_INT_ERROR = 500;

/**
 * Questionnaire object
 */
qg.Quest = new Class({
    Implements: Events,
	initialize: function(data){
        $extend(this, data);
        
        $each(this.quest, function(item, i) {
            this.tasks[i] = new qg.Task(item);
        }, this);
        
        this.mergeLocalization();
        this.constructTasks();
    },
    completed: false,
    tasks: [],
    currentTask: 1,
    maxTask: 1,
    activeElement: null,
    activeTasks: {},
    /** Extract localization from tasks and save it to engine */
    mergeLocalization: function(){
        this.info.languages.each(function(lang) {
        	qg.i18n[lang.code]['project_name'] = this.info.name[lang.code];
        	this.tasks.each(function(task) {
        		task.comments.each(function(com, i) {
        			qg.i18n[lang.code][task.comCode(i)] = com.text[lang.code];
        		});
        		if (task.type == qg.Task.RATE_TARGETS) {
        			task.values.each(function(val, j) {
        				qg.i18n[lang.code][task.valCode(j)] = val.text[lang.code];
        			});
        		}
        	});
        }, this);
	},
	/** Construct all tasks int this question sheet */
	constructTasks: function(){
        this.activeTasks = {};
        var no = 1;
        var lastTask = -1;
        this.tasks.each(function(task, n) {
            var mod;
            switch (task.type) {
            	case qg.Task.INFO:
                    mod = new qg.quests.Info(task);
            		break;
            	case qg.Task.SIMPLE_QUESTION:
                    mod = new qg.quests.SimpleQuest(task);
            		break;
            	case qg.Task.RATE_TARGETS:
                    mod = new qg.quests.RateTargets(
                        task,
                        this.targets,
                        this.info.myId
            		);
            		break;
            	case qg.Task.COMMET_TARGETS:
                    mod = new qg.quests.CommentTargets(
                        task,
                        this.targets,
                        this.info.myId
            		);
            		break;
            }
            if (!$defined(mod)) return;
            mod.no = no;
            if ($defined(this.answers)) {
            	if ($defined(this.answers.data)) {
	            	$each(this.answers.data, function(ans) {
	            		if ($defined(ans) && $defined(ans.id) && ans.id == task.id) mod.setAnswer(ans);
	                });
            	} else {
            		// temporary fix for old version of save format
            		$each(this.answers, function(ans) {
	            		if ($defined(ans) && $defined(ans.id) && ans.id == task.id) mod.setAnswer(ans);
	                });
            	}
            	if ($defined(this.answers.currentTask) && this.answers.currentTask == task.id) lastTask = no;
            	if ($defined(this.answers.maxTask) && this.answers.maxTask == task.id) this.maxTask = no;
            }
            this.activeTasks[no] = mod;
            no++;
        }, this);
        this.goToTask((lastTask != -1) ? lastTask : 1);
	},
	/** Go to this specific task */
	goToTask: function(no){
        if (!$defined(this.activeTasks[no])) return;
        if (no < this.currentTask && !this.info.returnEnabled) return; // can't go back
        
        var oldTask = this.activeTasks[this.currentTask];
        var newTask = this.activeTasks[no];
        
        if ($defined(this.activeElement)) {
            oldTask.saveState(this.activeElement);
            oldTask.visited = true;
	      
	        this.activeElement.dispose();
        }
        
        this.currentTask = no;
        if (no > this.maxTask) this.maxTask = no;
        this.activeElement = newTask.restoreState();
        
        var qel = $(qg.ID.quest); 
        
        newTask.drawTitle();
        newTask.drawComment();
        newTask.drawIcon();
        
        /* draw content */
        $(qg.ID.questCore).adopt(this.activeElement);
        
        this.fireEvent('progress', [newTask, this.activeElement]);
    },
    /** Total number of active tasks */
    getTotalTasks: function(){
        var num = 0;
        $each(this.activeTasks, function() { num++ });
        return num;
    },
    /** Returns al the answers from all the tasks */
    getAllAnswers: function(){
        var answerData = {};
		answerData.currentTask = this.activeTasks[this.currentTask].id;
		answerData.maxTask = this.maxTask;
		answerData.data = {};
		$each(this.activeTasks, function(mod) {
			answerData.data[mod.id] = mod.getAnswer();
		});
		return answerData; 
    },
    isCompleted: function(){
        var complete = true;
		$each(this.activeTasks, function(mod) {
			if (!mod.isValid()) complete = false; 
		});
		return complete;
    }
});

/**
 * Object wrapper that provides some basic functions for task data
 */
qg.Task = new Class({
	initialize: function(data){
    	$extend(this, data);
    	if ($defined(this.values)) {
    	    this.values.each(function(item, i) {
    		    item.no = i;
    		    if (!$defined(item.color))
                    item.color = qg.Config.DEF_COLOR;
    	    }, this);
	    }
	},    
	comCode: function(i){
		return 'comment_' + this.id + '_' + i;
	},
	valById: function(id){
	    var itm = null;
	    if ($defined(this.values)) {
    		this.values.each(function(item) {
    			if (this.id == item.id) itm = item;
    		}, this);
		}
		return itm;
	},
	valCode: function(i){
		return 'value_' + this.id + '_' + i;
	},
	getTitleCode: function(){
	    var code = '';
	    this.comments.each(function(item, i) {
			if (item.type == qg.Task.TITLE) {
				code = this.comCode(i);
			}
		}, this);
		return code;
	},
	getCommentCode: function(no){
	    if (!$defined(no)) no = 0;
		var i = 0;
		var code = '';
		this.comments.each(function(item, j) {
			if (item.type == qg.Task.INSTRUCTION) {
				if (i == no)
				    code = this.comCode(j);
				i++;
			}
		}, this);
		return code;
	}
});

/* Question types */
qg.Task.INFO = 'Info';
qg.Task.SIMPLE_QUESTION = 'SimpleQuestion';
qg.Task.RATE_TARGETS = 'RateTargets';
qg.Task.MULTI_RATE_TARGETS = 'MultiRateTargets';
qg.Task.COMMET_TARGETS = 'CommentTargets';

/* Text comment types */
qg.Task.TITLE = 'Title';
qg.Task.INSTRUCTION = 'Instruction';

/***********************************/
/** Question types implementation **/
/***********************************/
qg.quests = {};

/** Simple text info **/
qg.quests.Info = new Class({
	initialize: function(task){
    	$extend(this, task);
	},
	element: null,
	model: null,
	visited: false,
    saveState: function(element){
        this.element = element;
        this.updateModel();
    },
    restoreState: function(){
        this.updateView();
        return this.element;
    },
    setAnswer: function(ans){
        if ($defined(ans.data))
        	this.model = ans.data;
        if ($defined(ans.visited));
        	this.visited = ans.visited;
        this.updateView();
    },
    getAnswer: function(){
        var ans = {};
        ans.data = this.model;
        ans.visited = this.visited;
        ans.valid = this.isValid();
        ans.state = this.state();
        ans.type = this.type;
		ans.id = this.id;
		ans.no = this.no;
        return ans;
    },
    updateModel: $empty,
    updateView: $empty,
    isValid: function(){ return true },
    state: function(){
        var state = (this.element == null) ? 'info' : 'normal';
        if (this.visited)
            state = this.isValid() ? 'valid' : 'invalid';
        return state;
    },
    getIcon: function(){
        var ic = qg.Config.TASK_ICON;
        return new Element('img', {
            'src': ic.substitute({ state: this.state() })
        });
    },
    getNumber: function(){
        return '{no}. '.substitute({ no: this.no });
    },
    drawIcon: function(){
        var noh = $(qg.ID.questIcon);
        noh.set('text', this.getNumber());
        noh.grab(this.getIcon(), 'top');
    },
    drawTitle: function(){
        var tcode = this.getTitleCode();
        $(qg.ID.questTitle).setLoc(tcode);
    },
    drawComment: function(){
        var ccode = this.getCommentCode();
        $(qg.ID.questComment).setLoc(ccode);
    }
});
/** Simple Question **/
qg.quests.SimpleQuest = new Class({
    Extends: qg.quests.Info,
	initialize: function(task){
    	this.parent(task);
    	this.element = new Element('textarea', {
    	    'rows': 10
    	});
	},
    updateModel: function(){
        this.model = this.element.get('value');
    },
    updateView: function(){
        this.element.set('value', this.model);
    }
});
/** Rate Targets Question **/
qg.quests.RateTargets = new Class({
    Extends: qg.quests.Info,
	initialize: function(task, targets, myId){
    	this.parent(task);
    	this.targets = targets;
    	this.myId = myId;
    	
    	this.element = new Element('div');
    	
    	var rate = this.createRateTable();
    	this.element.adopt(rate);
    	
    	this.element.adopt(new Element('hr'));
    	
    	var p = new Element('div');
    	p.setLoc(this.comCode(2));
    	this.element.adopt(p);
    	
    	this.element.adopt(new Element('br'));
    	
    	var com = this.createCommentTable();
    	this.element.adopt(com);
	},
    updateModel: function(){
        if (!$defined(this.model)) this.model = {};
        this.model.targets = {};
        this.model.comments = {};
        this.targets.each(function(target){
            var value = null;
            var check = null;
            
            // rating
            this.element.getElements('input[name='+this.id+'_target_'+target.id+']').each(
                function(item){
                    if (item.getProperty('checked')) check = item;
            }.bind(this));
            if ($defined(check))
                value = check.get('value');
            this.model.targets[target.id] = value;
            
            // comments
            value = null;
            check = null;
            check = this.element.getElement('textarea[name='+this.id+'_target_'+target.id+']');
            if ($defined(check))
                value = check.get('value');
            this.model.comments[target.id] = value;
        }, this);
    },
    updateView: function(){
        if (!$defined(this.model)) return;
        if (!$defined(this.model.targets)) return;
        $each(this.model.targets, function(val, tar){
            var check = this.element.getElements('input[name='+this.id+'_target_'+tar+']');
            if ($defined(check)) {
                check.each(function(item){
                    if (item.getProperty('value') == val)
                        (function(){
                            this.setProperty('checked', 'checked');
                            this.retrieve('target').setStyle('color', this.retrieve('color', qg.Config.DEF_COLOR));
                            var c = $(this.retrieve('com'));
                            if ($defined(c))
                            	c.setStyle('color', this.retrieve('color', qg.Config.DEF_COLOR));
                        }).delay(10, item);
                }.bind(this));
            }
        }, this);
        if (!$defined(this.model.comments)) return;
        $each(this.model.comments, function(val, tar){
            check = this.element.getElement('textarea[name='+this.id+'_target_'+tar+']');
            if ($defined(check))
                check.set('html', val);
        }, this);
    },
    isValid: function(){
        if (!$defined(this.model)) return false;
        if (!$defined(this.model.targets)) return false;
        var result = true;
        $each(this.model.targets, function(val){
            if (val == null)
                result = false;
        });
        return result;
    },
    createRateTable: function(){
        var table = new Element('table',{
    	    'class': 'rate-table full-width',
    	    'cellpadding': 0,
    	    'cellspacing': 0
    	});
    	var body = new Element('tbody');
    	table.adopt(body);
    	var tr = new Element('tr');
    	var th = new Element('th', {
            'html': "&nbsp;"
        });
        tr.adopt(th);
        // value titles
    	this.values.each(function(val, j){
    	    var code = this.valCode(j);
    	    var color = val.color.substring(1);
    	    th = new Element('th');
    	    var img = new Element('img');
    	    img.setLocI(code, qg.Config.imgUrl(qg.m(code), color));
    	    img.setStyle('width', 20);
    	    img.store('color', color);
    	    th.adopt(img);
    	    tr.adopt(th);
    	}, this)
        th = new Element('th', {'class': 'part-width'});
        tr.adopt(th);
    	body.adopt(tr);
    	// targets
    	this.targets.each(function(tar, j){
    	    tr = new Element('tr');
    	    // target name
    	    th = new Element('th', {'text' : tar.name, 'class': 'target-name'});
        	tr.adopt(th);
        	// target rating
    	    this.values.each(function(val, j){
        	    var code = this.valCode(j);
        	    var td = new Element('td');
        	    td.setStyle('border-top', '1px solid ' + val.color);
        	    tr.adopt(td);
        	    var rad = new Element('input', {
        	        'type': 'radio',
        	        'name': this.id + '_target_' + tar.id,  
        	        'value': val.id
        	    });
        	    rad.setLocT(code);
        	    rad.addEvent('click', this.updateModel.bind(this));
        	    rad.store('target', th);
        	    rad.store('com', this.id+'_target_com_'+tar.id);
        	    rad.store('color', val.color);
        	    rad.addEvent('click', function() {
                    this.retrieve('target').setStyle('color', val.color);
                    $(this.retrieve('com')).setStyle('color', val.color);
                });
        	    td.adopt(rad);
        	}, this)
        	th = new Element('th');
        	tr.adopt(th);
        	body.adopt(tr);
    	}, this);
    	return table;
    },
    createCommentTable: function(){
        var table = new Element('table',{
    	    'class': 'comment-table full-width',
    	    'cellpadding': 0,
    	    'cellspacing': 0
    	});
    	var body = new Element('tbody');
    	table.adopt(body);
    	// targets
    	this.targets.each(function(tar, j){
    	    var tr = new Element('tr');
    	    // target name
    	    var th = new Element('th', {'text': tar.name, 'class': 'target-name', 'id': this.id+'_target_com_'+tar.id});
        	tr.adopt(th);
        	// target comment
    	    var td = new Element('td', {'class': 'empty'});
        	tr.adopt(td);
        	var area = new Element('textarea', {
        	    'rows': 5,
        	    'name': this.id + '_target_' + tar.id
        	});
        	td.adopt(area);
        	body.adopt(tr);
    	}, this);
    	return table;
    }
});
/** Comment Targets Question **/
qg.quests.CommentTargets = new Class({
    Extends: qg.quests.Info,
	initialize: function(task, targets, myId){
    	this.parent(task);
    	this.targets = targets;
    	this.myId = myId;
    	this.element = this.createCommentTable();
	},
    updateModel: function(){
       if (!$defined(this.model)) this.model = {};
        this.model.comments = {};
        this.targets.each(function(target){
            var value = null;
            var check = this.element.getElement('textarea[name='+this.id+'_target_'+target.id+']');
            if ($defined(check))
                value = check.get('value');
            this.model.comments[target.id] = value;
        }, this);
    },
    updateView: function(){
        if (!$defined(this.model)) return;
        if (!$defined(this.model.comments)) return;
        $each(this.model.comments, function(val, tar){
            var check = this.element.getElement('textarea[name='+this.id+'_target_'+tar+']');
            if ($defined(check))
                check.set('html', val);
        }, this);
    },
    isValid: function(){
        return true;
    },
    createCommentTable: function(){
        var table = new Element('table',{
    	    'class': 'comment-table full-width',
    	    'cellpadding': 0,
    	    'cellspacing': 0
    	});
    	var body = new Element('tbody');
    	table.adopt(body);
    	// targets
    	this.targets.each(function(tar, j){
    	    var tr = new Element('tr');
    	    // target name
    	    var th = new Element('th', {'text' : tar.name, 'class': 'target-name'});
        	tr.adopt(th);
        	// target comment
    	    var td = new Element('td');
        	tr.adopt(td);
        	var area = new Element('textarea', {
        	    'rows': 5,
        	    'name': this.id + '_target_' + tar.id
        	});
        	td.adopt(area);
        	body.adopt(tr);
    	}, this);
    	return table;
    }
});