"use strict";

var _postcss = _interopRequireDefault(require("postcss"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * we have give support for ignore(exclude) comments
 * These are the comments' keyword
 */
const hoverIgnoreQuery = 'Hover:ignore',
      activeIgnoreQuery = 'Active:ignore',
      hoverActiveIgnoreQuery = 'HoverActive:ignore';
const medHoverIgnoreQuery = 'MedHover:ignore',
      medActiveIgnoreQuery = 'MedActive:ignore',
      medHoverActiveIgnoreQuery = 'MedHoverActive:ignore';
let hoverMedQuerySuffix = '';
let hoverNoneMedQuerySuffix = '';
const ruleIgnoreCommentRegex = /(Hover:ignore|Active:ignore|HoverActive:ignore)/g;
const mediaQueryIgnoreCommentRegex = /(MedHover:ignore|MedActive:ignore|MedHoverActive:ignore)/g;

function isComment(node) {
  return node && node.type === 'comment' && node.text !== undefined;
}

function isHoverPresent(atrule) {
  let hoverPresent = false;
  atrule.walkRules(rule => {
    if (rule.selector.includes('hover')) {
      hoverPresent = true;
    }
  });
  return hoverPresent;
}

function checkForUsualClass(root, sel) {
  let present = false;
  sel.split(',').forEach(value => {
    if (value.includes('hover')) {
      const valueNoHover = value.replace(':hover', '').trim();
      root.walkRules(rule => {
        if (rule.selector === valueNoHover) {
          present = true;
        }
      });
    }
  });
  return present;
}

module.exports = _postcss.default.plugin('postcss-mobile-hover', mediaQueryHoverActiveString => rootOriginal => {
  const hoverRules = [];
  const positionsObj = {};
  hoverMedQuerySuffix = mediaQueryHoverActiveString.hover;
  hoverNoneMedQuerySuffix = mediaQueryHoverActiveString.none;

  function isRuleHasIgnoreComment(index, type) {
    const prevNode = rootOriginal.nodes[index - 1];

    if (isComment(prevNode)) {
      return prevNode.text === type;
    }

    return false;
  }

  function isMediaQueryHasIgnoreComment(node, type) {
    if (isComment(node)) {
      return node.text === type;
    }

    return false;
  }

  function hasIgnoreComment({
    index,
    atrule,
    type
  }) {
    if (type.match(mediaQueryIgnoreCommentRegex)) {
      return isMediaQueryHasIgnoreComment(atrule.nodes[index - 1], type.slice(3));
    }

    if (type.match(ruleIgnoreCommentRegex)) {
      return isRuleHasIgnoreComment(index, type);
    }

    return false;
  }

  function getPositionsOfHoverAndActiveMedQueries(parent) {
    const allNodes = rootOriginal.nodes;
    const hoverMediaQuery = `${parent.params} and ${hoverMedQuerySuffix}`;
    const hoverNoneMediaQuery = `${parent.params} and ${hoverNoneMedQuerySuffix}`;
    const positions = {
      hovMed: allNodes[positionsObj[hoverMediaQuery]],
      actMed: allNodes[positionsObj[hoverNoneMediaQuery]]
    };
    return positions;
  }

  function handleMedHoverAndHoverActiveIgnore(atrule, index) {
    return !hasIgnoreComment({
      atrule,
      index,
      type: medHoverIgnoreQuery
    }) && !hasIgnoreComment({
      atrule,
      index,
      type: medHoverActiveIgnoreQuery
    });
  }

  function handleMedActiveAndHoverActiveIgnore(atrule, index) {
    return !hasIgnoreComment({
      atrule,
      index,
      type: medActiveIgnoreQuery
    }) && !hasIgnoreComment({
      atrule,
      index,
      type: medHoverActiveIgnoreQuery
    });
  }

  function handleHoverAndHoverActiveIgnore(index) {
    return !hasIgnoreComment({
      index,
      type: hoverIgnoreQuery
    }) && !hasIgnoreComment({
      index,
      type: hoverActiveIgnoreQuery
    });
  }

  function handleActiveAndHoverActiveIgnore(index) {
    return !hasIgnoreComment({
      index,
      type: activeIgnoreQuery
    }) && !hasIgnoreComment({
      index,
      type: hoverActiveIgnoreQuery
    });
  }

  function handleAllIgnoreCases(index) {
    return !hasIgnoreComment({
      index,
      type: activeIgnoreQuery
    }) && !hasIgnoreComment({
      index,
      type: hoverIgnoreQuery
    }) && !hasIgnoreComment({
      index,
      type: hoverActiveIgnoreQuery
    });
  }

  function mediaCommaQuery(rule, index) {
    if (rule.parent.params !== undefined && !rule.parent.params.includes('hover')) {
      let newSelector = '';
      const {
        hovMed,
        actMed
      } = getPositionsOfHoverAndActiveMedQueries(rule.parent);
      const hovQueries = [];
      const actQueries = [];
      rule.selector.split(/\s*,\s*/).forEach(_subrule => {
        const subrule = _subrule.trim();

        const clone = rule.clone();

        if (subrule.includes('hover')) {
          clone.selector = subrule;

          if (handleMedHoverAndHoverActiveIgnore(rule.parent, index)) {
            hovQueries.push(subrule);
          }

          if (handleMedActiveAndHoverActiveIgnore(rule.parent, index)) {
            actQueries.push(subrule);
          }
        } else {
          newSelector += `${subrule}, `;
        }
      });

      if (hovQueries.length > 0) {
        const clone = rule.clone();
        clone.selector = hovQueries.join(',');
        hovMed.append(clone);
      }

      if (actQueries.length > 0) {
        const clone = rule.clone();
        clone.selector = actQueries.join(',');
        actMed.append(clone.clone({
          selector: clone.selector.replace(/:hover/gi, ':active')
        }));
      }

      if (handleMedHoverAndHoverActiveIgnore(rule.parent, index)) {
        rule.selector = newSelector.substring(0, newSelector.length - 2);
      }

      if (rule.selector === '') {
        rule.remove();
      }
    }
  }

  function mediaQuery(rule, index) {
    if (rule.parent.params !== undefined && !rule.parent.params.includes('hover')) {
      const {
        hovMed,
        actMed
      } = getPositionsOfHoverAndActiveMedQueries(rule.parent);

      if (rule.selector.includes('hover') && hovMed !== undefined && rule.parent.type === 'atrule') {
        if (handleMedHoverAndHoverActiveIgnore(rule.parent, index)) {
          hovMed.append(rule);
        }

        if (handleMedActiveAndHoverActiveIgnore(rule.parent, index)) {
          actMed.append(rule.clone({
            selector: rule.selector.replace(/:hover/gi, ':active')
          }));
        }
      }
    }
  }

  function commaQuery(rule, index) {
    let newSelector = '';
    const hovQueries = [];
    rule.selector.split(/\s*,\s*/).forEach(_subrule => {
      const subrule = _subrule.trim();

      if (subrule.includes('hover')) {
        // hoverRules.push({ rule: clone, index });
        hovQueries.push(subrule);
      } else {
        newSelector += `${subrule}, `;
      }
    });

    if (hovQueries.length > 0) {
      const clone = rule.clone();
      clone.selector = hovQueries.join(',');
      hoverRules.push({
        rule: clone,
        index
      });
    }

    if (handleHoverAndHoverActiveIgnore(index)) {
      if (checkForUsualClass(rootOriginal, rule.selector)) {
        rule.selector = newSelector.substring(0, newSelector.length - 2).trim();
      }
    }

    if (rule.selector === '') {
      rule.remove();
    }
  } // Start by identifying all :hover rules


  rootOriginal.walkAtRules(atrule => {
    const hoverQuery = `${atrule.params} and ${hoverMedQuerySuffix}`;
    const activeQuery = `${atrule.params} and ${hoverNoneMedQuerySuffix}`;

    if (isHoverPresent(atrule)) {
      if (!positionsObj[hoverQuery] && !positionsObj[activeQuery]) {
        rootOriginal.append({
          name: 'media',
          params: hoverQuery
        });
        positionsObj[hoverQuery] = rootOriginal.nodes.length - 1;
        rootOriginal.append({
          name: 'media',
          params: activeQuery
        });
        positionsObj[activeQuery] = rootOriginal.nodes.length - 1;
      }
    }
  });
  rootOriginal.walkRules(/:hover/i, (rule, index) => {
    // media hover query with ',' ' ' '+'
    if (rule.parent.type === 'atrule' && rule.selector.includes(',')) {
      mediaCommaQuery(rule, index);
    } else {
      // plus, space and other media queries
      mediaQuery(rule, index);
    } // usual hover query


    if (!rule.selector.match(/,+| +|\++/g) && rule.parent !== undefined && rule.parent.name === undefined) {
      hoverRules.push({
        rule,
        index
      });
    } //usual hover query with ',' ' ' '+'


    if (rule.selector.match(/,+| +|\++/g) && rule.parent.name === undefined) {
      if (rule.selector.includes(',')) {
        commaQuery(rule, index);
      } else if (rule.selector.match(/ +|\++/g)) {
        if (rule.selector.includes('hover')) {
          hoverRules.push({
            rule,
            index
          }); //rule.remove();
        }
      }
    }
  }); // If there are any :hover rules in the input, then create media queries
  // to automatically translate it into :active on touch-based devices

  if (hoverRules.length > 0) {
    // Create a media query targetting devices that actually support
    // hover
    const hoverQuery = rootOriginal.append({
      name: 'media',
      params: `${hoverMedQuerySuffix}`
    }).last; // Create a media query targetting devices that don't support hover
    // (ie. devices where we should fall back to :active instead)

    const activeQuery = rootOriginal.append({
      name: 'media',
      params: `${hoverNoneMedQuerySuffix}`
    }).last; // Loop through the hover rules and apply them to each of the media
    // queries
    // eslint-disable-next-line no-labels

    outerLoop: for (const hoverRule of hoverRules) {
      // determine if the rule has been nested inside another media
      // query; in that case bail out as we have no way of reliably
      // nesting these queries
      let parentRule = hoverRule.rule.parent;

      while (parentRule) {
        if (parentRule.type === 'atrule' && parentRule.name === 'media') {
          // eslint-disable-next-line no-labels
          continue outerLoop;
        }

        parentRule = parentRule.parent;
      } // Push a clone of the :hover rule 'as is' to queries where we
      // expect the user's device to support hover
      // ieQuery.append(hoverRule.clone());


      if (handleHoverAndHoverActiveIgnore(hoverRule.index)) {
        hoverQuery.append(hoverRule.rule.clone());
      } // Push a clone of the :hover rule, where we transform the
      // selector to :active to the query targetting devices that
      // don't support hover


      if (handleActiveAndHoverActiveIgnore(hoverRule.index)) {
        activeQuery.append(hoverRule.rule.clone({
          selector: hoverRule.rule.selector.replace(/:hover/gi, ':active')
        }));
      } // remove legacy rule from output


      if (handleAllIgnoreCases(hoverRule.index)) {
        hoverRule.rule.remove();
      }
    }
  } // rootOriginal.walkAtRules(atrule => {
  //   if (
  //     (atrule !== undefined &&
  //       atrule.nodes !== undefined &&
  //       atrule.nodes.length === 0) ||
  //     atrule.nodes === undefined
  //   ) {
  //     atrule.remove();
  //   }
  // });

});