Hotel74.BookingForm = function() {
    var NIGHTS_STR = ['ночь', 'ночи', 'ночей'];

    var $form = $(document.getElementById('booking-form')).submit(function() { return false; });
    var $roomsRows = $form.find('.rooms tbody tr');
    var $extRows = $form.find('.ext tbody tr');
    var $nights = $form.find('.nights-t')

    /** @type {Object} куча всякого хлама */
    var g = {};

    /** @type {Number} Количество ночей */
    var nightsCount = 0;

    /** @type {Number} Количество человек */
    var personsCount = 0;


    /** @type {String} способ оплаты */
    var paymentType;

    /** @type {Array} Массив номеров и услуг */
    var objects = [];

    var selectNameRegExp = /(\w+)_\[(\d+)\]/;
    var priceNameRegExp = /price_\[(\d+)\]/;

    var submitFlag = false;
    var RU = false;

    /**
     * Инициализация контролов
     */
    function _init() {
        if ($form.attr('name') === 'bookingVerify') {
            initVerifyForm();
        } else {
            initBookingForm();
        }
    }

    function initBookingForm() {
        RU = ($nights.html().indexOf('ночь') > -1);
        setupDateFields($form.find('input[name="arrival_date"]'));
        setupNightsField($form.find('input[name="nights"]'));
        setupTypeSelect($form.find('select[name="room_type"]'));
        setupPaymentTypes($form.find('.payment-types'));
        setupRooms();
        setupExt();
        calculate();
        $form.find('.submit span').click(function() { submit(); });
    }

    function initVerifyForm() {
        $form.find('.sback span').click(function() { submitVerify(false); });
        $form.find('.sgo span').click(function() { submitVerify(true); });
    }

    /**
     * Настройка инпута кол-ва суток
     */
    function setupNightsField($input) {
        var timeout, input = $input.get(0);
        input.defaultValue = input.value;
        $input.keydown(_keyDownHandler).change(changeHandler);

        function _keyDownHandler(e) {
            if (timeout) {
                window.clearInterval(timeout);
            }
            timeout = window.setTimeout(keyTimeoutHandler, 120);

        }

        function keyTimeoutHandler() {
            if (input.value != input.defaultValue) {
                changeHandler();
            }
        }

        function changeHandler(e) {
            var val = parseInt(input.value);
            if ((val == input.value) && (val > 0)) {
                input.defaultValue = val;
            } else if ((e) && (e.type === 'change')) {
                val = input.value = input.defaultValue;
            }
            if (val > 0) {
                setNightsCount(val);
            }
        }
        changeHandler();
    }

    /**
     * Настройка поля для выбора дат
     */
    function setupDateFields($input) {
        var selectedDateInput;

        var cal = Calendar.setup({
            dateFormat : '%d.%m.%Y',
            animation : false,
            onSelect : dateSelectHandler,
            onBlur : hideCalendar
        });
        $input.keydown(function(e) {
            if (e.which != 9) {
                return false;
            }
        });
        for (var i = 0; i < $input.length; i++) {
            $(document.createElement('ins'))
                .addClass('d-hellip')
                .html('...')
                .insertAfter($input.eq(i))
                .add($input.eq(i))
                    .mousedown(function() {
                        showCalendar(this);
                        return false;
                    });
        }

        function showCalendar(element) {
            var input = (element.tagName.toLowerCase() === 'input')
                ? element
                : $(element).prev('input').get(0);

            if (selectedDateInput != input) {
                if (selectedDateInput)
                    hideCalendar();

                var val = input.value;
                cal.popup(input);
                cal.selection.reset(Calendar.dateToInt(Calendar.parseDate(val, false)));

                selectedDateInput = input;
                $(selectedDateInput).addClass('focus');
            } else {
                hideCalendar();
            }
        }

        function hideCalendar() {
            if (selectedDateInput) {
                cal.hide();
                $(selectedDateInput).removeClass('focus');
            }
            selectedDateInput = null;

        }

        function dateSelectHandler(cal, selection) {
            if (selectedDateInput)
                selectedDateInput.value = selection.print('%d.%m.%Y');
            hideCalendar();
        }
    }

    /**
     * Настройки селекта типа номера
     */
    function setupTypeSelect($select) {
        $select.change(function(e, trigger) { filterRooms(this.value, trigger); }).triggerHandler('change', [true]);
    }

    /**
     * Настройка форм оплаты
     */
    function setupPaymentTypes($obj) {
        g.$paymentTypes = $obj;
        var $inputs = $obj.find('dt input')
            .change(function() {
                setPaymentType(this.value);
            });
        var $checked = $inputs.filter(':checked');
        if (!$checked.length) {
            $checked = $inputs.eq(0).attr('checked', 'checked');
        }
        $checked.triggerHandler('change');
    }


    /**
     * Настройка номеров
     */
    function setupRooms() {
        for (var i = 0; i < $roomsRows.length; i++) {
            var $row = $roomsRows.eq(i);
            var priceInput = $row.find('input[name^="price"]').get(0);
            if (priceInput) {
                var price = priceInput.value;
                var id = priceInput.name.match(priceNameRegExp);
                if (id) {
                    id = id[1];
                    var room = {
                        'type' : 'room',
                        'id' : id,
                        '$row' : $row,
                        'price' : price.split('|'),
                        'quantity' : 0,
                        'summ' : 0
                    };
                    if (room.price.length > 1) {
                        room.calcForPersons = true;
                    }
                    objects.push(room);
                }
            }
        }
        var $s = $roomsRows.find('select').change(function(e, trigger) {
            var s = this.name.match(selectNameRegExp);
            if (s) {
                var name = s[1];
                var id = s[2];
                var room = getRoomById(id);
                if (room) {
                    if (name === 'pq') {
                        room.persons = parseInt(this.value);
                    } else if (name === 'quantity') {
                        room.quantity = parseInt(this.value);
                    }
                    if ((name !== 'pq') || (!trigger)) {
                        calculate(room, trigger);
                    }
                }
            }
        });
        for (var i = 0; i < $s.length; i++) {
            $s.eq(i).triggerHandler('change', [true]);
        }
        updatePersonsCount();
    }

    function setupExt() {
        for (var i = 0; i < $extRows.length; i++) {
            var $row = $extRows.eq(i);
            var priceInput = $row.find('input[name^="price"]').get(0);
            if (priceInput) {
                var price = priceInput.value.split('|');
                var forDay = (parseInt(price[1]) > 0) ? true : false;
                var forPersons = (parseInt(price[2]) > 0) ? true : false;
                price = price[0];
                var id = priceInput.name.match(priceNameRegExp);
                if (id) {
                    id = id[1];
                    var ext = {
                        'type' : 'ext',
                        'id' : id,
                        '$row' : $row,
                        'price' : price,
                        'forDay' : forDay,
                        'forPersons' : forPersons,
                        'quantity' : 0,
                        'summ' : 0
                    };
                    objects.push(ext);
                }
            }
        }
        var $s = $extRows.find('input[name="services[]"]').change(function(e, trigger) {
            var id = this.value;
            if(!this.__popup){
                var $hd = $(this.parentNode).find(".hd");
                if($hd.length){
                    this.__popup = $(document.createElement("div"))
                                        .addClass("serv-popup")
                                        .html($hd.html())
                                        .appendTo(document.body);
                    if (Browser.isMSIE) {
                        this.__popup.hide();
                    } else {
                        this.__popup.css('opacity', 0);
                    }
                }
            }
            var ext = getExtById(id);
            if (ext) {
                if (this.checked) {
                    ext.quantity = 1;
                    if(this.__popup){
                        __animate(this);
                    }
                } else {
                    ext.quantity = 0;
                }
                calculate(ext, trigger);
            }
        });
        for (var i = 0; i < $s.length; i++) {
            $s.eq(i).triggerHandler('change', [true]);
        }
    }

    function __animate(el){
        if(el.__popup){
            var offset = $(el.parentNode.parentNode).offset();
            el.__popup.css({ left: offset.left,
                             top: offset.top + el.offsetHeight + 6 }).show();
        }
        if(Browser.isMSIE){
            if(el.__timer) window.clearTimeout(el.__timer);
            el.__timer = window.setTimeout(function(){ el.__popup.hide();
                                                       window.clearTimeout(el.__timer);
                                                       el.__timer = null;
                                                      },2000);
        }else{
            if(el.__timer) window.clearTimeout(el.__timer);
                el.__popup.stop().css({ opacity: 1 });
                el.__timer = window.setTimeout(function(){ el.__popup.stop()
                                                                     .animate({opacity : 0},
                                                                               1200, 'swing');
                                                           window.clearTimeout(el.__timer);
                                                           el.__timer = null;
                                                          },2000);
        }
    }

    /**
     * Отфильтровать номера по классу
     * @param {String} filter фильтр
     */
    function filterRooms(filter, trigger) {
        if (filter === 'all') {
            $roomsRows.show();
        } else {
            $roomsRows.hide().filter('.' + filter).show();
        }
        if (!trigger) {
            updatePersonsCount(true);
            calculate();
        }
    }

    /**
     * Установить тип оплаты
     * @param {String} type тип
     */
    function setPaymentType(type) {
        paymentType = type;
        g.$paymentTypes.find('dd').hide().filter('.' + type).show();
    }

    /**
     * Установить количество ночей
     * @param {Number} count количество
     */
    function setNightsCount(count) {
        nightsCount = count;
        if (RU) {
            var p = NIGHTS_STR[0];
            var z = count % 10;
            if (((count > 4) && (count < 21)) || (z > 4) || (z == 0)) {
                p = NIGHTS_STR[2];
            } else if (z > 1) {
                p = NIGHTS_STR[1];
            }
            $nights.html('(' + count + ' ' + p + ')');
        } else {
            $nights.html($nights.html().replace(/\d+/, count));
        }

        recalc();
    }

    /**
     * Найти объект по id
     * @param {Number} id
     * @param {String} type
     */
    function getObjectById(type, id) {
        for (var i = 0; i < objects.length; i++) {
            if ((objects[i].type === type) && (objects[i].id == id)) {
                return objects[i];
            }
        }
        return null;
    }
    function getRoomById(id) { return getObjectById('room', id); }
    function getExtById(id) { return getObjectById('ext', id); }

    /**
     * Пересчтать стоимость
     * @param {Object} object Объект вызвавший событий
     * @param {Boolean} fast Запретить пересчет суммы
     */
    function calculate(object, fast) {
        if (object) {
            if (object.type === 'room') {
                updateRoom(object, fast);
            } else  if (object.type === 'ext') {
                updateExt(object);
            }
            redrawRow(object);
        }
        if (!fast) {
            var total = calculateTotal();
            $form.find('.total-price').html(total.toPrice());
        }
    }

    /**
     * Пересчитать номер
     * @param {Object} object
     */
    function updateRoom(object, fast) {
        var pIdx = 0;
        if ((object.calcForPersons) && (object.persons == 1)) {
            pIdx = 1;
        }
        object.summ = object.price[pIdx] * object.quantity * nightsCount;
        if (!fast) {
            updatePersonsCount(true);
        }
    }

    /**
     * Пересчитать услугу
     * @param {Object} object
     */
    function updateExt(object) {
        object.summ = object.price * object.quantity;
        if (object.forDay) {
            object.summ *= nightsCount;
        }
        if (object.forPersons) {
            object.summ *= personsCount;
        }
    }

    /**
     * Обновить ряд в таблице
     * @param {Object} object
     */
    function redrawRow(object) {
        if (object.$row) {
            object.$row.find('.summ').html(object.summ.toPrice());
        }
    }

    /**
     * Пересчиталь итоговую сумму
     */
    function calculateTotal() {
        var total = 0;
        for (var i = 0; i < objects.length; i++) {
            if (isObjectVisible(objects[i])) {
                total += objects[i].summ;
            }
        }
        return total;
    }

    /**
     * Пересчитать все объекты
     */
    function recalc() {
        for (var i = 0; i < objects.length; i++) {
            calculate(objects[i], true);
        }
        calculate();
    }

    /**
     * Пересчитать количество людей
     * @param {Boolean} ext Пересчитать заодно услуги
     */
    function updatePersonsCount(ext) {
        var count = 0;
        for (var i = 0; i < objects.length; i++) {
            if ((objects[i].type === 'room') && (isObjectVisible(objects[i]))) {
                count += objects[i].persons * objects[i].quantity;
            }
        }
        personsCount = count;
        if (ext) {
            for (var i = 0; i < objects.length; i++) {
                if (objects[i].type === 'ext') {
                    updateExt(objects[i]);
                    redrawRow(objects[i]);
                }
            }
        }
    }

    /**
     * Отправка формы
     */
    function submit() {
        if ((!submitFlag) && (checkFields())) {
            var array = $form.serializeArray();
            var hash = {};
            for (var i = 0; i < array.length; i++) {
                var name = array[i].name.toString();
                var value = array[i].value.toString();
                if (!priceNameRegExp.test(name)) {
                    var flag = true;
                    var s = name.match(selectNameRegExp);
                    if (s) {
                        var room = getRoomById(s[2]);
                        if ((!room) || (!isObjectVisible(room))) {
                            flag = false;
                        }
                    }
                    if (flag) {
                        if (hash[name]) {
                            if (typeof hash[name] === 'string') {
                                hash[name] = new Array(hash[name]);
                            }
                            hash[name].push(value);
                        } else {
                            hash[name] = value;
                        }

                    }
                }
            }
            hash['_do'] = 'booking';
            submitFlag = true;
            Hotel74.getJSON(hash, responseHandler);
        }
    }

    function checkFields() {
        var flag = true;
        $form.find('.field').removeClass('error');
        $form.find('.f-error, .q-error').hide();
        var $requiredFields = $form.find('input,textarea').filter('.required:visible');
        for (var i = 0; i < $requiredFields.length; i++) {
            var $field = $requiredFields.eq(i);
            var val = $field.val().trim();
            if (val == '') {
                $field.parent().addClass('error');
                flag = false;
            }
        }
        var quantity = 0;
        for (var i = 0; i < objects.length; i++) {
            if ((objects[i].type === 'room') && (isObjectVisible(objects[i]))) {
                quantity += objects[i].quantity;
            }
        }
        var scrollTo = 0;
        if (!flag) {
            scrollTo = $form.find('.error').eq(0).offset().top;
            $('.f-error', $form).show();
        }
        if (quantity == 0) {
            flag = false;
            var rTop = $form.find('.rooms').offset().top;
            if (rTop < scrollTo) {
                scrollTo = rTop;
            }
            $('.q-error', $form).show();
        }
        if (!flag) {
            $(document.documentElement).animate({
                scrollTop : scrollTo - 40
            }, 300, 'swing');
        }
        return flag;
    }

    function isObjectVisible(object) {
        return ((object.$row) && (object.$row.filter(':visible').length));
    }

    function responseHandler(data) {
        submitFlag = false;
        if (data.errors) {
            for (var i = 0; i < data.errors.length; i++) {
                $form.find('input,textarea').filter('[name="' + data.errors[i] + '"]')
                    .parent().addClass('error');
            }
            $(document.documentElement).animate({
                scrollTop : $form.find('.error').eq(0).offset().top - 40
            }, 300, 'swing');
        } else if (data.ok) {
            window.location.reload(true);
        }
    }

    function submitVerify(state) {
        if (!submitFlag) {
            var array = $form.serializeArray();
            var hash = {};
            for (var i = 0; i < array.length; i++) {
                var name = array[i].name.toString();
                var value = array[i].value.toString();
                hash[name] = value;
            }
            hash['_do'] = 'booking_verify';
            hash['v'] = (state) ? 1 : 0;
            submitFlag = true;
            Hotel74.getJSON(hash, responseHandler);
        }
    }

    _init();

};

