/// <reference path="helpers.js" />
/// <reference path="callBackFunction.js" />

function FormDependencyRule(id, exp, callback)
{
    this.id = id;
    this.exp = exp;
    this.callback = callback;
}

function FormDependencyHandler(form, rules)
{
    var tmp = new Object();
    for(var i=0; i<rules.length; i++)
    {
        if(rules[i])
        {
            tmp[ rules[i].id ] = rules[i];
        }
    }
        
    this._form = (typeof form == 'string' ? document.forms[ form ] : form);
    this._rules = tmp;
    this._usedElementObjList = new Array();
    this._callbacks = new Object();
    this._elementsToWatch = new Array();
}

FormDependencyHandler.prototype.Attach = function()
{
    var tthis = this;
    var list = this._BuildElementList();
    this._usedElementObjList = this._elementsToWatch;
    for(var i=0; i<list.length; i++)
    {
        var found = false;
        for(var j=0; j<this._usedElementObjList.length; j++)
        {
            if(this._usedElementObjList[j] == null)
                continue;
            if(list[i] == this._usedElementObjList[j])
            {
                found = true;
                break;
            }
        }
        if(!found)
        {
            this._usedElementObjList.push(list[i]);
        }
    }
    for(var k = 0, e = this._usedElementObjList; k < e.length; ++k) 
    {
        var eventsToAttach = ["change"/*, "keyup", "focus", "click", "keydown"*/];
//        if(e[j].tagName && e[j].tagName.toLowerCase()=='input' && e[j].type.toLowerCase()=='checkbox')
//            eventsToAttach = ["change"];
//        else
//            eventsToAttach = ["change"];
        this._AddEvents([e[k]], eventsToAttach, function(ev){ tthis._ChangeHandler(ev); });
    } 
    
    this._ChangeHandler(null);
}

FormDependencyHandler.prototype.RegisterChangeCallback = function(callbackObj)
{
    this._callbacks[callbackObj.id] = callbackObj;
}

FormDependencyHandler.prototype.UnRegisterChangeCallback = function(id)
{
    this._callbacks[id] = null;
}

FormDependencyHandler.prototype.AddElementsToWatch = function(elements)
{
    var obj;
    for(var i=0; i<elements.length; i++)
    {
        obj = Helpers.GetElement(elements[i], this._form);
        if(obj)
        {
            var found = false;
            for(var j=0; j<this._elementsToWatch.length; j++)
            {
                if(this._elementsToWatch[j] == null)
                    continue;
                if(obj == this._elementsToWatch[i])
                {
                    found = true;
                    break;
                }
            }
            if(!found)
                this._elementsToWatch.push(obj);
        }
    }
}

FormDependencyHandler.prototype.RemoveElementsFromWatch = function(elements)
{
    var obj;
    for(var i=0; i<elements.length; i++)
    {
        obj = Helpers.GetElement(elements[i], this._form);
        if(obj)
        {
            for(var j=0; j<this._usedElementObjList.length; j++)
            {
                if(obj == this._usedElementObjList[j])
                {
                    var eventsToAttach = ["change"/*, "keyup", "focus", "click", "keydown"*/];
                    this._RemoveEvents([this._usedElementObjList[j]], eventsToAttach, function(ev){ tthis._ChangeHandler(ev); });
                    this._usedElementObjList[j] = null;
                    break;
                }
            }
        }
    }
}

FormDependencyHandler.prototype.RemoveRules = function(rules)
{
    this._RemoveAllEvents();
    var tmp = new Object();
    var found;
    for(var ruleID in this._rules)
    {
        found = false;
        for(var i=0; i<rules.length; i++)
        {
            if(rules[i].id == ruleID)
            {
                found = true;
                break;
            }
        }
        if(found)
            continue;
        tmp[ruleID] = this._rules[ruleID];
    }
    
    this._rules = tmp;
    this.Attach();
}

FormDependencyHandler.prototype._RemoveAllEvents = function()
{
    for(var j = 0, e = this._usedElementObjList; j < e.length; ++j) 
    {
        var eventsToAttach = ["change"/*, "keyup", "focus", "click", "keydown"*/];
        this._RemoveEvents([e[j]], eventsToAttach, function(ev){ tthis._ChangeHandler(ev); });
    } 
}

FormDependencyHandler.prototype._ChangeHandler = function(e)
{
 	// if ( e != null ) return;

	Helpers.Events.Stop(e);
    var callbackObj;

    for(var callbackName in this._callbacks)
    {
        callbackObj = this._callbacks[callbackName];
        if(callbackObj != null && callbackObj.beforeRules && callbackObj.callback)
            callbackObj.callback(e);
    }
	
    this._CalculateDependencies();
    
    for(var callbackName in this._callbacks)
    {
        callbackObj = this._callbacks[callbackName];
        if(callbackObj != null && !callbackObj.beforeRules && callbackObj.callback)
            callbackObj.callback(e);
    }
}

