/*
 * Tagクラス
 *
 *--------------------------------------------------------------------- */
var Tag = Class.create();

Tag.TAG_ID_PREFIX = 'tag_';
Tag.TAG_HANDLER_ID_SUFFIX = '_handler';
Tag.TAG_RESIZER_ID_SUFFIX = '_resizer';

Tag.prototype = {

  /*
   * Instance properties
   *
   *--------------------------------------------------------------------- */
  element: null,
  tagId: null,
  tagName: null,
  tagType: null,
  startDate: null,
  period: null,
  traderId: null,
  comment: null,
  isNew: false,

  /*
   * Constructor
   *
   *--------------------------------------------------------------------- */
  initialize: function(option, isNew) {

    /*
     * 与えられたオプションからタグの生成に必須となる値を取得する。
     */
    var tagId = option.tagId;
    var tagName = option.tagName;
    var tagType = option.tagType;
    var date = option.startDate;
    var startDate = new Date(date.year, date.month - 1, date.day);
    var period = option.period;
    var traderId = option.traderId;
    var comment = option.comment;

    /*
     * タグを構成する要素のIDを設定する。
     */
    var decorateTagId = Tag.TAG_ID_PREFIX + tagId;
    var handlerId = decorateTagId + Tag.TAG_HANDLER_ID_SUFFIX;
    var resizerId = decorateTagId + Tag.TAG_RESIZER_ID_SUFFIX;

    /*
     * タグを構成する要素を生成する。
     */
    var handler = Builder.node('div', {id: handlerId, className: 'handler'});
    var resizer = Builder.node('div', {id: resizerId, className: 'resizer'});
    var tag = Builder.node('div', {id: decorateTagId, className: 'tag'}, [handler, resizer]);

    /*
     * "タグ強調表示"イベントを定義し, 付与する。
     */
    var activateListener = function(event) {
      ProgressManager.getInstance().activateTagAndLinker(this);
    }.bind(this);

    var inactivateListener = function(event) {
      ProgressManager.getInstance().inactivateTagAndLinker(this);
    }.bind(this);

    var relocateListener = function(event) {
      TagDisplay.getInstance().locatePlain(Event.pointerX(event), Event.pointerY(event));
    }.bind(this);

    Event.observe(tag, 'mouseover', activateListener);
    Event.observe(tag, 'mouseout', inactivateListener);
    Event.observe(tag, 'mousemove', relocateListener);

    /*
     * "タグメニュー表示"イベントを付与する。
     */
    Event.observe(tag, 'contextmenu', function(event) {
      this.showMenu(Event.pointerX(event), Event.pointerY(event));
      Event.stop(event);
    }.bind(this));

    /*
     * タグのドラッグを有効にする。
     */
    var draggableOption = {
      handle: handler,
      constraint: 'horizontal',

      starteffect: function(dragElement) {

        /*
         * "タグ強調表示"イベントを解除する。
         */
        Event.stopObserving(tag, 'mouseover', activateListener);
        Event.stopObserving(tag, 'mouseout', inactivateListener);
        Event.stopObserving(tag, 'mousemove', relocateListener);

        /*
         * タグに関わるリンカを消去し, タグとリンカを非アクティヴにする。
         */
        var pm = ProgressManager.getInstance();

        pm.clearLinker(this);
        pm.inactivateTagAndLinker(this);

        /*
         * ドラッグ中のスタイルを設定する。
         */
        dragElement.setStyle({opacity: 0.5, zIndex: 1100});

      }.bind(this),

      snap: function(x, y) {
        return [(x - x % 40 + 4), y];
      },

      endeffect: function(dragElement) {

        /*
         * "タグ強調表示"イベントを付与する。
         */
        Event.observe(tag, 'mouseover', activateListener);
        Event.observe(tag, 'mouseout', inactivateListener);
        Event.observe(tag, 'mousemove', relocateListener);

        /*
         * タグに関わるリンカを描画する。
         */
        var pm = ProgressManager.getInstance();

        pm.drawLinker(this);

        /*
         * スタイルを元に戻す。
         */
        dragElement.setStyle({opacity: 1, zIndex: 1000});

        /*
         * 開始日を設定する。
         */
        var baseDate = pm.getCalendarStartDate();
        var period = Math.floor(Element.getLeft(dragElement) / 40);

        this.setStartDate(baseDate.shift(period));

      }.bind(this)
    };

    new Draggable(tag, draggableOption);

    /*
     * リンカ追加時のドロップ先を設定する。
     */
    Droppables.add(tag, {
      accept: 'linker_adder',
      hoverclass: 'active_tag',

      onDrop: function() {

        /*
         * リンカを追加し, 終端タグに関わるリンカを描画しなおす。
         * またタグメニューを非表示にする。
         */
        var tm = TagMenu.getInstance();
        var pm = ProgressManager.getInstance();

        pm.addLinker(tm.getTag().getTagId(), this.getTagId());
        pm.drawLinker(this);
        tm.hide();

      }.bind(this)
    });

    /*
     * リンカ削除時のドロップ先を設定する。
     */
    Droppables.add(tag, {
      accept: 'linker_remover',
      hoverclass: 'active_tag',

      onDrop: function(dragElement, dropElement) {

        /*
         * 始端タグIDおよび終端タグIDを取得し, リンカを削除する。
         * またタグメニューを非表示にする。
         */
        var tm = TagMenu.getInstance();

        ProgressManager.getInstance().removeLinker(tm.getTag().getTagId(), this.getTagId());
        tm.hide();

      }.bind(this)
    });

    /*
     * "タグ終端位置変更開始"イベントを付与する。
     */
    Event.observe(resizer, 'mousedown', function(event) {

      /*
       * タグに関わるリンカを消去し, タグとリンカを非アクティヴにする。
       */
      var pm = ProgressManager.getInstance();

      pm.clearLinker(this);
      pm.inactivateTagAndLinker(this);

      /*
       * "タグ強調表示"イベントを解除する。
       */
      Event.stopObserving(tag, 'mouseover', activateListener);
      Event.stopObserving(tag, 'mouseout', inactivateListener);
      Event.stopObserving(tag, 'mousemove', relocateListener);

      /*
       * "タグ終端位置変更中"イベントを付与する。
       */
      var baseWidth = Element.getWidth(tag) - Event.pointerX(event);

      document.onmousemove = function(event) { // Event.observeだとちょっと都合が悪い

        /*
         * タグの幅を計算し, 設定する。
         * また幅から期間を計算し, 期間の設定も行う。
         */
        var floor = Math.floor;
        var width = floor((baseWidth + Event.pointerX(event)) / 40 + 0.5) * 40 - 9;

        if (width < 31) {
          width = 31;
        }

        tag.style.width = width + 'px';
        handler.style.width = (width - 18) + 'px';
        this.setPeriod(floor((width + 9) / 40));

      }.bindAsEventListener(this)

      /*
       * "タグ終端位置変更終了"イベントを付与する。
       */
      document.onmouseup = function(event) {

        /*
         * "タグ強調表示"イベントを付与する。
         */
        Event.observe(tag, 'mouseover', activateListener);
        Event.observe(tag, 'mouseout', inactivateListener);
        Event.observe(tag, 'mousemove', relocateListener);

        /*
         * "タグ終端位置変更中"イベントおよび"タグ終端位置変更終了"イベントを解除する。
         */
        document.onmousemove = document.onmouseup = null;

        /*
         * タグに関わるリンカを描画する。
         */
        ProgressManager.getInstance().drawLinker(this);

      }.bindAsEventListener(this)

    }.bind(this));

    /*
     * プロパティを設定し, 反映させる。
     */
    this.element = tag;
    this.tagId = tagId;
    this.tagName = tagName;
    this.tagType = tagType;
    this.startDate = startDate;
    this.period = period;
    this.traderId = traderId;
    this.comment = comment;
    this.isNew = (typeof isNew == 'undefined') ? false : isNew;
    this.reflect();

  },

  /*
   * getElement
   *
   *--------------------------------------------------------------------- */
  getElement: function() {
    return this.element;
  },

  /*
   * getTagId
   *
   *--------------------------------------------------------------------- */
  getTagId: function() {
    return this.tagId;
  },

  /*
   * setTagName
   *
   *--------------------------------------------------------------------- */
  setTagName: function(tagName) {
    this.tagName = tagName;
  },

  /*
   * getTagName
   *
   *--------------------------------------------------------------------- */
  getTagName: function() {
    return this.tagName;
  },

  /*
   * getStartDate
   *
   *--------------------------------------------------------------------- */
  getStartDate: function() {
    return this.startDate;
  },

  /*
   * setStartDate
   *
   *--------------------------------------------------------------------- */
  setStartDate: function(date) {
    this.startDate = date;
  },

  /*
   * getEndDate
   *
   *--------------------------------------------------------------------- */
  getEndDate: function() {
    return this.startDate.shift(this.period - 1);
  },

  /*
   * setEndDate
   *
   *--------------------------------------------------------------------- */
  setEndDate: function(date) {
    this.period = this.startDate.between(date) + 1;
  },

  /*
   * getPeriod
   *
   *--------------------------------------------------------------------- */
  getPeriod: function() {
    return this.period;
  },

  /*
   * setPeriod
   *
   *--------------------------------------------------------------------- */
  setPeriod: function(period) {
    this.period = period;
  },

  /*
   * getTraderId
   *
   *--------------------------------------------------------------------- */
  getTraderId: function() {
    return this.traderId;
  },

  /*
   * setTraderId
   *
   *--------------------------------------------------------------------- */
  setTraderId: function(traderId) {
    this.traderId = traderId;
  },

  /*
   * getComment
   *
   *--------------------------------------------------------------------- */
  getComment: function() {
    return this.comment;
  },

  /*
   * setComment
   *
   *--------------------------------------------------------------------- */
  setComment: function(comment) {
    this.comment = comment;
  },

  /*
   * reflect
   *
   *--------------------------------------------------------------------- */
  reflect: function() {
    this.element.getElementsByClassName('handler')[0].innerHTML = this.tagName;

    var typeClass;

    switch (this.tagType) {
      case 1:
        typeClass = 'drawing';
        break;
      case 2:
        typeClass = 'parts';
        break;
      case 3:
        typeClass = 'mounting';
        break;
    }

    this.element.addClassName(typeClass);
  },

  /*
   * activate
   *
   *--------------------------------------------------------------------- */
  activate: function() {
    this.element.addClassName('active_tag');
  },

  /*
   * inactivate
   *
   *--------------------------------------------------------------------- */
  inactivate: function() {
    this.element.removeClassName('active_tag');
  },

  /**
   * showMenu
   *
   */
  showMenu: function(x, y) {
    TagMenu.getInstance().locateAndShow(x, y, this);
  },

  /**
   * toJson
   *
   */
  toJson: function() {
    var date = this.startDate;

    return {
      id: this.tagId,
      type: this.tagType,
      name: this.tagName,
      start_date: date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(),
      period: this.period,
      trader_id: this.traderId,
      comment: this.comment,
      is_new: (this.isNew ? 1 : 0)
    };
  }

};

