// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
// limitations under the License.
(function (shared, testing) {
  var shorthandToLonghand = {
    background: ['backgroundImage', 'backgroundPosition', 'backgroundSize', 'backgroundRepeat', 'backgroundAttachment', 'backgroundOrigin', 'backgroundClip', 'backgroundColor'],
    border: ['borderTopColor', 'borderTopStyle', 'borderTopWidth', 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderBottomColor', 'borderBottomStyle', 'borderBottomWidth', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth'],
    borderBottom: ['borderBottomWidth', 'borderBottomStyle', 'borderBottomColor'],
    borderColor: ['borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor'],
    borderLeft: ['borderLeftWidth', 'borderLeftStyle', 'borderLeftColor'],
    borderRadius: ['borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'],
    borderRight: ['borderRightWidth', 'borderRightStyle', 'borderRightColor'],
    borderTop: ['borderTopWidth', 'borderTopStyle', 'borderTopColor'],
    borderWidth: ['borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth'],
    flex: ['flexGrow', 'flexShrink', 'flexBasis'],
    font: ['fontFamily', 'fontSize', 'fontStyle', 'fontVariant', 'fontWeight', 'lineHeight'],
    margin: ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'],
    outline: ['outlineColor', 'outlineStyle', 'outlineWidth'],
    padding: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft']
  };
  var shorthandExpanderElem = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
  var borderWidthAliases = {
    thin: '1px',
    medium: '3px',
    thick: '5px'
  };
  var aliases = {
    borderBottomWidth: borderWidthAliases,
    borderLeftWidth: borderWidthAliases,
    borderRightWidth: borderWidthAliases,
    borderTopWidth: borderWidthAliases,
    fontSize: {
      'xx-small': '60%',
      'x-small': '75%',
      'small': '89%',
      'medium': '100%',
      'large': '120%',
      'x-large': '150%',
      'xx-large': '200%'
    },
    fontWeight: {
      normal: '400',
      bold: '700'
    },
    outlineWidth: borderWidthAliases,
    textShadow: {
      none: '0px 0px 0px transparent'
    },
    boxShadow: {
      none: '0px 0px 0px 0px transparent'
    }
  };

  function antiAlias(property, value) {
    if (property in aliases) {
      return aliases[property][value] || value;
    }

    return value;
  }

  function isNotAnimatable(property) {
    // https://drafts.csswg.org/web-animations/#concept-not-animatable
    return property === 'display' || property.lastIndexOf('animation', 0) === 0 || property.lastIndexOf('transition', 0) === 0;
  } // This delegates parsing shorthand value syntax to the browser.


  function expandShorthandAndAntiAlias(property, value, result) {
    if (isNotAnimatable(property)) {
      return;
    }

    var longProperties = shorthandToLonghand[property];

    if (longProperties) {
      shorthandExpanderElem.style[property] = value;

      for (var i in longProperties) {
        var longProperty = longProperties[i];
        var longhandValue = shorthandExpanderElem.style[longProperty];
        result[longProperty] = antiAlias(longProperty, longhandValue);
      }
    } else {
      result[property] = antiAlias(property, value);
    }
  }

  ;

  function convertToArrayForm(effectInput) {
    var normalizedEffectInput = [];

    for (var property in effectInput) {
      if (property in ['easing', 'offset', 'composite']) {
        continue;
      }

      var values = effectInput[property];

      if (!Array.isArray(values)) {
        values = [values];
      }

      var keyframe;
      var numKeyframes = values.length;

      for (var i = 0; i < numKeyframes; i++) {
        keyframe = {};

        if ('offset' in effectInput) {
          keyframe.offset = effectInput.offset;
        } else if (numKeyframes == 1) {
          keyframe.offset = 1.0;
        } else {
          keyframe.offset = i / (numKeyframes - 1.0);
        }

        if ('easing' in effectInput) {
          keyframe.easing = effectInput.easing;
        }

        if ('composite' in effectInput) {
          keyframe.composite = effectInput.composite;
        }

        keyframe[property] = values[i];
        normalizedEffectInput.push(keyframe);
      }
    }

    normalizedEffectInput.sort(function (a, b) {
      return a.offset - b.offset;
    });
    return normalizedEffectInput;
  }

  ;

  function normalizeKeyframes(effectInput) {
    if (effectInput == null) {
      return [];
    }

    if (window.Symbol && Symbol.iterator && Array.prototype.from && effectInput[Symbol.iterator]) {
      // Handle custom iterables in most browsers by converting to an array
      effectInput = Array.from(effectInput);
    }

    if (!Array.isArray(effectInput)) {
      effectInput = convertToArrayForm(effectInput);
    }

    var keyframes = effectInput.map(function (originalKeyframe) {
      var keyframe = {};

      for (var member in originalKeyframe) {
        var memberValue = originalKeyframe[member];

        if (member == 'offset') {
          if (memberValue != null) {
            memberValue = Number(memberValue);
            if (!isFinite(memberValue)) throw new TypeError('Keyframe offsets must be numbers.');
            if (memberValue < 0 || memberValue > 1) throw new TypeError('Keyframe offsets must be between 0 and 1.');
          }
        } else if (member == 'composite') {
          if (memberValue == 'add' || memberValue == 'accumulate') {
            throw {
              type: DOMException.NOT_SUPPORTED_ERR,
              name: 'NotSupportedError',
              message: 'add compositing is not supported'
            };
          } else if (memberValue != 'replace') {
            throw new TypeError('Invalid composite mode ' + memberValue + '.');
          }
        } else if (member == 'easing') {
          memberValue = shared.normalizeEasing(memberValue);
        } else {
          memberValue = '' + memberValue;
        }

        expandShorthandAndAntiAlias(member, memberValue, keyframe);
      }

      if (keyframe.offset == undefined) keyframe.offset = null;
      if (keyframe.easing == undefined) keyframe.easing = 'linear';
      return keyframe;
    });
    var everyFrameHasOffset = true;
    var looselySortedByOffset = true;
    var previousOffset = -Infinity;

    for (var i = 0; i < keyframes.length; i++) {
      var offset = keyframes[i].offset;

      if (offset != null) {
        if (offset < previousOffset) {
          throw new TypeError('Keyframes are not loosely sorted by offset. Sort or specify offsets.');
        }

        previousOffset = offset;
      } else {
        everyFrameHasOffset = false;
      }
    }

    keyframes = keyframes.filter(function (keyframe) {
      return keyframe.offset >= 0 && keyframe.offset <= 1;
    });

    function spaceKeyframes() {
      var length = keyframes.length;
      if (keyframes[length - 1].offset == null) keyframes[length - 1].offset = 1;
      if (length > 1 && keyframes[0].offset == null) keyframes[0].offset = 0;
      var previousIndex = 0;
      var previousOffset = keyframes[0].offset;

      for (var i = 1; i < length; i++) {
        var offset = keyframes[i].offset;

        if (offset != null) {
          for (var j = 1; j < i - previousIndex; j++) keyframes[previousIndex + j].offset = previousOffset + (offset - previousOffset) * j / (i - previousIndex);

          previousIndex = i;
          previousOffset = offset;
        }
      }
    }

    if (!everyFrameHasOffset) spaceKeyframes();
    return keyframes;
  }

  shared.convertToArrayForm = convertToArrayForm;
  shared.normalizeKeyframes = normalizeKeyframes;

  if (WEB_ANIMATIONS_TESTING) {
    testing.normalizeKeyframes = normalizeKeyframes;
  }
})(webAnimationsShared, webAnimationsTesting);