/**
 * @author ZhangHuihua@msn.com
 */
( function($) {
    $.setRegional("datepicker", {
        dayNames: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ] ,
        monthNames: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
    });
    $.fn.datepicker = function(opts) {
        var setting = {
            box$: "#calendar",
            year$: "#calendar [name=year]", month$: "#calendar [name=month]",tmInputs$: "#calendar .time :text", 
            hour$: "#calendar .time .hh" , minute$: "#calendar .time .mm", second$: "#calendar .time .ss",
            tmBox$: "#calendar .tm", tmUp$: "#calendar .time .up", tmDown$: "#calendar .time .down" ,
            close$: "#calendar .close", calIcon$:"a.inputDateButton", main$: "#calendar .main", days$: "#calendar .days", 
            dayNames$: "#calendar .dayNames" , clearBut$: "#calendar .clearBut", okBut$: "#calendar .okBut"
        };
        function changeTmMenu(sltClass) {
            var $tm = $(setting.tmBox$);
            $tm.removeClass("hh").removeClass("mm").removeClass("ss");
            if (sltClass ) {
                $tm.addClass(sltClass);
                $(setting.tmInputs$).removeClass("slt").filter("." + sltClass).addClass("slt");
            }
        }
        function clickTmMenu($input, type) {
            $(setting.tmBox$).find("." + type + " li").each(function() {
                var $li = $(this);
                $li.on("click", function() {
                    $input.val($li.text());
                });
            });
        }
        function keydownInt(e) {
            if (! ( ( e.keyCode >= 48 && e.keyCode <= 57 ) || ( e.keyCode == JUI.keyCode.DELETE || e.keyCode == JUI.keyCode.BACKSPACE ) ) ) {
                return false;
            }
        }
        function changeTm($input, type) {
            var ivalue = parseInt($input.val()), istart = parseInt($input.attr("start")) || 0, iend = parseInt($input.attr("end"));
            var istep = parseInt($input.attr("step") || 1);
            if (type == 1 ) {
                if (ivalue <= iend - istep ) {
                    $input.val(ivalue + istep);
                }
            } else if (type == -1 ) {
                if (ivalue >= istart + istep ) {
                    $input.val(ivalue - istep);
                }
            } else if (ivalue > iend ) {
                $input.val(iend);
            } else if (ivalue < istart ) {
                $input.val(istart);
            }
        }
        return this.each(function() {
            var $this = $(this);
            new Datepicker($this.val(), opts);
            function generateCalendar(dp) {
                var dw = dp.getDateWrap();
                var minDate = dp.getMinDate();
                var maxDate = dp.getMaxDate();
                var monthStart = new Date(dw.year, dw.month - 1, 1);
                var startDay = monthStart.getDay();
                var dayStr = "";
                if (startDay > 0 ) {
                    monthStart.setMonth(monthStart.getMonth() - 1);
                    var prevDateWrap = dp.getDateWrap(monthStart);
                    for (var t = prevDateWrap.days - startDay + 1; t <= prevDateWrap.days; t++) {
                        var _date = new Date(dw.year, dw.month - 2, t);
                        var _ctrClass = ( _date >= minDate && _date <= maxDate ) ? "": "disabled";
                        dayStr += "<dd class=\"other " + _ctrClass + "\" chMonth=\"-1\" day=\"" + t + "\">" + t + "</dd>";
                    }
                }
                for (var t = 1; t <= dw.days; t++) {
                    var _date = new Date(dw.year, dw.month - 1, t);
                    var _ctrClass = ( _date >= minDate && _date <= maxDate ) ? "": "disabled";
                    if (t == dw.day ) {
                        dayStr += "<dd class=\"slt " + _ctrClass + "\" day=\"" + t + "\">" + t + "</dd>";
                    } else {
                        dayStr += "<dd class=\"" + _ctrClass + "\" day=\"" + t + "\">" + t + "</dd>";
                    }
                }
                for (var t = 1; t <= 42 - startDay - dw.days; t++) {
                    var _date = new Date(dw.year, dw.month, t);
                    var _ctrClass = ( _date >= minDate && _date <= maxDate ) ? "": "disabled";
                    dayStr += "<dd class=\"other " + _ctrClass + "\" chMonth=\"1\" day=\"" + t + "\">" + t + "</dd>";
                }
                var $days = $(setting.days$).html(dayStr).find("dd");
                $days.not(".disabled").on("click", function() {
                    var $day = $(this);
                    if (!dp.hasTime() ) {
                        $this.val(dp.formatDate(dp.changeDay($day.attr("day"), $day.attr("chMonth"))));
                        closeCalendar();
                    } else {
                        $days.removeClass("slt");
                        $day.addClass("slt");
                    }
                });
                if (!dp.hasDate() ) {
                    $(setting.main$).addClass("nodate"); // 仅时间，无日期
                }

                if (dp.hasTime() ) {
                    $("#calendar .time").show();

                    var iHour = dw.hour, iMinute = dw.minute, iSecond = dw.second;

                    if (dp.opts.defaultTime && !$this.val()) {
                        var timeStr = dp.opts.defaultTime.split(":");
                        iHour = parseInt(timeStr[0]);
                        iMinute = parseInt(timeStr[1]);
                        iSecond = parseInt(timeStr[2]);
                    }
                    iMinute = parseInt(iMinute / dp.opts.mmStep) * dp.opts.mmStep;
                    iSecond = dp.hasSecond() ? iSecond : 0;

                    var $hour = $(setting.hour$).val(iHour).focus(function() {
                        changeTmMenu("hh");
                    });
                    var $minute = $(setting.minute$).val(iMinute).attr("step", dp.opts.mmStep).focus(function() {
                        changeTmMenu("mm");
                    });
                    var $second = $(setting.second$).val(iSecond).attr("step", dp.opts.ssStep).focus(function() {
                        changeTmMenu("ss");
                    });
                    $hour.add($minute).add($second).on("click", function() {
                        return false
                    });
                    clickTmMenu($hour, "hh");
                    clickTmMenu($minute, "mm");
                    clickTmMenu($second, "ss");
                    $(setting.box$).on("click", function() {
                        changeTmMenu();
                    });
                    var $inputs = $(setting.tmInputs$);
                    $inputs.keydown(keydownInt).each(function() {
                        var $input = $(this);
                        $input.keyup(function() {
                            changeTm($input, 0);
                        });
                    });
                    $(setting.tmUp$).on("click", function() {
                        $inputs.filter(".slt").each(function() {
                            changeTm($(this), 1);
                        });
                    });
                    $(setting.tmDown$).on("click", function() {
                        $inputs.filter(".slt").each(function() {
                            changeTm($(this), -1);
                        });
                    });
                    if (!dp.hasHour() ) {
                        $hour.attr("disabled", true);
                    }
                    if (!dp.hasMinute() ) {
                        $minute.attr("disabled", true);
                    }
                    if (!dp.hasSecond() ) {
                        $second.attr("disabled", true);
                    }
                }
            }
            function closeCalendar() {
                $(setting.box$).remove();
                $(document).off("click", null, closeCalendar);
            }
            $this.on("click", function() {
                closeCalendar();
                var dp = new Datepicker($this.val(), opts);
                var offset = $this.offset();
                var iTop = offset.top + this.offsetHeight;
                $($.parseHTML(JUI.frag["calendarFrag"], document, true)).appendTo("body").css({
                    left: offset.left + "px", top: iTop + "px"
                }).show().on("click", function(event) {
                    event.stopPropagation();
                });
                var dayNames = "";
                $.each($.regional.datepicker.dayNames, function(_i, v) {
                    dayNames += "<dt>" + v + "</dt>"
                });
                $(setting.dayNames$).html(dayNames);
                var dw = dp.getDateWrap();
                var $year = $(setting.year$);
                var yearstart = dp.getMinDate().getFullYear();
                var yearend = dp.getMaxDate().getFullYear();
                for (y = yearstart; y <= yearend; y++) {
                    $year.append("<option value=\"" + y + "\"" + ( dw.year == y ? " selected=\"selected\"": "" ) + ">" + y + "</option>");
                }
                var $month = $(setting.month$);
                $.each($.regional.datepicker.monthNames, function(i, v) {
                    var m = i + 1;
                    $month.append("<option value=\"" + m + "\"" + ( dw.month == m ? " selected=\"selected\"": "" ) + ">" + v + "</option>");
                });

                // generate calendar
                generateCalendar(dp);
                $year.add($month).change(function() {
                    dp.changeDate($year.val(), $month.val());
                    generateCalendar(dp);
                });

                // fix top
                var iBoxH = $(setting.box$).outerHeight(true);
                if (iTop > iBoxH && iTop > $(window).height() - iBoxH ) {
                    $(setting.box$).css("top", offset.top - iBoxH);
                }
                $(setting.close$).on("click", function() {
                    closeCalendar();
                });
                $(setting.clearBut$).on("click", function() {
                    $this.val("");
                    closeCalendar();
                });
                $(setting.okBut$).on("click", function() {
                    var $dd = $(setting.days$).find("dd.slt");
                    if ($dd.hasClass("disabled") ) {
                        return false;
                    }
                    var date = dp.changeDay($dd.attr("day"), $dd.attr("chMonth"));
                    if (dp.hasTime() ) {
                        date.setHours(parseInt($(setting.hour$).val()));
                        date.setMinutes(parseInt($(setting.minute$).val()));
                        date.setSeconds(parseInt($(setting.second$).val()));
                    }
                    $this.val(dp.formatDate(date));
                    closeCalendar();
                });
                $(document).on("click", null, null, closeCalendar);
                return false;
            });

            if($this.next().is(setting.calIcon$)){
                $this.next().on("click", function(){
                    $this.trigger("click");
                    return false;
                });
            }
        });
    }
    var Datepicker = function(sDate, opts) {
        this.opts = $.extend({
            pattern: "yyyy-MM-dd", minDate: "1970-01-01", maxDate: "2099-12-31", mmStep: 1, ssStep: 1
        }, opts);

        // 动态minDate、maxDate
        var now = new Date();
        this.opts.minDate = now.formatDateTm(this.opts.minDate);
        this.opts.maxDate = now.formatDateTm(this.opts.maxDate);
        this.sDate = sDate.trim();
    }
    $.extend(Datepicker.prototype, {
        get: function(name) {
            return this.opts[name];
        },
        _getDays: function(y, m) {// 获取某年某月的天数

            return m == 2 ? ( y % 4 || ! ( y % 100 ) && y % 400 ? 28: 29 ): ( /4|6|9|11/.test(m) ? 30: 31 );
        },
        _minMaxDate: function(sDate) {
            var _count = sDate.split("-").length - 1;
            var _format = "y-M-d";
            if (_count == 1 ) {
                _format = "y-M";
            } else if (_count == 0 ) {
                _format = "y";
            }
            return sDate.parseDate(_format);
        },
        getMinDate: function() {
            return this._minMaxDate(this.opts.minDate);
        },
        getMaxDate: function() {
            var _sDate = this.opts.maxDate;
            var _count = _sDate.split("-").length - 1;
            var _date = this._minMaxDate(_sDate);

            if (_count < 2 ) { // format:y-M、y

                var _day = this._getDays(_date.getFullYear(), _date.getMonth() + 1);
                _date.setDate(_day);
                if (_count == 0 ) {// format:y

                    _date.setMonth(11);
                }
            }
            return _date;
        },
        getDateWrap: function(date) { // 得到年,月,日

            if (!date ) {
                date = this.parseDate(this.sDate) || new Date();
            }
            var y = date.getFullYear();
            var m = date.getMonth() + 1;
            var days = this._getDays(y, m);
            return {
                year: y, month: m, day: date.getDate(), hour: date.getHours(), minute: date.getMinutes(), second: date.getSeconds(), days: days, date: date
            }
        } ,
        /**
         * @param {year:2010,
         *            month:05, day:24}
         */
        changeDate: function(y, m, d) {
            var date = new Date(y, m - 1, d || 1);
            this.sDate = this.formatDate(date);
            return date;
        },
        changeDay: function(day, chMonth) {
            if (!chMonth ) {
                chMonth = 0;
            }
            var dw = this.getDateWrap();
            return this.changeDate(dw.year, dw.month + parseInt(chMonth), day);
        },
        parseDate: function(sDate) {
            if (!sDate ) {
                return null;
            }
            return sDate.parseDate(this.opts.pattern);
        },
        formatDate: function(date) {
            return date.formatDate(this.opts.pattern);
        },
        hasHour: function() {
            return this.opts.pattern.indexOf("H") != -1;
        },
        hasMinute: function() {
            return this.opts.pattern.indexOf("m") != -1;
        },
        hasSecond: function() {
            return this.opts.pattern.indexOf("s") != -1;
        },
        hasTime: function() {
            return this.hasHour() || this.hasMinute() || this.hasSecond();
        },
        hasDate: function() {
            var _dateKeys = ["y", "M", "d", "E"];
            for (var i = 0; i < _dateKeys.length; i++) {
                if (this.opts.pattern.indexOf(_dateKeys[i]) != -1 ) {
                    return true;
                }
            }
            return false;
        }
    });
} )(jQuery);