function WidgetDate(params) {
  this.input = $(params.input);
  this.selector = $(params.selector);
  this.selector.title = 'Календарь';
  this.container = this.getContainer();
  this.useTime = params.useTime || false;
  this.dateFormat = params.dateFormat || 'd.m.Y';
  return this.initialize();
}

WidgetDate.prototype = {
  daysOfWeek: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'],
  monthes: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']
};

WidgetDate.prototype.initialize = function() {
  var thisObj = this;
  var onSelectorClick = function(e) {
    e = e || window.event;
    thisObj.buildCalendar((thisObj.getDate() || false));
    thisObj.toggleDisplayCalendar();
    thisObj.input.blur();
    return false;
  };
  this.selector.onclick = onSelectorClick;
  this.input.onclick = function() {
    thisObj.hide();
    return true;
  };
  return this;
}

WidgetDate.prototype.getDate = function() {
  var value = this.input.value.trim();
  if (value == '') {
    return null;
  }
  var re = '^' + this.dateFormat.replace(/[dm]/g, '(\\d{1,2})').replace('Y', '(\\d{4})');
  this.useTime && (re += '\\s+(\\d{1,2}):(\\d{1,2}):(\\d{1,2})');
  re += '$';
  re = new RegExp(re);
  if (!re.test(value)) {
    return null;
  }
  if (this.useTime) {
    var res = value.match(re);
    !(parseInt(res[4]) >= 0 && parseInt(res[4]) <= 23) && (res[4] = '00');
    !(parseInt(res[5]) >= 0 && parseInt(res[5]) <= 59) && (res[5] = '00');
    !(parseInt(res[6]) >= 0 && parseInt(res[6]) <= 59) && (res[6] = '00');
    return new Date(res[3] + '/' + res[2] + '/' + res[1] + ' ' + res[4] + ':' + res[5] + ':' + res[6]);
  }
  return new Date(value.replace(re, '$3/$2/$1'));
}

WidgetDate.prototype.isValid = function() {
  return (this.getDate() ? true : false);
}

WidgetDate.prototype.getTime = function() {
  !this.timerHoursId && (this.timerHoursId = this.input.id + '_hours_' + new Date().getTime());
  !this.timerMinutesId && (this.timerMinutesId = this.input.id + '_minutes_' + new Date().getTime());
  !this.timerSecondsId && (this.timerSecondsId = this.input.id + '_seconds_' + new Date().getTime());
  if (!this.timerContent) {
    this.timerContent = ['Time: &nbsp;<input type="text" id="', this.timerHoursId, '" maxlength="2" style="width: 26px;" value="00" /> : ',
      '<input type="text" id="', this.timerMinutesId, '" maxlength="2" style="width: 26px;" value="00" /> : ',
      '<input type="text" id="', this.timerSecondsId, '" maxlength="2" style="width: 26px;" value="00" />'].join('');
  }
  var timerContent = document.createElement('div');
  timerContent.innerHTML = this.timerContent;
  timerContent.style.margin = '2px';
  this.getContainer().appendChild(timerContent);
  var date = this.getDate();
  if (date) {
    $(this.timerHoursId).value = date.getHours() >= 10 ? date.getHours() : '0' + date.getHours();
    $(this.timerMinutesId).value = date.getMinutes() >= 10 ? date.getMinutes() : '0' + date.getMinutes();
    $(this.timerSecondsId).value = date.getSeconds() >= 10 ? date.getSeconds() : '0' + date.getSeconds();
  }
  return true;
}

WidgetDate.prototype.getContainer = function() {
  if (!this.container) {
    this.container = document.createElement('div');
    this.container.id = this.input.id + '_container' + new Date().getTime();
    this.container.style.display = 'none';
    this.container.style.position = 'absolute';
    this.container.style.background = '#EEE';
    this.container.style.border = '1px solid #555';
    this.container.style.zIndex = getStyle(this.input, 'zIndex') || 7000;
    this.container.style.fontSize = '8pt';
    this.container.style.fontFamily = 'Tahoma';
    document.getElementsByTagName('body')[0].appendChild(this.container);
    this.setHidden(true);
  }
  this.container.style.top = getY(this.input) + parseInt((this.input.style.height || this.input.offsetHeight), 10) + 1 + 'px';
  this.container.style.left = getX(this.input) + 'px';
  return this.container;
}

WidgetDate.prototype.setDate = function(dateObj) {
  var date = dateObj.getDate();
  var month = dateObj.getMonth() + 1;
  var year = dateObj.getFullYear();
  var time = '';
  if (this.useTime) {
    time += ' ' +
      (dateObj.getHours() >= 10 ? dateObj.getHours() : '0' + dateObj.getHours()) + ':' +
      (dateObj.getMinutes() >= 10 ? dateObj.getMinutes() : '0' + dateObj.getMinutes()) + ':' +
      (dateObj.getSeconds() >= 10 ? dateObj.getSeconds() : '0' + dateObj.getSeconds());
  }
  this.setValue(this.dateFormat.replace('d', (date >= 10 ? date : '0' + date)).replace('m', (month >= 10 ? month : '0' + month)).replace('Y', year) + time);
  return this;
}

WidgetDate.prototype.setValue = function(value) {
  this.input.value = value;
  return this;
}

WidgetDate.prototype.getValue = function() {
  var date = this.getDate();
  return date ? date.getFullYear() + '-' + (date.getMonth() + 1 >= 10 ? date.getMonth() + 1 : '0' + (date.getMonth() + 1)) + '-' + (date.getDate() >= 10 ? date.getDate() : '0' + date.getDate()) + (this.useTime ? (' ' + (date.getHours() >= 10 ? date.getHours() : '0' + date.getHours()) + ':' + (date.getMinutes() >= 10 ? date.getMinutes() : '0' + date.getMinutes()) + ':' + (date.getSeconds() >= 10 ? date.getSeconds() : '0' + date.getSeconds()) ) : '') : false;
}

