var Lyase = Class.def({
  __static__ : {
    Version : '0.0.1',
    ROOT_PATH : "",
    LIB_PATH  : "",
    loadedFiles : [],

    require  : function(libraryPath) {
      document.write('<script type="text/javascript" src="'+libraryPath+'"></script>');
    },

    findPath : function() {
      var scriptTags = document.getElementsByTagName("script");
      for(var i=0,l=scriptTags.length;i<l;i++) {
        if(scriptTags[i].src && scriptTags[i].src.match(/lib\/lyase\/lyase\.js(\?.*)?$/)) {
          Lyase.ROOT_PATH = scriptTags[i].src.replace(/lib\/lyase\/lyase\.js(\?.*)?$/,'');
          Lyase.LIB_PATH = Lyase.ROOT_PATH+"lib/";
          var data = scriptTags[i].src.split("?");
          if(data.length>1) Lyase.loadedFiles = data.last().split(",");
          return;
        }
      }
    },

    load: function(test) {
      if((typeof Prototype=='undefined') ||
        parseFloat(Prototype.Version.split(".")[0] + "." +
                   Prototype.Version.split(".")[1]) < 1.4)
        throw("Lyase requires the Prototype JavaScript framework >= 1.4.0");
      Lyase.findPath();
      for(var i=0,f = Lyase.loadedFiles, l=f.length; i < l; i++) {
        Lyase.require([Lyase.LIB_PATH,f[i],".js"].join(""));
      }
    }
  }
});

var $Lyase = null;
Lyase.Dispather = Class.def({
  __static__ : {
    dispatch : function() {
      eval("$Lyase = new "+Lyase.Dispather.findController()+"();");
    },

    findController : function() {
      return document.body.getAttribute('lyase:c') || "Lyase.Controller";
    }
  }
});

Lyase.Helper = Class.def({
  __static__ : {
    /*configure*/
    baseImagePath : "/images/",
    /*configure*/


    tag : function(tag, options, content) {
      var html = ['<',tag," ",$H(options||{}).toAttribute()].join("");
      if(!content) return html+" />";
      return html+['>',content.join ? content.join("") : content ,'</', tag, '>'].join("");
    },

    imagePath : function(file) {
      return Lyase.Helper.baseImagePath+file;
    },

    _imageOption : function(file, options) {
      options = options || {};
      options.src = Lyase.Helper.imagePath(file);
      return options;
    },

    imageTag : function(file, options) {
      return Lyase.Helper.tag("img", Lyase.Helper._imageOption(file, options));
    },

    nodes : {},

    node : function(tag, options, children) {
      var node = document.createElement(tag);
      for(i in (options || {})) node.setAttribute(i, options[i]);
      if(!children) return node;
      (children.constructor == Array ? children : [children]).each(function(c){
        if(c.constructor == String) c = document.createTextNode(c);
        node.appendChild(c);
      });
      return node;
    },

    imageNode : function(file, options) {
      return Lyase.Helper.node("img", Lyase.Helper._imageOption(file, options));
    }
  }
});

(function(){
var h = Lyase.Helper;
["button", "tt", "pre", "h1", "h2", "h3", "br", "hr", "label", "textarea", "form", "select", "option", "optgroup", "legend", "fieldset", "p", "ul", "ol", "li", "td", "tr", "thead", "tbody", "tfoot", "table", "th", "input", "span", "a", "div", "img",].each(function(tag){h.nodes[tag]=h.node.curry(tag)});
})();

Lyase.Controller = Class.def({__include__ : Lyase.Helper,
  validator  : null,
  hotKey     : null,

  initialize : function(){
    if(this.validator) this.validator.regist();
  },

  addValidationRules : function(rules, klass) {
    this.validator = this.validator || new (klass || Lyase.Validator)();
    this.validator.add(rules);
  },

  addBehaviourRules : function(rules) {
    Behaviour.list = [rules];
    Behaviour.apply();
    Behaviour.list = [];
  },

  addEventRules     : function(rules) {
    var _rules = {};
    for(sel in rules) _rules[sel] = this._createObserver(rules[sel]);
    this.addBehaviourRules(_rules);
  },

  _createObserver : function(rule) {
    return function(el){ for(type in rule) Event.observe(el, type, rule[type]); };
  },

  addHotKeyRules       : function(rules) {
    this.hotKey = this.hotKey || new HotKey();
    rules = (typeof rules == "function") ? rules(this.hotKey) : rules;
    if(rules && rules.constructor == Object) {
      for (var rule in rules) this.hotKey.add(rule, rules[rule]);
    }
  }
});


