text: import * as utils from '../src/utils';
  import { BANNER, VIDEO } from '../src/mediaTypes';
  import { config } from '../src/config';
  import find from 'core-js/library/fn/array/find';
  import isInteger from 'core-js/library/fn/number/is-integer';
  import { registerBidder } from '../src/adapters/bidderFactory';
  
  const BIDDER_CODE = 'ix';
  const SECURE_BID_URL = 'https://as-sec.casalemedia.com/cygnus';
  const SUPPORTED_AD_TYPES = [BANNER, VIDEO];
  const BANNER_ENDPOINT_VERSION = 7.2;
  const VIDEO_ENDPOINT_VERSION = 8.1;
  const CENT_TO_DOLLAR_FACTOR = 100;
  const BANNER_TIME_TO_LIVE = 35;
  const VIDEO_TIME_TO_LIVE = 3600; // 1hr
  const NET_REVENUE = true;
  const PRICE_TO_DOLLAR_FACTOR = {
    JPY: 1
  };
  
  /**
   * Transform valid bid request config object to banner impression object that will be sent to ad server.
   *
   * @param  {object} bid A valid bid request config object.
   * @return {object}     A impression object that will be sent to ad server.
   */
  function bidToBannerImp(bid) {
    const imp = bidToImp(bid);
  
    imp.banner = {};
    imp.banner.w = bid.params.size[0];
    imp.banner.h = bid.params.size[1];
    imp.banner.topframe = utils.inIframe() ? 0 : 1;
  
    return imp;
  }
  
  /**
   * Transform valid bid request config object to video impression object that will be sent to ad server.
   *
   * @param  {object} bid A valid bid request config object.
   * @return {object}     A impression object that will be sent to ad server.
   */
  function bidToVideoImp(bid) {
    const imp = bidToImp(bid);
  
    imp.video = utils.deepClone(bid.params.video)
    imp.video.w = bid.params.size[0];
    imp.video.h = bid.params.size[1];
  
    const context = utils.deepAccess(bid, 'mediaTypes.video.context');
    if (context) {
      if (context === 'instream') {
        imp.video.placement = 1;
      } else if (context === 'outstream') {
        imp.video.placement = 4;
      } else {
        utils.logWarn(`ix bidder params: video context '${context}' is not supported`);
      }
    }
  
    return imp;
  }
  
  function bidToImp(bid) {
    const imp = {};
  
    imp.id = bid.bidId;
  
    imp.ext = {};
    imp.ext.siteID = bid.params.siteId;
  
    if (bid.params.hasOwnProperty('id') &&
      (typeof bid.params.id === 'string' || typeof bid.params.id === 'number')) {
      imp.ext.sid = String(bid.params.id);
    } else {
      imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`;
    }
  
    if (bid.params.hasOwnProperty('bidFloor') && bid.params.hasOwnProperty('bidFloorCur')) {
      imp.bidfloor = bid.params.bidFloor;
      imp.bidfloorcur = bid.params.bidFloorCur;
    }
  
    return imp;
  }
  
  /**
   * Parses a raw bid for the relevant information.
   *
   * @param  {object} rawBid   The bid to be parsed.
   * @param  {string} currency Global currency in bid response.
   * @return {object} bid      The parsed bid.
   */
  function parseBid(rawBid, currency, bidRequest) {
    const bid = {};
  
    if (PRICE_TO_DOLLAR_FACTOR.hasOwnProperty(currency)) {
      bid.cpm = rawBid.price / PRICE_TO_DOLLAR_FACTOR[currency];
    } else {
      bid.cpm = rawBid.price / CENT_TO_DOLLAR_FACTOR;
    }
  
    bid.requestId = rawBid.impid;
  
    bid.dealId = utils.deepAccess(rawBid, 'ext.dealid');
    bid.netRevenue = NET_REVENUE;
    bid.currency = currency;
    bid.creativeId = rawBid.hasOwnProperty('crid') ? rawBid.crid : '-';
  
    // in the event of a video
    if (utils.deepAccess(rawBid, 'ext.vasturl')) {
      bid.vastUrl = rawBid.ext.vasturl
      bid.width = bidRequest.video.w;
      bid.height = bidRequest.video.h;
      bid.mediaType = VIDEO;
      bid.ttl = VIDEO_TIME_TO_LIVE;
    } else {
      bid.ad = rawBid.adm;
      bid.width = rawBid.w;
      bid.height = rawBid.h;
      bid.mediaType = BANNER;
      bid.ttl = BANNER_TIME_TO_LIVE;
    }
  
    bid.meta = {};
    bid.meta.networkId = utils.deepAccess(rawBid, 'ext.dspid');
    bid.meta.brandId = utils.deepAccess(rawBid, 'ext.advbrandid');
    bid.meta.brandName = utils.deepAccess(rawBid, 'ext.advbrand');
  
    return bid;
  }
  
  /**
   * Determines whether or not the given object is valid size format.
   *
   * @param  {*}       size The object to be validated.
   * @return {boolean}      True if this is a valid size format, and false otherwise.
   */
  function isValidSize(size) {
    return Array.isArray(size) && size.length === 2 && isInteger(size[0]) && isInteger(size[1]);
  }
  
  /**
   * Determines whether or not the given size object is an element of the size
   * array.
   *
   * @param  {array}  sizeArray The size array.
   * @param  {object} size      The size object.
   * @return {boolean}          True if the size object is an element of the size array, and false
   *                            otherwise.
   */
  function includesSize(sizeArray, size) {
    if (isValidSize(sizeArray)) {
      return sizeArray[0] === size[0] && sizeArray[1] === size[1];
    }
  
    for (let i = 0; i < sizeArray.length; i++) {
      if (sizeArray[i][0] === size[0] && sizeArray[i][1] === size[1]) {
        return true;
      }
    }
  
    return false;
  }
  
  /**
   * Determines whether or not the given bidFloor parameters are valid.
   *
   * @param  {*}       bidFloor    The bidFloor parameter inside bid request config.
   * @param  {*}       bidFloorCur The bidFloorCur parameter inside bid request config.
   * @return {boolean}             True if this is a valid biFfloor parameters format, and false
   *                               otherwise.
   */
  function isValidBidFloorParams(bidFloor, bidFloorCur) {
    const curRegex = /^[A-Z]{3}$/;
  
    return Boolean(typeof bidFloor === 'number' && typeof bidFloorCur === 'string' &&
      bidFloorCur.match(curRegex));
  }
  
  /**
   * Finds the impression with the associated id.
   *
   * @param  {*}      id          Id of the impression.
   * @param  {array}  impressions List of impressions sent in the request.
   * @return {object}             The impression with the associated id.
   */
  function getBidRequest(id, impressions) {
    if (!id) {
      return;
    }
    return find(impressions, imp => imp.id === id);
  }
  
  /**
   * Builds a request object to be sent to the ad server based on bid requests.
   *
   * @param  {array}  validBidRequests A list of valid bid request config objects.
   * @param  {object} bidderRequest    An object containing other info like gdprConsent.
   * @param  {array}  impressions      List of impression objects describing the bids.
   * @param  {array}  version          Endpoint version denoting banner or video.
   * @return {object}                  Info describing the request to the server.
   *
   */
  function buildRequest(validBidRequests, bidderRequest, impressions, version) {
    const userEids = [];
  
    // Always use secure HTTPS protocol.
    let baseUrl = SECURE_BID_URL;
  
    // RTI ids will be included in the bid request if the function getIdentityInfo() is loaded
    // and if the data for the partner exist
    if (window.headertag && typeof window.headertag.getIdentityInfo === 'function') {
      let identityInfo = window.headertag.getIdentityInfo();
      if (identityInfo && typeof identityInfo === 'object') {
        for (const partnerName in identityInfo) {
          if (identityInfo.hasOwnProperty(partnerName)) {
            let response = identityInfo[partnerName];
            if (!response.responsePending && response.data && typeof response.data === 'object' && Object.keys(response.data).length) {
              userEids.push(response.data);
            }
          }
        }
      }
    }
    const r = {};
  
    // Since bidderRequestId are the same for different bid request, just use the first one.
    r.id = validBidRequests[0].bidderRequestId;
  
    r.imp = impressions;
  
    r.site = {};
    r.ext = {};
    r.ext.source = 'prebid';
  
    // if an schain is provided, send it along
    if (validBidRequests[0].schain) {
      r.source = {
        ext: {
          schain: validBidRequests[0].schain
        }
      };
    }
  
    if (userEids.length > 0) {
      r.user = {};
      r.user.eids = userEids;
    }
  
    if (document.referrer && document.referrer !== '') {
      r.site.ref = document.referrer;
    }
  
    // Apply GDPR information to the request if GDPR is enabled.
    if (bidderRequest) {
      if (bidderRequest.gdprConsent) {
        const gdprConsent = bidderRequest.gdprConsent;
  
        if (gdprConsent.hasOwnProperty('gdprApplies')) {
          r.regs = {
            ext: {
              gdpr: gdprConsent.gdprApplies ? 1 : 0
            }
          };
        }
  
        if (gdprConsent.hasOwnProperty('consentString')) {
          r.user = r.user || {};
          r.user.ext = {
            consent: gdprConsent.consentString || ''
          };
        }
      }
  
      if (bidderRequest.uspConsent) {
        utils.deepSetValue(r, 'regs.ext.us_privacy', bidderRequest.uspConsent);
      }
  
      if (bidderRequest.refererInfo) {
        r.site.page = bidderRequest.refererInfo.referer;
      }
    }
  
    const payload = {};
  
    // Parse additional runtime configs.
    const otherIxConfig = config.getConfig('ix');
    if (otherIxConfig) {
      // Append firstPartyData to r.site.page if firstPartyData exists.
      if (typeof otherIxConfig.firstPartyData === 'object') {
        const firstPartyData = otherIxConfig.firstPartyData;
        let firstPartyString = '?';
        for (const key in firstPartyData) {
          if (firstPartyData.hasOwnProperty(key)) {
            firstPartyString += `${encodeURIComponent(key)}=${encodeURIComponent(firstPartyData[key])}&`;
          }
        }
        firstPartyString = firstPartyString.slice(0, -1);
  
        r.site.page += firstPartyString;
      }
  
      // Create t in payload if timeout is configured.
      if (typeof otherIxConfig.timeout === 'number') {
        payload.t = otherIxConfig.timeout;
      }
    }
  
    // Use the siteId in the first bid request as the main siteId.
    payload.s = validBidRequests[0].params.siteId;
    payload.v = version;
    payload.r = JSON.stringify(r);
    payload.ac = 'j';
    payload.sd = 1;
    if (version === VIDEO_ENDPOINT_VERSION) {
      payload.nf = 1;
    }
  
    return {
      method: 'GET',
      url: baseUrl,
      data: payload
    };
  }
  
  export const spec = {
  
    code: BIDDER_CODE,
    supportedMediaTypes: SUPPORTED_AD_TYPES,
  
    /**
     * Determines whether or not the given bid request is valid.
     *
     * @param  {object}  bid The bid to validate.
     * @return {boolean}     True if this is a valid bid, and false otherwise.
     */
    isBidRequestValid: function (bid) {
      if (!isValidSize(bid.params.size)) {
        utils.logError('ix bidder params: bid size has invalid format.');
        return false;
      }
  
      if (!includesSize(bid.sizes, bid.params.size)) {
        utils.logError('ix bidder params: bid size is not included in ad unit sizes.');
        return false;
      }
  
      if (bid.hasOwnProperty('mediaType') && !(utils.contains(SUPPORTED_AD_TYPES, bid.mediaType))) {
        return false;
      }
  
      if (bid.hasOwnProperty('mediaTypes') && !(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || utils.deepAccess(bid, 'mediaTypes.video.playerSize'))) {
        return false;
      }
  
      if (typeof bid.params.siteId !== 'string' && typeof bid.params.siteId !== 'number') {
        utils.logError('ix bidder params: siteId must be string or number value.');
        return false;
      }
  
      const hasBidFloor = bid.params.hasOwnProperty('bidFloor');
      const hasBidFloorCur = bid.params.hasOwnProperty('bidFloorCur');
  
      if (hasBidFloor || hasBidFloorCur) {
        if (!(hasBidFloor && hasBidFloorCur && isValidBidFloorParams(bid.params.bidFloor, bid.params.bidFloorCur))) {
          utils.logError('ix bidder params: bidFloor / bidFloorCur parameter has invalid format.');
          return false;
        }
      }
  
      return true;
    },
  
    /**
     * Make a server request from the list of BidRequests.
     *
     * @param  {array}  validBidRequests A list of valid bid request config objects.
     * @param  {object} bidderRequest    A object contains bids and other info like gdprConsent.
     * @return {object}                  Info describing the request to the server.
     */
    buildRequests: function (validBidRequests, bidderRequest) {
      let reqs = [];
      let bannerImps = [];
      let videoImps = [];
      let validBidRequest = null;
  
      for (let i = 0; i < validBidRequests.length; i++) {
        validBidRequest = validBidRequests[i];
  
        if (validBidRequest.mediaType === VIDEO || utils.deepAccess(validBidRequest, 'mediaTypes.video')) {
          if (validBidRequest.mediaType === VIDEO || includesSize(validBidRequest.mediaTypes.video.playerSize, validBidRequest.params.size)) {
            videoImps.push(bidToVideoImp(validBidRequest));
          } else {
            utils.logError('Bid size is not included in video playerSize')
          }
        }
  
        if (validBidRequest.mediaType === BANNER || utils.deepAccess(validBidRequest, 'mediaTypes.banner') ||
            (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) {
          bannerImps.push(bidToBannerImp(validBidRequest));
        }
      }
  
      if (bannerImps.length > 0) {
        reqs.push(buildRequest(validBidRequests, bidderRequest, bannerImps, BANNER_ENDPOINT_VERSION));
      }
      if (videoImps.length > 0) {
        reqs.push(buildRequest(validBidRequests, bidderRequest, videoImps, VIDEO_ENDPOINT_VERSION));
      }
  
      return reqs;
    },
  
    /**
     * Unpack the response from the server into a list of bids.
     *
     * @param  {object} serverResponse A successful response from the server.
     * @param  {object} bidderRequest  The bid request sent to the server.
     * @return {array}                 An array of bids which were nested inside the server.
     */
    interpretResponse: function (serverResponse, bidderRequest) {
      const bids = [];
      let bid = null;
  
      if (!serverResponse.hasOwnProperty('body') || !serverResponse.body.hasOwnProperty('seatbid')) {
        return bids;
      }
  
      const responseBody = serverResponse.body;
      const seatbid = responseBody.seatbid;
      for (let i = 0; i < seatbid.length; i++) {
        if (!seatbid[i].hasOwnProperty('bid')) {
          continue;
        }
  
        // Transform rawBid in bid response to the format that will be accepted by prebid.
        const innerBids = seatbid[i].bid;
        let requestBid = JSON.parse(bidderRequest.data.r);
  
        for (let j = 0; j < innerBids.length; j++) {
          const bidRequest = getBidRequest(innerBids[j].impid, requestBid.imp);
          bid = parseBid(innerBids[j], responseBody.cur, bidRequest);
          bids.push(bid);
        }
      }
  
      return bids;
    },
  
    /**
     * Covert bid param types for S2S
     * @param {Object} params bid params
     * @param {Boolean} isOpenRtb boolean to check openrtb2 protocol
     * @return {Object} params bid params
     */
    transformBidParams: function(params, isOpenRtb) {
      return utils.convertTypes({
        'siteID': 'number'
      }, params);
    }
  };
  
  registerBidder(spec);
  