﻿//<using>
//  nui/nui.js
//  nui/prototype.js
//  nui/lang.js
//  nui/quill/processor/processor.js
//  nui/quill/entity/*
//  nui/quill/proxy.js
//</using>

/**
 * Get a survey's processor
 * @param vSurvey {object}. The entity of the survey
 */
NUI.Quill.Processor.getSurveyPrcs = function(vSurvey) {
    return NUI.Quill.Processor.Survey(vSurvey);
};

/**
 * The processor class of a survey
 * @param vSurvey {object}. The entity of the survey
 */
NUI.Quill.Processor.Survey = (function() {
    var g_lang = NUI.Lang,
        g_enum = NUI.Quill.Enum,
        g_entity = NUI.Quill.Entity,
        g_ht = NUI.Util.DataStructure.HashTable;
    
    return function(vSurvey) {
        if(vSurvey == null) {
            return null;
        }
        var m_entity = vSurvey;
        
        /**
         * Reresh the survey's id.
         * IMPORTANT: This method is used after saving a new survey. 
         * If the survey has been saved before(id>0), this method is invalid.
         */
        function _refreshId(vNewId) {
            if(!g_lang.isNumber(vNewId) || vNewId <= 0) {
                return false;
            }
            if(m_entity.id > 0) {
                return false;
            }
            m_entity.id = vNewId;
            return true;
        }
        
        /**
         * Update caption/subcaption/description/header/footer
         */
        function _updateStringVar(vVariable, vValue) {
            if(!g_lang.isString(vValue) || vValue == null || !g_lang.isString(vVariable)) {
                return false;
            }
            try {
                m_entity[vVariable] = vValue;
                return true;
            } catch (ex) {
                return false;
            }
        }
        
        /**
         * Pagination
         */
        function _addPageTag(vQId) {
            if(m_entity.qIdList[0] == vQId || !m_entity.qIdList.contain(vQId)) {
                return;
            }
            m_entity.pageTags.setValue(vQId, true);
        }
        function _removePageTag(vQId) {
            m_entity.pageTags.remove(vQId);
        }
        
        /**
         * Portion
         */
        function _setPortionTag(vQId, vText) {
            if(!m_entity.qIdList.contain(vQId)) {
                return;
            }
            m_entity.portionTags.setValue(vQId, vText);
        }
        function _removePortionTag(vQId) {
            m_entity.portionTags.remove(vQId);
        }
        
        /**
         * Jumper
         */
        function _addJumper(vQId, vOptionIndex, vTargetQId) {
            var jp = m_entity.jumper;
            if(!jp.exist(vQId)) {
                jp.setValue(vQId, new g_ht());
            }
            var optSet = jp.getValue(vQId);
            if(!optSet.exist(vOptionIndex)) {
                optSet.setValue(vOptionIndex, new g_ht());
            }
            var idSet = optSet.getValue(vOptionIndex);
            idSet.setValue(vTargetQId, true);
        }
        function _removeJumper(vQId, vOptionIndex, vTargetQId) {
            var jp = m_entity.jumper;
            if(!jp.exist(vQId)) {
                return;
            }
            var optSet = jp.getValue(vQId);
            if(!optSet.exist(vOptionIndex)) {
                return;
            }
            var idSet = optSet.getValue(vOptionIndex);
            idSet.remove(vTargetQId);
            if(idSet.count() == 0) {  // adjust the hashtable after remove the target jumper
                optSet.remove(vOptionIndex);
                if(optSet.count() == 0) {
                    jp.remove(vQId);
                }
            }
        }
        function _removeQJumper(vQId) {
            m_entity.jumper.remove(vQId);
        }
        function _isJumper(vQId) {
            return m_entity.jumper.exist(vQId);
        }
        
        /**
         * Branch
         */
        function _setBranch(vQId, vOptIdx, vBranchType, vValue) {
            var br = m_entity.branch;
            if(!br.exist(vQId)) {
                br.setValue(vQId, new g_ht());
            }
            var qBr = br.getValue(vQId);
            qBr.setValue(vOptIdx, {
                type : vBranchType,
                value : vValue
            });
        }
        function _removeBranch(vQId, vOptIdx) {
            var br = m_entity.branch;
            if(!br.exist(vQId)) {
                return;
            }
            var qBr = br.getValue(vQId);
            qBr.remove(vOptIdx);
            if(qBr.count() == 0) {
                br.remove(vQId);
            }
        }
        function _removeQBranch(vQId) {
            m_entity.branch.remove(vQId);
        }
        function _isBranch(vQId) {
            return m_entity.branch.exist(vQId);
        }
        function _isBranchByType(vQId, vOptIdx, vBranchType) {
            var br = m_entity.branch;
            if(br) {
                var qBr = br.getValue(vQId);
                if(qBr) {
                    var value = qBr.getValue(vOptIdx);
                    return (value && value.type == vBranchType);
                }
            }
            return false;
        }
        
        /**
         * Create a id for a new question
         */
        function __createQId() {
            var qIds = m_entity.qIdList;
            if(qIds == null || qIds.length == 0) {
                return 1;
            }
            var len = qIds.length,
                retval = 0;
            for(var i=0; i<len; i++) {
                if(qIds[i] > retval) {
                    retval = qIds[i];
                }
            }
            retval ++;
            return retval;
        }
        /**
         * Add a question at the target position
         * @return {int}. The id of the new question
         */
        function _addQuestion(vType, vPosition) {
            var qIds = m_entity.qIdList;
            if(vPosition != qIds.length  && !qIds.isIndexLegal(vPosition)) {
                return -1;
            }
            var qId = __createQId(),
                qst = g_entity.Question[vType.value](qId);
            m_entity.questionSet.setValue(qId, qst);
            qIds.splice(vPosition, 0, qId);
            return qId;
        }
        /**
         * Paste a question. The id of clone version of the question will be changed.
         * @param vQuestion {object}. The question to be paste into the survey
         * @param vPosition {int}. The target position
         * @return {int} the new question's id
         */
        function _pasteQuestion(vQuestion, vPosition) {
            var qId = __createQId(),
                qIds = m_entity.qIdList;
            if(vPosition != qIds.length  && !qIds.isIndexLegal(vPosition)) {
                return -1;
            }
            var pasteQst = NUI.Util.ObjectService.clone(vQuestion);
            pasteQst.id = qId;
            pasteQst.modified = true;
            m_entity.questionSet.setValue(qId, pasteQst);
            qIds.splice(vPosition, 0, qId);
            return qId;
        }
        /**
         * Delete a question from the questionSet
         * @param vQId {int}. The id of the question to be deleted
         */
        function _deleteQuestion(vQId) {
            var qIdx = m_entity.qIdList.getIndex(vQId);
            if(qIdx < 0) {
                return;
            }
            var preIdx = qIdx - 1,
                nextIdx = qIdx + 1,
                len = m_entity.qIdList.length;
                
            // 1. Adjust pagination tag
            if(m_entity.pageTags && m_entity.pageTags.exist(vQId)) {
                _removePageTag(vQId);
                if(nextIdx>0 && nextIdx<len) {
                    _addPageTag(m_entity.qIdList[nextIdx]);
                }
            }
            if(nextIdx == 1) {
                _removePageTag(m_entity.qIdList[nextIdx]);
            }
            
            // 2. Adjust portion tag
            if(m_entity.portionTags && m_entity.portionTags.exist(vQId)) {
                var text = m_entity.portionTags.getValue(vQId);
                _removePortionTag(vQId);
                if(nextIdx>0 && nextIdx<len) {
                    _setPortionTag(m_entity.qIdList[nextIdx], text);
                }
            }
            
            // 3. Adjust qIdList
            m_entity.qIdList.remove(vQId);
            
            // 4. Adjust jumper
            _removeQJumper(vQId);
            
            // 5. Adjust branch
            _removeQBranch(vQId);
            
            // 6. Adjust questionSet
            m_entity.questionSet.remove(vQId);
        }
        /**
         * Move a question
         */
        function _moveQuestion(vQId, vStep) {
            // Move question
            var qIds = m_entity.qIdList,
                len = qIds.length,
                oldPos = 0;
            for(; oldPos<len; oldPos++) {
                if(qIds[oldPos] == vQId) {
                    break;
                }
            }
            if(oldPos == len) {
                return false;
            }
            var newPos = oldPos + vStep,
                newPageTagQId = qIds[oldPos + 1];
            if(newPos < 0) {
                newPos = 0;
            } else if(newPos >= len) {
                newPos = len;
            }
            qIds.remove(vQId);
            qIds.splice(newPos, 0, vQId);
            
            // Adjust pagination
            if(m_entity.pageTags.exist(vQId)) {
                _removePageTag(vQId);
                if(newPageTagQId) {
                    _addPageTag(newPageTagQId);
                }
            }
            if(m_entity.pageTags.exist(qIds[0])) {
                _removePageTag(qIds[0]);
            }
            
            return true;
        }
        /**
         * Get a question from the server
         * @param vQId {int}. The id of the question
         * @param vFunc(vQuestion) {function}. The function to execute after gettting the question
         */
        function _getQuestion(vQId, vFunc) {
            var retval = m_entity.questionSet.getValue(vQId);
            if(retval != null) {
                vFunc(retval);
            } else {
                NUI.Quill.Proxy.Question.getQuestion(m_entity.id, vQId, function(vQuestion) {
                    var q = m_entity.questionSet.getValue(vQId);
                    if(vQuestion != null) {
                        if(q == null) {
                            m_entity.questionSet.setValue(vQId, vQuestion);
                        } else {
                            vQuestion = q;
                        }
                    }
                    vFunc(vQuestion);
                });
            }
        }
        
        var that = {
            /**
             * Update caption/subcaption/description/header/footer
             */
            updateCaption : function(vCaption) {
                return _updateStringVar("caption", vCaption);
            },
            updateSubcaption : function(vSubcap) {
                return _updateStringVar("subcaption", vSubcap);
            },
            updateDescription : function(vDesc) {
                return _updateStringVar("description", vDesc);
            },
            updateHeader : function(vHeader) {
                return _updateStringVar("header", vHeader);
            },
            updateFooter : function(vFooter) {
                return _updateStringVar("footer", vFooter);
            },
            
            /**
             * Deal with page tags
             */
            addPageTag : function(vQId) {
                _addPageTag(vQId);
            },
            removePageTag : function(vQId) {
                _removePageTag(vQId);
            },
            
            /**
             * Deal with portion tags
             */
            setPortionTag : function(vQId, vText) {
                if(!g_lang.isString(vText) || vText == null) {
                    return;
                }
                _setPortionTag(vQId, vText);
            },
            removePortionTag : function(vQId) {
                _removePortionTag(vQId);
            },
            
            /**
             * Deal with jumper
             */
            addJumper : function(vQId, vOptionIndex, vTargetQId) {
                _addJumper(vQId, vOptionIndex, vTargetQId);
            },
            removeJumper : function(vQId, vOptionIndex, vTargetQId) {
                _removeJumper(vQId, vOptionIndex, vTargetQId);
            },
            isJumper : function(vQId) {
                return _isJumper(vQId);
            },
            
            /**
             * Deal with branch
             */
            setBranch : function(vQId, vOptIdx, vBranchType, vValue) {
                if(!g_enum.inEnum(g_enum.BranchType, vBranchType)) {
                    return;
                }
                _setBranch(vQId, vOptIdx, vBranchType, vValue);
            },
            removeBranch : function(vQId, vOptIdx) {
                _removeBranch(vQId, vOptIdx);
            },
            isBranch : function(vQId) {
                return _isBranch(vQId);
            },
            isBranchByType : function(vQId, vOptIdx, vBranchType) {
                return _isBranchByType(vQId, vOptIdx, vBranchType);
            },
            
            /**
             * Deal with questions
             */
            /**
             * Get a question
             */
            getQuestion : function(vQId, vFunc) {
                var m_func = vFunc ? vFunc : function() {};
                if(vQId == null || vQId <= 0) {
                    m_func(null);
                    return;
                } else {
                    _getQuestion(vQId, vFunc);
                }
            },
            /**
             * Add a question
             * @param vType {NUI.Quill.Enum.QuestionType}
             * @param vPosition {int}. The position of the question
             */
            addQuestion : function(vType, vPosition) {
                if(!g_enum.inEnum(NUI.Quill.Enum.QuestionType, vType)) {
                    return -1;
                }
                return _addQuestion(vType, vPosition);
            },
            /**
             * Delete a question
             */
            deleteQuestion : function(vQId) {
                _deleteQuestion(vQId);
            },
            /**
             * Paste question
             */
            pasteQuestion : function(vQuestion, vPosition) {
                if(vQuestion == null) {
                    return null;
                }
                return _pasteQuestion(vQuestion, vPosition);
            },
            /**
             * Move a question
             * @param vQId {int}. The target question's id
             * @param vStep {int}. The step count. if vStep>0, move backwards, else forwards
             */
            moveQuestion : function(vQId, vStep) {
                return _moveQuestion(vQId, vStep);
            },
            /**
             * Get the target question's index
             * @param vQId {int}. The target question's id
             */
            getQstIdx : function(vQId) {
                return m_entity.qIdList.getIndex(vQId);
            },
            
            /**
             * Create survey
             * @param vFunc(vSurveyId) {function}. The function to execute after creating the survey
             */
            create : function(vFunc) {
                NUI.Quill.Proxy.Survey.createSurvey(m_entity, function(vSurveyId) {
                    if(vSurveyId > 0) {
                        _refreshId(vSurveyId);
                        vFunc(true);
                    } else {
                        vFunc(false);
                    }
                });
            },
            /**
             * Save survey
             * @param vFunc(vIsSuccess) {function}. The function to execute after saving the survey
             */
            save : function(vFunc) {
                NUI.Quill.Proxy.Survey.saveSurvey(m_entity, function(vSurveyId) {
                    if(vSurveyId > 0) {
                        _refreshId(vSurveyId);
                        vFunc(true);
                    } else {
                        vFunc(false);
                    }
                });
            },
            /**
             * Save a survey as another
             * @param vNewCaption {string}
             * @param vFunc(vNewSurveyId) {function}. The function to execute after saving the survey
             */
            saveAs : function(vNewCaption, vFunc) {
                if(m_entity.id <= 0 || vNewCaption == null || vNewCaption == "" || vNewCaption == m_entity.caption) {
                    vFunc(-1);
                } else {
                    NUI.Quill.Proxy.Survey.saveAsSurvey(m_entity.id, vNewCaption, function(vNewSurveyId) {
                        vFunc(vNewSurveyId);
                    });
                }
            }
        };
        return that;
    };
})();