Lyase.Validator = Class.def({
  __static__ : {
    /*configure*/
    errorImage : "",
    okImage : "",
    infoImage : "",
    imagePosition : "previous"
    /*configure*/
  },

  initialize : function() {
  },

  add : function(rules) {
    rules = (typeof rules == "function") ? rules(this) : rules;
    if(!this.rules)
      this.rules = rules
    else
      Object.extend(this.rules, rules);
  },

  regist : function() {
    var listOld = Behaviour.list;
    Behaviour.list = [];
    Validator.register(this.rules);
    Behaviour.apply();
    Behaviour.list = listOld;
  },

  message : function(type, msg) {
    var message = [msg], self = Lyase.Validator;
    message[self.imagePosition == "after" ? "push" : "unshift"](self[type+"Image"]);
    return message.join("");
  },

  error : function(msg) {
    return this.message("error", msg || "");
  },

  ok    : function(msg) {
    return this.message("ok", msg || "");
  },

  info  : function(msg) {
    return this.message("info", msg || "");
  },

  _baseValidation : function(optionz) {
     var val = {};
     val.options = Object.extend({
      require : true,
      requiredMessage : "必ず入力してください。",
      okMessage       : "",
      infoMessage     : null
    }, optionz || {});
    val.rule = {};
    if(val.options.infoMessage) {
      val.rule["/^$/"] = this.info(val.options.infoMessage);
    }else if(val.options.require){
      val.rule["/^$/"] = this.error(val.options.requiredMessage);
    }else if(!val.options.require) {
      val.rule["/^$/"] = this.ok(val.options.okMessage);
    }
    return val;
  },

  validatesEmailOf : function(optionz){
    var val = this._baseValidation(Object.extend({
      invalidEmailMessage : "メールアドレスが正しくありません。"
    }, optionz || {}));
    val.rule["/^([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})$/"] = this.ok(val.options.okMessage),
    val.rule["/.+/"] = this.error(val.options.invalidEmailMessage);
    return val.rule;
  },

  validatesUrlOf : function(optionz) {
    var val = this._baseValidation(Object.extend({
      invalidUrlMessage : "URLが正しくありません。"
    }, optionz || {}));
    val.rule["/s?https?:\\/\\/[-_\\.!~*'()a-zA-Z0-9;\\/?:@&=+$,%#]+\\.[-_\\.!~*'()a-zA-Z0-9;\\/?:@&=+$,%#]+/"] = this.ok(val.options.okMessage),
    val.rule["/.+/"] = this.error(val.options.invalidUrlMessage);
    return val.rule;
  },

  validatesFormatOf : function(optionz) {
    var val = this._baseValidation(Object.extend({
      format : "/.*/",
      invalidFormatMessage : "フォーマットが正しくありません。",
      maximum : null,
      tooLongMessage : "長すぎます。",
      minimum : null,
      tooShortMessage : "短すぎます。",
      infoMessage      : ""
    }, optionz || {}));
    var options = val.options;
    val.rule[val.options.format] = (function(element) {
      if(options.maximum && element.value.length > options.maximum) {
        return this.error(options.tooLongMessage);
      }
      if(options.minimum && element.value.length < options.minimum) {
        return this.error(options.tooShortMessage);
      }
      return this.ok(this.okMessage);
    }).bind(this);
    if(!val.rule["/.*/"]) val.rule["/.*/"] = this.error(options.invalidFormatMessage);
    return val.rule;
  }
});

Lyase.Model = Class.def({
  __static__ : {
    /*configure*/
    prefix : "lyase", /*cookie prefix. each subclass should overwrite this.*/
    defaultDays : 30, /*default days to expire*/
    defaultPath : "/",
    defaultValues : {}, /* schema */
    /*configure*/

    findById : function(klass, id, options) {
      options = options || {};
      var name = [klass.prefix,"_",id,"="].join("");
      var cookies = document.cookie.split(";"), model, c;
      for(var i=0,l=cookies.length; i < l; i++) {
        c = cookies[i];
        while(c.charAt(0)==' ') c = c.substring(1,c.length);
        if(c.indexOf(name) == 0 ) {
          eval("model = "+ unescape(c.substring(name.length, c.length)));
          break;
        }
      }
      return model ? options.onlyData ? model 
                                      :new klass(model)
                   : null;
    },

    findOrCreateById : function(klass, id, options) {
      var model = Lyase.find(klass, id, options);
      options = options || {};
      if(model) return model;
      if(options.onlyData) return klass.dafault || {};
      return new klass(Object.extend({id:id}, klass.defaultValues || {}));
    }

  },

  initialize : function(values) {
    this.update(values);
  },

  update : function(values) {
    Object.extend(this, values || {});
  },

  fullId : function() {
    if(!this._fullId) this._fullId = this.constructor.prefix+"_"+this.id;
    return this._fullId;
  },

  save : function(days, path) {
    if(!this.id) throw "Could not be persisted because this object has no id.";
    var date = new Date();
    date.setTime(date.getTime()+(days || Lyase.Model.defaultDays*24*60*60*1000));
    var expires ="; expires="+date.toGMTString();
    document.cookie = [this.fullId(),"=",escape(Object.toJSON(this)),expires,"; path=", path||Lyase.Model.defaultPath].join("");
  }

});
Lyase.find = Lyase.Model.findById;
Lyase.findOrCreate = Lyase.Model.findOrCreateById;


Lyase.Initializer = Class.def({
  __static__ : {
    run : function(block) {
      block(Lyase);
    }
  }
});

Lyase.load();
Event.observe(window, "load", Lyase.Dispather.dispatch);