WidgetDate.prototype.toggleDisplayCalendar = function() {
  return (this.isHidden() ? this.show() : this.hide());
}

WidgetDate.prototype.show = function() {
  this.getContainer().style.display = '';
  this.setHidden(false);
  return this;
}

WidgetDate.prototype.hide = function() {
  this.getContainer().style.display = 'none';
  this.setHidden(true);
  return this;
}

WidgetDate.prototype.isHidden = function() {
  return this.hidden || false;
}

WidgetDate.prototype.setHidden = function(hidden) {
  this.hidden = hidden;
  return true;
}

WidgetDate.prototype.buildCalendar = function(date) {
  if (this.getDate() == date) {
    return true;
  }
  // TODO: remove setting style and replace with appropriate className
  date = date || new Date();
  var today = new Date();
  var valueDate = this.getDate();
  var month = this.month = date.getMonth();
  var year = this.year = date.getFullYear();
  var days = (new Date(year, month + 1, 0)).getDate();
  var thisObj = this;
  var nextMonth = this.input.id + '_next_month' + new Date().getTime();
  var nextYear = this.input.id + '_next_year' + new Date().getTime();
  var prevMonth = this.input.id + '_prev_month' + new Date().getTime();
  var prevYear = this.input.id + '_prev_year' + new Date().getTime();
  var tableId = this.input.id + '_calendar_table' + new Date().getTime();
  var tableContent = ['<table cellpadding="1" cellspacing="1" id="', tableId, '"><tr align="center" style="background: #C0C0C0; cursor: default;">',
    '<td><a href="#" id="', prevYear, '">&laquo;&laquo;</a></td>',
    '<td><a href="#" id="', prevMonth, '">&laquo;</a></td>',
    '<td colspan="3"><b>', this.monthes[this.month].substr(0, 3), ' ', this.year, '</b></td>',
    '<td><a href="#" id="', nextMonth, '">&raquo;</a></td>',
    '<td><a href="#" id="', nextYear, '">&raquo;&raquo;</a></td></tr></table>'].join('');
  this.getContainer().innerHTML = tableContent;
  table = document.getElementById(tableId);
  var row = table.insertRow(-1), td;
  for (var k = 0; k < this.daysOfWeek.length; k++) {
    var cellContent = ['<span style="font-weight: bold; font-family: Tahoma; font-size: 8pt;', (k == 5 || k == 6 ? ' color:  #DC143C;' : ''), '">', this.daysOfWeek[k], '</span>'].join('');
    (td = row.insertCell(-1)).innerHTML = cellContent;
    td.style.cursor = 'default';
    td.style.border = '1px solid #888';
    td.style.background = '#DDD';
    td.style.width = '18px';
  }
  var thisObj = this;
  for (var day = 1 - (((new Date(year, month, 1)).getDay() + 6) % 7); day <= days; day++) {
    row.cells.length == 7 && (row = table.insertRow(-1));
    (td = row.insertCell(-1)).innerHTML = day > 0 ? day : '&nbsp;';
    td.style.textAlign = 'right';
    td.style.border = '1px solid #888';
    td.style.cursor = 'default';
    if (day > 0) {
      td.title = day + ' ' + this.monthes[month].toLowerCase() + ' ' + year + ' г.';
      td.style.cursor = 'pointer';
      td.onclick = (function(x) {
        return function() {
          if (thisObj.useTime &&
            parseInt($(thisObj.timerHoursId).value) >= 0 && parseInt($(thisObj.timerHoursId).value) <= 23 &&
            parseInt($(thisObj.timerMinutesId).value) >= 0 && parseInt($(thisObj.timerMinutesId).value) <= 59 &&
            parseInt($(thisObj.timerSecondsId).value) >= 0 && parseInt($(thisObj.timerSecondsId).value) <= 59
          ) {
            thisObj.setDate(new Date(year, month, x, parseInt($(thisObj.timerHoursId).value), parseInt($(thisObj.timerMinutesId).value), parseInt($(thisObj.timerSecondsId).value)));
          } else {
            thisObj.setDate(new Date(year, month, x));
          }
          thisObj.hide();
          return true;
        }
      })(day);
      today.getDate() == day && today.getMonth() == month && today.getFullYear() == year && (td.style.background = '#9ACD32') && (td.style.fontWeight = 'bold');
      valueDate && valueDate.getDate() == day && valueDate.getMonth() == month && valueDate.getFullYear() == year && (td.style.background = '#B0C4DE');
      (row.cells.length == 6 || row.cells.length == 7) && (td.style.color = '#DC143C');
    }
  }
  while (row.cells.length < 7) {
    (td = row.insertCell(-1)).innerHTML = '&nbsp;';
    td.style.border = '1px solid #888';
    td.style.cursor = 'default';
  }

  $(nextMonth).onclick = function() {
    thisObj.buildCalendar(new Date(thisObj.year, thisObj.month + 1, 1));
    return false;
  };
  $(nextYear).onclick = function() {
    thisObj.buildCalendar(new Date(thisObj.year + 1, thisObj.month, 1));
    return false;
  };
  $(prevYear).onclick = function() {
    thisObj.buildCalendar(new Date(thisObj.year - 1, thisObj.month, 1));
    return false;
  };
  $(prevMonth).onclick = function() {
    thisObj.buildCalendar(new Date(thisObj.year, thisObj.month - 1, 1));
    return false;
  };

  if (thisObj.useTime) {
    thisObj.getTime();
  }

  return this;
}

