// Copyright (c) 2010 Simo Niemelä
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
Object.size = function(obj) {
  var size = 0, key;
  for (key in obj) {
    if (obj.hasOwnProperty(key)) {
      size++;
    }
  }
  return size;
}

Validations = function(options) {
  this.customValidations = new Array();
  this.validatableFields = new Array();
  this.errors = {};
  
  this.opt = {
    errors_container_id: 'errors',
    error_container_class: 'error',
    errors_container_style: '',
    error_container_style: 'border: 1px solid red; padding: 2px 4px; background: #FAC6C6',
    field_error_class: 'field_error',
    field_error_style: 'border: 2px solid red',
    element_as_field_name: 'title',
    
    wrap_errors_on_fields: true,
    inline_styles: true,
    add_classes: true,
    
    // Virheviestit
    field_required_message: ' on pakollinen kenttä',
    
    // SisÃ¤iset validoinnit
    validates_presence_of_fields: true
  };
  
  for (var property in options) {
    this.opt[property] = options[property];
  }
} // Validations

Validations.prototype = {
  // RekisterÃ¶i kÃ¤yttÃ¤jÃ¤n mÃ¤Ã¤rittelemÃ¤n tarkistusfunktion.
  //
  // Tarkistusfunktio tÃ¤ytyy sisÃ¤ltÃ¤Ã¤ invoke-metodin, joka pitÃ¤Ã¤
  // sisÃ¤llÃ¤Ã¤n tarkistusprosessin ja palautettavan totuusarvon.
  // Esimerkki tarkistusfunktiosta:
  //
  //  function omaTarkistusFunktio() {
  //    this.message = 'Viesti, joka nÃ¤ytetÃ¤Ã¤n virheen jÃ¤lkeen';
  //    this.errors_on_fields = new Array(); // Kenttien id:t joissa ilmeni ongelmia
  //    var field_id = 'kentta_id';
  //    
  //    // Prosessi, jonka sisÃ¤llÃ¤ kÃ¤ydÃ¤Ã¤n tarkistus.
  //    // Kenttien id/id:t joissa ilmeni ongelmia, lisÃ¤tÃ¤Ã¤n ne taulukkoon.
  //    this.invoke = function() {
  //      if (document.getElementById(field_id).value == '') {
  //        this.errors_on_fields.push(field_id);
  //      }
  //    }
  //  }
  registerValidation: function(validation) {
    this.customValidations.push(validation);
  },
  
  // Ottaa parameterina taulukon, jossa on tarkistusfunktiota
  // ja rekisterÃ¶i funktiot yksitellen.
  // 
  // ks. registerValidation
  registerValidations: function(validations) {
    if (validations instanceof Array) {
      for (var i = 0; i < validations.length; ++i) {
        this.registerValidation(validations[i]);
      }
    }
  },
  
  isValid: function() {
    return Object.size(this.errors) == 0;
  },
  
  addError: function(field, message) {
    if (!(this.errors[field] instanceof Array)) {
      this.errors[field] = new Array();
    }
    this.errors[field].push(message);
  },
  
  resetErrors: function() {
    if (this.opt.wrap_errors_on_fields) {
      var len = this.validatableFields.length;

      for (var i = 0; i < len; ++i) {
        var element = document.getElementById(this.validatableFields[i]);
        element.style.cssText = '';
        element.setAttribute('class', '');
      }
    }

    this.errors = null;
    this.errors = {};
  },
  
  showErrors: function() {
    var errorsContainer = document.getElementById(this.opt.errors_container_id);
    var html = '';
    for (var field in this.errors) {
      for (var i = 0; i < this.errors[field].length; i++) {
        html += '<p'; 
        if (this.opt.inline_styles) {
          html += ' style="'+ this.opt.error_container_style +'"';
        }
        
        if (this.opt.add_classes) {
          html += ' class="'+ this.opt.error_container_class +'"';
        }
        
        html += '><span>' + this.getProperFieldName(field) + this.errors[field][i] + '</span></p>';
      }
      
      if (this.opt.wrap_errors_on_fields) {
        this.wrapErrorOnField(field);
      }
    }
    
    if (this.opt.inline_styles) {
      errorsContainer.style.cssText = this.opt.errors_container_style;
    }
    errorsContainer.innerHTML = html;
  },
  
  getProperFieldName: function(field) {
    return document.getElementById(field).getAttribute(this.opt.element_as_field_name) || field;
  },
  
  wrapErrorOnField: function(field) {
    var element = document.getElementById(field);
    if (this.opt.inline_styles) {
      element.style.cssText = this.opt.field_error_style;
    }
    
    if (this.opt.add_classes) {
      element.setAttribute('class', this.opt.field_error_class);
    }
  },
  
  // Suoritetaan sisÃ¤iset sekÃ¤ kÃ¤yttÃ¤jÃ¤n mÃ¤Ã¤rittelemÃ¤t 
  // tarkistukset.
  run: function() {
    this.resetErrors();
    this.runAllValidations();
    
    if (!this.isValid()) {
      this.showErrors();
      return false;
    }
    
    return true;
  },
  
  // Suorittaa kÃ¤yttÃ¤jÃ¤n mÃ¤Ã¤rittelemÃ¤t tarkistusfunktiot.
  runCustomValidations: function() {
    for (var i = 0, customsSize = this.customValidations.length; i < customsSize; ++i) {
      var customValidation = new this.customValidations[i];
      customValidation.validate();
      var errors = customValidation.errors_on_fields.length;
      
      if (errors > 0) {
        for (var j = 0; j < errors; j++) {
          this.addError(customValidation.errors_on_fields[j], customValidation.message);
        }
      }
    }
  },
  
  runInternalValidations: function() {
    this.validatesPresenceOfFields();
  },
  
  runAllValidations: function() {
    this.runInternalValidations();
    this.runCustomValidations();
  },
  
  addValidatableFields: function(id_fields) {
    if (id_fields instanceof Array) {
      for (var i = 0; i < id_fields.length; ++i) {
        this.validatableFields.push(id_fields[i]);
      }
    }

    if (id_fields.constructor == String) {
      this.validatableFields.push(id_fields);
    }
  },
  
  validatesPresenceOfFields: function() {
    if (!this.opt.validates_presence_of_fields) {
      return;
    }
    
    for (var i = 0; i < this.validatableFields.length; i++) {
      var field = this.validatableFields[i];
      var element = document.getElementById(field) || document.getElementsByName(field)[0];
      if (!element) {
        continue;
      }
      
      var fieldValid = false;
      switch (element.tagName.toLowerCase()) {
        case 'input':
          fieldValid = element.value != '';
          break;
        case 'select':
          var index = element.selectedIndex;
          fieldValid = element[index].value != '';
          break;
        default:
          break;
      }
      
      if (!fieldValid) {
        this.addError(field, this.opt.field_required_message);
      }
    }
  }
} // Validations.prototype