FormDependencyHandler.prototype._CalculateDependencies = function()
{
    var rule, setupStr, exp, resultStr, result;
     
    for(var i = 0, e = this._usedElementObjList; i < e.length; i++)
    {
        if(this._usedElementObjList[i] == null)
            continue;
        rule = typeof e[i].name != 'undefined' && e[i].name != '' ? this._rules[ e[i].name ] : (typeof e[i].id != 'undefined' && e[i].id != '' ? this._rules[ e[i].id ] : '');
        if(!rule)
            continue;
        setupStr = rule.exp;
        if(typeof setupStr == 'undefined' || setupStr == '')
            continue;
        exp = this._ParseExpression(setupStr);
        
        try
        {
            resultStr = eval(exp);
            result = typeof resultStr == 'boolean' ? resultStr : parseInt(resultStr);
        }
        catch(ex)
        {
            result = false;
        }
        Helpers.Log('Setup: ' + setupStr + '; Parser: '+ exp + '; Result: '+ result +'; Rule: '+ rule.id, exp);
        if(typeof rule.callback == 'function')
        {
            result = rule.callback(result, rule);
        }
        
        if(result != 0)
        {
            this._ShowElement(e[i]);
        }
        else
        {
            this._HideElement(e[i]);
        }
    }
}

FormDependencyHandler.prototype._ParseExpression = function(exp)
{
    var vars = this._GetVariables(exp);
    for(var i=0; i<vars.length; i++)
    {
        var value = this._GetElementValue( this._GetUsedElementByName(vars[i]) );
        var regex = new RegExp("\\b"+ vars[i] +"\\b", "gi");
        if(typeof value == 'number')
        {
            exp = exp.replace(regex, value);
        }
        else
        {
            exp = exp.replace(regex, "'"+ value +"'");
        }
    }
    return exp;
}

FormDependencyHandler.prototype._GetElementValue = function(el)
{
    var type = el.tagName ? el.tagName.toLowerCase() : '';
    if(type == 'input')
        type = el.type;
    var ret = null;
    switch(type)
    {
        case 'checkbox':
            ret = el.checked ? 1 : 0;
            break;
        case 'div':
        case 'span':
            ret = el.style.display == '' ? 1 : 0;
            break;
        case 'radio':
            if(el.length)
            {
                for(var i = 0; i < el.length; ++i)
                {
                    if(el[i].checked)
                    {
                        val = el[i].value;
                        break;
                    }
                }
            }
            break;
        case 'hidden':
        case 'text':
        case 'textarea':
            ret = el.value;
            break;
        case 'select':
            ret = el.options[el.selectedIndex].value;
            break;
        default:
            ret = null;
            break;
    }
    return ret;
}

FormDependencyHandler.prototype._GetUsedElementByName = function(name)
{
    for(var i=0; i<this._usedElementObjList.length; i++)
    {
        if(this._usedElementObjList[i] == null)
            continue;
        if(this._usedElementObjList[i].name == name || this._usedElementObjList[i].id == name)
        {
            //console.log('_GetUsedElementByName >> found: %s', name);
            return this._usedElementObjList[i];
        }
    }
    //console.log('_GetUsedElementByName >> NOT found: %s', name);
    return null;
}

FormDependencyHandler.prototype._ShowElement = function(el) 
{
    if(!el.style)
        return;
    // el.style.display = "inline"; // korabban: "";
    el.style.display = ""; // korabban: "";
    if(el.tagName.toLowerCase()!="div" && el.parentNode.tagName.toLowerCase() == "div")
      el.parentNode.style.display = "";
}

FormDependencyHandler.prototype._HideElement = function(el) 
{
    if(!el.style)
        return;
    el.style.display = "none";
    //if(typeof this.checked !== "undefined") this.checked = false;
    //else this.value = "";
    if(el.tagName.toLowerCase()!="div" && el.parentNode.tagName.toLowerCase() == "div")
      el.parentNode.style.display = "none";
    if(el.tagName.toLowerCase()!="div")
      el.hidden = true;
}

FormDependencyHandler.prototype._BuildElementList = function()
{
    var list = new Array();
    var names = new Object();
    var frmEls = this._form.elements;
    var el = null;
    
    for(var prop in this._rules)
    {
        if(!names[ prop ])
        {
            el = Helpers.GetElement(prop, this._form);
            if(!el)
            {
                Helpers.Log("FormDependencyHandler >> _BuildElementList: Element (%s) skipped, object not exists", prop);
                continue;
            }
            list[list.length] = el;
            names[ prop ] = true;
        }
        var rule = this._rules[prop];
        if(!rule)
            continue;
        var vars = this._GetVariables(rule.exp);
        for(var i=0; i<vars.length; i++)
        {
            if(!names[ vars[i] ])
            {
                list[list.length] = Helpers.GetElement(vars[i], this._form);
                names[ vars[i] ] = true;
            }
        }
    }
    return list;
}

FormDependencyHandler.prototype._GetVariables = function(exp)
{
    var pattGroup = /([\(\s&\|=!]|^)([a-zA-Z][a-zA-Z0-9_]+)([\)\s&\|=<>!]|$)/g;
    var patt = /([\(\s&\|=!]|^)([a-zA-Z][a-zA-Z0-9_]+)([\)\s&\|=<>!]|$)/;
    var tmp = new Array();
    
    resGroup = exp.match(pattGroup);
    if(resGroup)
    {
        for(var i=0; i<resGroup.length; i++)
        {
            res = resGroup[i].match(patt);
            if(res[2] && res[2].length > 0)
                tmp[ tmp.length ] = res[2]; 
        }
    }
    return tmp;
}

FormDependencyHandler.prototype._AddEvents = function(els, evs, f)
{
  for(var i = 0; i < els.length; ++i)
    for(var j = 0; j < evs.length; ++j)
      Helpers.Events.Add(els[i], evs[j], f);
}

FormDependencyHandler.prototype._RemoveEvents = function(els, evs, f)
{
  for(var i = 0; i < els.length; ++i)
    for(var j = 0; j < evs.length; ++j)
      Helpers.Events.Remove(els[i], evs[j], f);
}
