import {
  extendPrototype,
} from '../utils/functionExtensions';
import createNS from '../utils/helpers/svg_elements';
import createTag from '../utils/helpers/html_elements';
import SVGRenderer from './SVGRenderer';
import HSolidElement from '../elements/htmlElements/HSolidElement';
import {
  styleDiv,
} from '../utils/common';
import BaseRenderer from './BaseRenderer';
import IImageElement from '../elements/ImageElement';
import SVGShapeElement from '../elements/svgElements/SVGShapeElement';
import HShapeElement from '../elements/htmlElements/HShapeElement';
import HTextElement from '../elements/htmlElements/HTextElement';
import HCameraElement from '../elements/htmlElements/HCameraElement';
import HImageElement from '../elements/htmlElements/HImageElement';
import ISolidElement from '../elements/SolidElement';
import SVGTextLottieElement from '../elements/svgElements/SVGTextElement';

function HybridRendererBase(animationItem, config) {
  this.animationItem = animationItem;
  this.layers = null;
  this.renderedFrame = -1;
  this.renderConfig = {
    className: (config && config.className) || '',
    imagePreserveAspectRatio: (config && config.imagePreserveAspectRatio) || 'xMidYMid slice',
    hideOnTransparent: !(config && config.hideOnTransparent === false),
    filterSize: {
      width: (config && config.filterSize && config.filterSize.width) || '400%',
      height: (config && config.filterSize && config.filterSize.height) || '400%',
      x: (config && config.filterSize && config.filterSize.x) || '-100%',
      y: (config && config.filterSize && config.filterSize.y) || '-100%',
    },
  };
  this.globalData = {
    _mdf: false,
    frameNum: -1,
    renderConfig: this.renderConfig,
  };
  this.pendingElements = [];
  this.elements = [];
  this.threeDElements = [];
  this.destroyed = false;
  this.camera = null;
  this.supports3d = true;
  this.rendererType = 'html';
}

extendPrototype([BaseRenderer], HybridRendererBase);

HybridRendererBase.prototype.buildItem = SVGRenderer.prototype.buildItem;

HybridRendererBase.prototype.checkPendingElements = function () {
  while (this.pendingElements.length) {
    var element = this.pendingElements.pop();
    element.checkParenting();
  }
};

HybridRendererBase.prototype.appendElementInPos = function (element, pos) {
  var newDOMElement = element.getBaseElement();
  if (!newDOMElement) {
    return;
  }
  var layer = this.layers[pos];
  if (!layer.ddd || !this.supports3d) {
    if (this.threeDElements) {
      this.addTo3dContainer(newDOMElement, pos);
    } else {
      var i = 0;
      var nextDOMElement;
      var nextLayer;
      var tmpDOMElement;
      while (i < pos) {
        if (this.elements[i] && this.elements[i] !== true && this.elements[i].getBaseElement) {
          nextLayer = this.elements[i];
          tmpDOMElement = this.layers[i].ddd ? this.getThreeDContainerByPos(i) : nextLayer.getBaseElement();
          nextDOMElement = tmpDOMElement || nextDOMElement;
        }
        i += 1;
      }
      if (nextDOMElement) {
        if (!layer.ddd || !this.supports3d) {
          this.layerElement.insertBefore(newDOMElement, nextDOMElement);
        }
      } else if (!layer.ddd || !this.supports3d) {
        this.layerElement.appendChild(newDOMElement);
      }
    }
  } else {
    this.addTo3dContainer(newDOMElement, pos);
  }
};

HybridRendererBase.prototype.createShape = function (data) {
  if (!this.supports3d) {
    return new SVGShapeElement(data, this.globalData, this);
  }
  return new HShapeElement(data, this.globalData, this);
};

HybridRendererBase.prototype.createText = function (data) {
  if (!this.supports3d) {
    return new SVGTextLottieElement(data, this.globalData, this);
  }
  return new HTextElement(data, this.globalData, this);
};

HybridRendererBase.prototype.createCamera = function (data) {
  this.camera = new HCameraElement(data, this.globalData, this);
  return this.camera;
};

