function productInfoCtrl(winWr) {
    this.winWr = typeof(winWr) != "undefined" ? winWr : _wrapper;
}
implement(productInfoCtrl.prototype, [{
  oOptWr : {},
  oModelWr : null,
  bIsModel : false,
  spanWr : {},
  nProdPrice : 0,
  nPaymentsMmiAmount : 0,
  ini : function (oRelatedGallery, oOptions, oSpan, winWr)
  {
    this.oRelatedGallery = oRelatedGallery;
    this.oOptions = oOptions;
    this.oSpan = oSpan;
    this.winWr = typeof(winWr) != "undefined" ? winWr : _wrapper;
    this.frmWr = this.winWr.getForm(this.config.frmName);
    this.voile = this.winWr.getElement(this.config.voileId);
    
  },
  preset : function ()
  {
    this.subscribeElm();
    this.subscribeImg();
    this.subscribePrice();
    if (this.bIsModel) {
      this.changeModel();
    } else {
      this.changeOption();
    }
  },
  subscribePrice : function ()
  {
    var aTags, i, iSpan;
    for (var divId in this.oSpan) {
      if (!this.winWr.checkElement(divId)) {
        continue;
      }
      iSpan = 0;
      this.spanWr[divId] = {};
      aTags = this.winWr.getElement(divId).elm.getElementsByTagName('SPAN');
      for (i = 0; i < aTags.length; i++) {
        if (!aTags[i].getElementsByTagName("*").length) {
          this.spanWr[divId][this.oSpan[divId][iSpan]] = this.winWr.getElmWrapper(aTags[i]);
          iSpan++;
        }
      }
    }
    this.preparePriceData();
  },
  subscribeElm : function ()
  {
    var aRes, i, oElmWr, oLsnr;
    var oReg = new RegExp("^([^\\d]+)(\\[(\\d+)\\])?$", "");
    oReg.compile("^([^\\d]+)(\\[(\\d+)\\])?$" , "");

    var aElm = this.frmWr.elm.elements;
    for (i = 0; i < aElm.length; i++) {
      aRes = oReg.exec(aElm[i].name);
      if (aRes == null || !aRes[1]) {
        continue;
      }
      switch (aRes[1]) {
        case 'gallery_id':
          this.bIsModel = true;
          this.oModelWr = this.winWr.getElmWrapper(aElm[i]);
          this.oModelWr.addListener(this, "onclick", "changeModel");
          break;
        case 'id':
          if (aElm[i].type == "hidden") {
            break;
          }
          this.oOptWr[aRes[3]] = this.winWr.getElmWrapper(aElm[i]);
          aElParams = aRes[3] + ':' + aElm[i].value;
          this.oOptWr[aRes[3]].addListener(this, (aElm[i].type == "radio" ? "onclick" : "onchange"), "changeOption").aElParams = aElParams;
          break;
        case 'model_id':
          this.oOptWr[aRes[3]] = this.winWr.getElmWrapper(aElm[i]);
          break;
      }
    }
  },
  subscribeImg : function ()
  {
    var oReg = new RegExp("^[^\\d]+_(\\d+)", "");
    oReg.compile("^[^\\d]+_(\\d+)", "");
    this._subscribeImg(this.config.thumbsId, oReg);
  },
  _subscribeImg : function (divContentId, oReg)
  {
    var aRes, i;
    var aTags = this.winWr.getElement(divContentId).elm.getElementsByTagName('IMG');
    for (i = 0; i < aTags.length; i++) {
      if (!aTags[i].id) {
        continue;
      }
      aRes = oReg.exec(aTags[i].id);
      if (aRes == null && !aRes[1]) {
        continue;
      }
      this.winWr.getElmWrapper(aTags[i]).addListener(this, "onclick", "changeImage").gallery_id = aRes[1];
    }
  },
  preparePriceData : function ()
  {
    if (typeof(Math[option_price['rounding_rule']]) != "function") {
      option_price['rounding_rule'] = 'round';
    }
    if (typeof(Math[option_price['mls_rounding_rule']]) != "function") {
      option_price['mls_rounding_rule'] = 'round';
    }
    this.nProdPrice = option_price['prices']['product_price']['special_price'] ? option_price['prices']['product_price']['special_price'] : option_price['prices']['product_price']['base_price'];
    this.nProdPrice = isFinite(parseFloat(this.nProdPrice)) ? parseFloat(this.nProdPrice) : 0;
    this.nPaymentsMmiAmount = isFinite(parseFloat(option_price['prices']['rest_amount']['payments_mmi_amount'])) ? parseFloat(option_price['prices']['rest_amount']['payments_mmi_amount']) : 0;
  },
  changeImage : function (evtWr, par)
  {
    this._setOptions(par.gallery_id);
    this._setPrice();
    this._swapImg(par.gallery_id);
  },
  changeModel : function (evtWr)
  {
    this._setOptions(this.oModelWr.elm.value);
    this._setPrice();
    this._swapImg(this.oModelWr.elm.value);
  },
  changeOption : function (evtWr, par)
  {
    this._setPrice();

    if (typeof(par) != "undefined") {
      aParams = par.aElParams.split(':');
      option_id = aParams[0];
      option_value = aParams[1];
      if (this.oOptWr[option_id].elm.type == "radio") {
        attributeChange(option_id, option_value);
      } else {
        attributeChange(option_id, -1);
      }
    }

    //get relation attributes array
    var aRel = [];
    if (typeof(par) != "undefined" && this.oOptWr[option_id].elm.type == "radio") {
      aRel[option_id] = option_value;
    } else {
      for (var i in this.oOptions) {
        var sTempOpt = this.oOptions[i];
        var sTempVal = this.oOptWr[sTempOpt].elm.value;
        if (sTempVal != 0) {
          aRel[sTempOpt] = sTempVal;
        }
      }
    }
    //End: get relation attributes array

    //change image if related galleries options match with selected options
    for (var sOptKey in this.oRelatedGallery) {
      sTempOptKey = sOptKey.split('|');
      if (sTempOptKey[0] != '') {
        //get options and values arrays from related gallery
        var aOptionIds = new Array();
        var aValueIds = new Array();

        if (sTempOptKey[0].indexOf(':') > -1) {
          aOptionIds = sTempOptKey[0].split(':');
          aValueIds = sTempOptKey[1].split(':');
        } else {
          aOptionIds[0] = sTempOptKey[0];
          aValueIds[0] = sTempOptKey[1];
        }
        //End: get options and values arrays from related gallery

        //check for options match
        if (aOptionIds.length > 0) {
          var bMatch = true;
          for (var key in aOptionIds) {
            if (typeof(aRel[aOptionIds[key]]) != "undefined" && aRel[aOptionIds[key]] != aValueIds[key]) { //option value not match
              bMatch = false;
              break;
            }
          }
          if (bMatch && (typeof(pHiddenImages) == "undefined" || typeof(pHiddenImages[this.oRelatedGallery[sOptKey]]) == "undefined")) {
            this._swapImg(this.oRelatedGallery[sOptKey]);
            break;
          }
        }
        //End: check for options match
      }
    }
    //End: change image if related galleries options match with selected options

//    if (typeof(this.oRelatedGallery[sSelect]) == "undefined") {
//      return;
//    }
    //this._swapImg(this.oRelatedGallery[sSelect]);
  },
  _setOptions : function (iGalleryId)
  {
    if (this.bIsModel) {
      this.oModelWr.elm.value = iGalleryId;
    }
    if (typeof(attr_related_images[iGalleryId].attrs) == "undefined") {
      return;
    }
    var oAttr = attr_related_images[iGalleryId].attrs;
    for (var iOptId in this.oOptWr) {
      if (typeof(oAttr[iOptId]) != "undefined") {
        element = document.getElementById('opt_id_' + iOptId);
        if (element) {
          element.value = oAttr[iOptId];
        } else {
          element = document.getElementById('opt_id_' + iOptId + '_val_' + oAttr[iOptId]);
          if (element) {
            element.checked = "checked";
          }
        }
        //this.oOptWr[iOptId].elm.value = oAttr[iOptId];
      }
    }
  },
  _swapImg : function (iGalleryId)
  {
    if (typeof(attr_related_images[iGalleryId]) == "undefined") {
      return;
    }
    swap_img(attr_related_images[iGalleryId]['image'], attr_related_images[iGalleryId]['big_image_src'], attr_related_images[iGalleryId]['big_image_width'], attr_related_images[iGalleryId]['big_image_height']);
  },
  _setPrice : function ()
  {
    this._writePrice(this._getOptionPrice());
  },
  _getOptionPrice : function ()
  {
    var nOptPrice = 0;
    for (var iOptId in this.oOptWr) {
      if (typeof(option_price['data'][iOptId]) == "undefined" || typeof(option_price['data'][iOptId][this.oOptWr[iOptId].elm.value]) == "undefined") {
        continue;
      }
      if (radioButtons.length > 0) {
        for (var i = 0; i < radioButtons.length; i++) {
          if (radioButtons[i].checked == true) {
            nOptPrice += parseFloat(option_price['data'][iOptId][radioButtons[i].value]);
          }
        }
      }
      else {
        nOptPrice += parseFloat(option_price['data'][iOptId][this.oOptWr[iOptId].elm.value]);
      }
    }
    return nOptPrice;
  },
  _writePrice : function (nOptPrice)
  {
    var sFunc = '';
    for (var sDivId in this.spanWr) {
      for (var sPriceType in this.spanWr[sDivId]) {
        sFunc = 'calculate_' + sDivId + "_" + sPriceType;
        if (this[sFunc]) {
          this.spanWr[sDivId][sPriceType].write(this[sFunc](nOptPrice));
        }
      }
    }
  },
  _formatPrice : function (iPrice, bIsMiles)
  {
    var sEndPost, sBeginPos;
    var sSepTh = bIsMiles ? option_price['thousands_point']['MLS'] : option_price['thousands_point']['curr_currency'];
    var sSepDe = bIsMiles ? option_price['decimal_point']['MLS'] : option_price['decimal_point']['curr_currency'];
    var aPart = iPrice.toString().split('.');
    var sResult = typeof(aPart[1]) != "undefined" ? sSepDe + aPart[1] : '';
    var iLen = aPart[0].length;
    for (var iCurPos = 0; iCurPos < iLen; iCurPos += 3) {
      sEndPost = iLen - iCurPos;
      sBeginPos = sEndPost < 3 ? 0 : sEndPost - 3;
      sResult = (sBeginPos ? sSepTh : '') + aPart[0].substring(sBeginPos, sEndPost) + sResult;
    }
    return sResult;
  },
  _calculatePrice : function (iPrice)
  {
    iPrice = Math[option_price['rounding_rule']](iPrice * option_price['curr_currency'] / option_price['precision']) * option_price['precision'];
    return this._formatPrice(iPrice.toFixed((option_price['curr_currency'] == option_price['mls_rate'] ? 0 : 2)), option_price['curr_currency'] == option_price['mls_rate']);
  },
  calculate_product_price_expired : function (nOptPrice)
  {
    return this._calculatePrice(parseFloat(option_price['addition']['expired_price']) + nOptPrice);
  },
  calculate_product_price_price : function (nOptPrice)
  {
    return this._calculatePrice(this.nProdPrice + nOptPrice);
  },
  calculate_product_price_mls : function (nOptPrice)
  {
    return this._formatPrice(Math[option_price['mls_rounding_rule']]((this.nProdPrice + nOptPrice) * parseFloat(option_price['mls_rate'])), true);
  },
  calculate_spend_get_miles_mls : function (nOptPrice)
  {
    return this._formatPrice(Math[option_price['mls_rounding_rule']]((this.nProdPrice + nOptPrice) * parseFloat(option_price['mls_rate'])), true);
  },
  calculate_spend_get_miles_accrual : function (nOptPrice)
  {
    return Math.floor((this.nProdPrice + nOptPrice) * parseFloat(option_price['default_currency']));
  },
  calculate_rest_amount_price : function (nOptPrice)
  {
    var iPrice = this.nProdPrice + nOptPrice - this.nPaymentsMmiAmount;
    iPrice = (iPrice > 0) ? iPrice : 0;
    return this._calculatePrice(iPrice);
  },
  calculate_rest_amount_mls : function (nOptPrice)
  {
    var iPrice = this.nProdPrice + nOptPrice - this.nPaymentsMmiAmount;
    iPrice = (iPrice > 0) ? iPrice : 0;
    return this._formatPrice(Math[option_price['mls_rounding_rule']](iPrice * parseFloat(option_price['mls_rate'])), true);
  },
  config : {
    frmName : "cart_quantity",
    voileId : "voile",
    thumbsId : "div_img_thumbs",
    loadDivId : "ajax_loading_div"
  }
}
]);
