WForm = function() {
  this.version = '0.1';
  this.callwhenready = null; 
  this.formelement = null;
  this.errorelement = null;
  this.fields = 0;  
  this.formOK = true;
  this.widgets = new Array();
  this.hasFileUpload = false;
  this.formaction = location.href+'/action';
  this.use_assistant = true;
  this.formmessage = '';
  this.submitstatus = 'OK';
  bindMethods(this);
}

WForm.prototype = {
  init: function(id, element, url, callwhenready) {
    this.id = id;
    this.url = url;           
    this.callwhenready = callwhenready;
    this.element = (element=='body') ? document.getElementsByTagName('body')[0] : $(element);
    this.fields = null;
    this.loadFormDef(); 
  }, 
  useAssistant: function(val) {
    this.use_assistant = val;
  },
  loadFormDef: function() {
    d = loadJSONDoc(this.url);
    d.addCallbacks(this.loadFormFetched, this.loadFormFailed);
  }, 
  loadFormFetched:function(data) {
    this.fields = data[0].fields;    
    this.render();
    this.callwhenready(this.errorelement, this.formelement);
  },             
  loadFormFailed:function(err) {
    alert(err.req.responseText);
  },
  setFormAction: function(url) {
    this.formaction = url;
  },
  render: function() { 
    this.formelement = FORM({'name':this.id,'id':this.id});
    this.errorelement = DIV({'id':'formerror','class':'error','style':'display:none;width:auto;height:auto'},'TestError');
    
    for (ix = 0; ix < this.fields.length; ix++) {
      fld = this.fields[ix];  
      switch (fld.type) {
        case 'String':
          fdef = new WField.String();
          break;
        case 'Email':
          fdef = new WField.Email();
          break;
        case 'Password':
          fdef = new WField.Password();
          break;
        case 'Text':
          fdef = new WField.Text();
          break;
        case 'Integer':
          fdef = new WField.Integer();
          break;
        case 'Date':
          fdef = new WField.Date();
          break;
        case 'Checkbox':
          fdef = new WField.Checkbox();
          break;
        case 'RadioGroup':
          fdef = new WField.RadioGroup();
          break;
        case 'Select':
          fdef = new WField.Select();
          break;
        case 'File':
          fdef = new WField.File();
          this.hasFileUpload = true;
          break;
      }
      fdef.init(fld.id, this.formelement, fld.options, this);
      this.widgets.push(fdef);
    }
  },
  getField: function(id) {
    // Get the field by name
    for (fv = 0; fv < this.widgets.length; fv++) {
      wdg = this.widgets[fv];
      if (wdg.id == id) {
        return wdg;
      }
    }                     
    throw "WForm.getField(): Veld met id "+id+" is niet gedefinieerd";
  },
  setFieldValue: function(id, val) {
    fld = this.getField(id);
    fld.setValue(val);
  },
  getFieldValue: function(id) {
    fld = this.getField(id);
    fld.syncValue();
    return fld.value;
  },
  getFieldValues: function() {
    vals = new Array();
    for (fv = 0; fv < this.widgets.length; fv++) {
      wdg = this.widgets[fv];
      wdg.syncValue();
      vals.push({ 'id' : wdg.id, 'value': wdg.value });
    }                     
    return vals;
  },
  serializeValues: function() { 
    keys = new Array();
    vals = new Array();
    for (fv = 0; fv < this.widgets.length; fv++) {
      wdg = this.widgets[fv];
      wdg.syncValue();
      keys.push(wdg.id);
      vals.push(wdg.value);
    }                     
    return queryString(keys,vals);
  },
  checkForm: function() {
    vals = new Array();
    this.formOK = true;
    vals = filter(this.checkField, this.widgets);
    return vals;    
  },
  checkField: function (ob) {
    fldOK = ob.checkValue();                                                   
    if (!fldOK) {
      this.formOK = false;                                           
      return true;
    } else {
      return this.userCheck(ob)
    }
                                                                                             
  },
  userCheck: function(ob) {
    // Do usercheck
    if (ob.leaveField!=null) {                                      
      lfOK = ob.leaveField();
      if (!lfOK) {
        this.formOK = false;
        return true;          
      }
    }
  },
  showErrors: function() {
    vals = this.checkForm();
    if (vals.length>0) {
      return DIV(null, DIV(null,'De volgende velden bevatten invoerfouten, corrigeer deze en verzend het formulier opniew:'),
        UL(null, map(this.addErrorMessage, vals)));                   
    } else {
      return null;
    }
  },
  addErrorMessage: function(ob) {
    return LI(null, ob.errorMessage);
  },
  submit: function() {                    
    var self = this;          
    nErrs = this.checkForm().length;
    if (nErrs == 0) {
      // No errors, proceed
      if (this.hasFileUpload) {
        this.submitAjaxFormWithAttachments(
          function(evt) {
            src = evt.src();
            rec = {'status':'OK', 'responseText':evt.src().contentWindow.document.body.innerHTML};
            self.submitAjaxReady(rec);
          }
        );
      } else {
        d = this.submitAjaxForm();
        d.addCallbacks(this.submitAjaxReady, this.submitAjaxFailed);
      }
    } else {
      // There are errors in the form, show a message
      this.errorelement = DIV({'id':'formerror','class':'error','style':'display:none;width:auto;height:auto'},this.showErrors());
      swapDOM($('formerror'),this.errorelement)                                      
      slideDown(this.errorelement);
      callLater(nErrs*2.0, slideUp, this.errorelement);
    }    
  },
  submitAjaxForm: function() {
    req = getXMLHttpRequest();       
    req.open('POST', this.formaction, true);
    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
    postBody = this.serializeValues();//queryString(this.formelement);
    return sendXMLHttpRequest(req, postBody); 
  },
  submitAjaxFormWithAttachments: function(callback) {
    this.formelement.setAttribute('target','dummyajax');
    this.formelement.setAttribute('action',this.formaction);
    this.formelement.setAttribute('method','post');
    this.formelement.setAttribute('enctype','multipart/form-data');
    this.formelement.enctype='multipart/form-data';
    this.formelement.target='dummyajax';
    this.formelement.action=this.formaction;

    if (!$('dummyajax')) {
      da = document.createElement('iframe');
      da.setAttribute('id','dummyajax');
      da.setAttribute('name','dummyajax');
      da.name = 'dummyajax';
      da.style.display='none';

      document.getElementsByTagName('body')[0].appendChild(da);
      connect(da, 'onload', callback);
    }
    this.formelement.submit();  
  },
  submitAjaxReady: function(data) {
    log(data.responseText);

    mydata = evalJSONRequest(data);  
    this.submitstatus = mydata.status;
    this.responsedata = mydata;
    try {
      this.formmessage = mydata.message;
    } catch(e) {}
    if (mydata.status == 'OK') {
      map(this.updateSheetValues,mydata.data)
    }
    signal(this, 'afterformsubmit');
  },
  submitAjaxFailed: function (err) {
    log(err.responseText);
    signal(this, 'formsubmitfailed', err);
  },
  updateSheetValues: function(ob) {
    map(this.updateFieldValue, ob.fields)    
  },
  updateFieldValue: function(ob) {
    this.setFieldValue(ob.id, ob.value);
  }
};   