HybridRendererBase.prototype.createImage = function (data) {
  if (!this.supports3d) {
    return new IImageElement(data, this.globalData, this);
  }
  return new HImageElement(data, this.globalData, this);
};

HybridRendererBase.prototype.createSolid = function (data) {
  if (!this.supports3d) {
    return new ISolidElement(data, this.globalData, this);
  }
  return new HSolidElement(data, this.globalData, this);
};

HybridRendererBase.prototype.createNull = SVGRenderer.prototype.createNull;

HybridRendererBase.prototype.getThreeDContainerByPos = function (pos) {
  var i = 0;
  var len = this.threeDElements.length;
  while (i < len) {
    if (this.threeDElements[i].startPos <= pos && this.threeDElements[i].endPos >= pos) {
      return this.threeDElements[i].perspectiveElem;
    }
    i += 1;
  }
  return null;
};

HybridRendererBase.prototype.createThreeDContainer = function (pos, type) {
  var perspectiveElem = createTag('div');
  var style;
  var containerStyle;
  styleDiv(perspectiveElem);
  var container = createTag('div');
  styleDiv(container);
  if (type === '3d') {
    style = perspectiveElem.style;
    style.width = this.globalData.compSize.w + 'px';
    style.height = this.globalData.compSize.h + 'px';
    var center = '50% 50%';
    style.webkitTransformOrigin = center;
    style.mozTransformOrigin = center;
    style.transformOrigin = center;
    containerStyle = container.style;
    var matrix = 'matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)';
    containerStyle.transform = matrix;
    containerStyle.webkitTransform = matrix;
  }

  perspectiveElem.appendChild(container);
  // this.resizerElem.appendChild(perspectiveElem);
  var threeDContainerData = {
    container: container,
    perspectiveElem: perspectiveElem,
    startPos: pos,
    endPos: pos,
    type: type,
  };
  this.threeDElements.push(threeDContainerData);
  return threeDContainerData;
};

HybridRendererBase.prototype.build3dContainers = function () {
  var i;
  var len = this.layers.length;
  var lastThreeDContainerData;
  var currentContainer = '';
  for (i = 0; i < len; i += 1) {
    if (this.layers[i].ddd && this.layers[i].ty !== 3) {
      if (currentContainer !== '3d') {
        currentContainer = '3d';
        lastThreeDContainerData = this.createThreeDContainer(i, '3d');
      }
      lastThreeDContainerData.endPos = Math.max(lastThreeDContainerData.endPos, i);
    } else {
      if (currentContainer !== '2d') {
        currentContainer = '2d';
        lastThreeDContainerData = this.createThreeDContainer(i, '2d');
      }
      lastThreeDContainerData.endPos = Math.max(lastThreeDContainerData.endPos, i);
    }
  }
  len = this.threeDElements.length;
  for (i = len - 1; i >= 0; i -= 1) {
    this.resizerElem.appendChild(this.threeDElements[i].perspectiveElem);
  }
};

HybridRendererBase.prototype.addTo3dContainer = function (elem, pos) {
  var i = 0;
  var len = this.threeDElements.length;
  while (i < len) {
    if (pos <= this.threeDElements[i].endPos) {
      var j = this.threeDElements[i].startPos;
      var nextElement;
      while (j < pos) {
        if (this.elements[j] && this.elements[j].getBaseElement) {
          nextElement = this.elements[j].getBaseElement();
        }
        j += 1;
      }
      if (nextElement) {
        this.threeDElements[i].container.insertBefore(elem, nextElement);
      } else {
        this.threeDElements[i].container.appendChild(elem);
      }
      break;
    }
    i += 1;
  }
};

HybridRendererBase.prototype.configAnimation = function (animData) {
  var resizerElem = createTag('div');
  var wrapper = this.animationItem.wrapper;
  var style = resizerElem.style;
  style.width = animData.w + 'px';
  style.height = animData.h + 'px';
  this.resizerElem = resizerElem;
  styleDiv(resizerElem);
  style.transformStyle = 'flat';
  style.mozTransformStyle = 'flat';
  style.webkitTransformStyle = 'flat';
  if (this.renderConfig.className) {
    resizerElem.setAttribute('class', this.renderConfig.className);
  }
  wrapper.appendChild(resizerElem);

  style.overflow = 'hidden';
  var svg = createNS('svg');
  svg.setAttribute('width', '1');
  svg.setAttribute('height', '1');
  styleDiv(svg);
  this.resizerElem.appendChild(svg);
  var defs = createNS('defs');
  svg.appendChild(defs);
  this.data = animData;
  // Mask animation
  this.setupGlobalData(animData, svg);
  this.globalData.defs = defs;
  this.layers = animData.layers;
  this.layerElement = this.resizerElem;
  this.build3dContainers();
  this.updateContainerSize();
};

HybridRendererBase.prototype.destroy = function () {
  if (this.animationItem.wrapper) {
    this.animationItem.wrapper.innerText = '';
  }
  this.animationItem.container = null;
  this.globalData.defs = null;
  var i;
  var len = this.layers ? this.layers.length : 0;
  for (i = 0; i < len; i += 1) {
    if (this.elements[i] && this.elements[i].destroy) {
      this.elements[i].destroy();
    }
  }
  this.elements.length = 0;
  this.destroyed = true;
  this.animationItem = null;
};

HybridRendererBase.prototype.updateContainerSize = function () {
  var elementWidth = this.animationItem.wrapper.offsetWidth;
  var elementHeight = this.animationItem.wrapper.offsetHeight;
  var elementRel = elementWidth / elementHeight;
  var animationRel = this.globalData.compSize.w / this.globalData.compSize.h;
  var sx;
  var sy;
  var tx;
  var ty;
  if (animationRel > elementRel) {
    sx = elementWidth / (this.globalData.compSize.w);
    sy = elementWidth / (this.globalData.compSize.w);
    tx = 0;
    ty = ((elementHeight - this.globalData.compSize.h * (elementWidth / this.globalData.compSize.w)) / 2);
  } else {
    sx = elementHeight / (this.globalData.compSize.h);
    sy = elementHeight / (this.globalData.compSize.h);
    tx = (elementWidth - this.globalData.compSize.w * (elementHeight / this.globalData.compSize.h)) / 2;
    ty = 0;
  }
  var style = this.resizerElem.style;
  style.webkitTransform = 'matrix3d(' + sx + ',0,0,0,0,' + sy + ',0,0,0,0,1,0,' + tx + ',' + ty + ',0,1)';
  style.transform = style.webkitTransform;
};

HybridRendererBase.prototype.renderFrame = SVGRenderer.prototype.renderFrame;

HybridRendererBase.prototype.hide = function () {
  this.resizerElem.style.display = 'none';
};

HybridRendererBase.prototype.show = function () {
  this.resizerElem.style.display = 'block';
};

HybridRendererBase.prototype.initItems = function () {
  this.buildAllItems();
  if (this.camera) {
    this.camera.setup();
  } else {
    var cWidth = this.globalData.compSize.w;
    var cHeight = this.globalData.compSize.h;
    var i;
    var len = this.threeDElements.length;
    for (i = 0; i < len; i += 1) {
      var style = this.threeDElements[i].perspectiveElem.style;
      style.webkitPerspective = Math.sqrt(Math.pow(cWidth, 2) + Math.pow(cHeight, 2)) + 'px';
      style.perspective = style.webkitPerspective;
    }
  }
};

HybridRendererBase.prototype.searchExtraCompositions = function (assets) {
  var i;
  var len = assets.length;
  var floatingContainer = createTag('div');
  for (i = 0; i < len; i += 1) {
    if (assets[i].xt) {
      var comp = this.createComp(assets[i], floatingContainer, this.globalData.comp, null);
      comp.initExpressions();
      this.globalData.projectInterface.registerComposition(comp);
    }
  }
};

export default HybridRendererBase;
