<?php

/**
 * Render block
 */
class Flo_Flex_Builder_Render_Element {

  public $block_id = null;
  public $attributes = null;
  public $block = null;
  public $colors = null;

  public function __construct($data) {
    $this->parse_attributes($data);
  }

  public function parse_attributes($attributes) {
    foreach ($attributes as $prop_name => $prop_val) {
      $this->$prop_name = $prop_val;
    }

    $this->all_element_keys = array_keys(array_merge($this->elements, $this->mobile_elements));

    // Reordering
    $elements_props = array();
    foreach ($this->all_element_keys as $key) {
      $elements_props[$key] = isset($this->elements[$key]) ? $this->elements[$key] : $this->mobile_elements[$key];
      $elements_props[$key]['top_position'] = floatval(get_deep_value($elements_props[$key], ['data', 'posY'], 1));
    }
    $elements_props = $this->reorder_elements($elements_props);

    $this->all_element_keys = array_column($elements_props, 'id');

    $this->only_desktop = [];
    $this->only_mobile = [];
    $this->mobile_async = [];
    $this->common_elements = [];

    foreach ($this->all_element_keys as $key) {
      if (!isset($this->elements[$key])) {
        $this->only_mobile[] = $key;
      }
      if (!isset($this->mobile_elements[$key])) {
        $this->only_desktop[] = $key;
      } else if (!get_deep_value($this->mobile_elements[$key], ['data', 'synced'])) {
        $this->mobile_async[] = $key;
      } else {
        $this->common_elements[] = $key;
      }
    }
  }



  /**
   * Returns a flat array with element IDs as they sit in map
   * @param array $children[]
   * @return array elementIDs
   */
  public function get_element_ids_from_map_in_order($children) {
    $element_ids = [];

    foreach ($children as $child) {
      if ($child['type'] === 'gr') {
        $element_ids = array_merge($element_ids, $this->get_element_ids_from_map_in_order($child['children']));
      } else {
        $element_ids[] = $child['id'];
      }
    }
    return $element_ids;
  }

  /**
   * Sorts an array with arrays by a specific prop
   */
  public function sort_elements_by_position($a, $b) {
    $atop = $a['top_position'];
    $btop = $b['top_position'];
    if ($atop === $btop) {
      return 0;
    }
    return ($atop < $btop) ? -1 : 1;
  }

  public function get_block_prop($option_arr, $default) {
    return get_deep_value($this->block, $option_arr, $default);
  }

  /**
   * Reordering by position of appearance. Desktop view priority.
   * In case option is set, reorder by map
   */
  public function reorder_elements($elements) {
    $in_layer_order = $this->get_block_prop(["globalOptions", "htmlInLayerOrder"], false);
    if ($in_layer_order) {
      $element_ids = $this->get_element_ids_from_map_in_order($this->maps['desktop']['children']);
      $element_ids_mobile = $this->get_element_ids_from_map_in_order($this->maps['mobile']['children']);
      foreach ($elements as $element) {
        $desktop_index = array_search($element['id'], $element_ids);
        $elements[$element['id']]['top_position'] = $desktop_index !== false ? $desktop_index : array_search($element['id'], $element_ids_mobile);
      }
    }
    usort($elements, [$this, 'sort_elements_by_position']);

    if ($in_layer_order) {
      $elements = array_reverse($elements);
    }

    return $elements;
  }

  /**
   * Start looping within elements
   */
  function render_elements() {

    $markup = '';
    $scripts = '';
    $styles = '';

    $desktop_elements = 0;
    $mobile_elements = 0;

    foreach ($this->all_element_keys as $key) {
      $element_obj = $this->render_element($key);
      $markup .=  $element_obj['markup'];
      $scripts .=  $element_obj['script'];
      $styles .=  $element_obj['style'];

      if ($element_obj['type'] === 'only_desktop') {
        $desktop_elements++;
      } else if ($element_obj['type'] === 'only_mobile') {
        $mobile_elements++;
      } else {
        $desktop_elements++;
        $mobile_elements++;
      }
    }

    if ($scripts) $scripts = '<script>' . $scripts . '</script>';
    if ($styles) $styles = '<style>' . $styles . '</style>';

    return [
      'markup' => remove_new_lines($styles) . $scripts . $markup,
      'statistics' => [
        'desktop_elements' => $desktop_elements,
        'mobile_elements' => $mobile_elements,
      ]
    ];
  }

  /**
   * Return tablet prop or fallback to default prop if not present
   */
  public function get_tablet_prop($data, $prop_name) {
    if (isset($data[$prop_name . '_tablet'])) return $data[$prop_name . '_tablet'];
    return $data[$prop_name];
  }

  /**
   * Main file to generate element styles
   */
  public function get_element_styles($element, $zIndexFromMap = null, $viewport = "desktop") {

    $general_vars = "";
    $element_vars = "";
    $data = $element['data'];
    $user_data = $data['user_data'];

    $elementWidth_tablet = $this->get_tablet_prop($data, 'elementWidth');
    $elementHeight_tablet = $this->get_tablet_prop($data, 'elementHeight');

    $elementWidthPx = $data['elementWidth'] * 16;
    $elementWidthPx_tablet = $elementWidth_tablet * 16;

    $elementHeightPx = $data['elementHeight'] * 16;
    $elementHeightPx_tablet = $elementHeight_tablet * 16;

    $colors = $this->colors;
    $parentSizePos = null;

    $elementWidthEm = flexParseNr($data['elementWidth']);
    $elementWidthEm_tablet = flexParseNr($elementWidth_tablet);

    $elementHeightEm = flexParseNr($data['elementHeight']);
    $elementHeightEm_tablet = flexParseNr($elementHeight_tablet);

    $elementWidth = $elementWidthPx == 1 ? $elementWidthPx . "px" : $elementWidthEm . "em";
    $elementWidth_tablet = $elementWidthPx_tablet == 1 ? $elementWidthPx_tablet . "px" : $elementWidthEm_tablet . "em";

    $elementHeight = $elementHeightPx == 1 ? $elementHeightPx . "px" : $elementHeightEm . "em";
    $elementHeight_tablet = $elementHeightPx_tablet == 1 ? $elementHeightPx_tablet . "px" : $elementHeightEm_tablet . "em";

    if ($zIndexFromMap) {
      $elZIndex = $zIndexFromMap;
    } else {
      $elZIndex = $data['zIndex'];
    }

    if ($parentSizePos) {
      $posX = $data["posX"] - $parentSizePos["left"];
      $posY = $data["posY"] - $parentSizePos["top"];
    } else {
      $posX = $data["posX"];
      $posY = $data["posY"];
    }

    $posX = flexParseNr($posX);
    $posX_tablet = flexParseNr($this->get_tablet_prop($data, 'posX'));

    $posY = flexParseNr($posY);
    $posY_tablet = flexParseNr($this->get_tablet_prop($data, 'posY'));

    $rotateDeg = flexParseNr($data['rotateDeg']);


    $general_vars = "
          --el-width: " . $elementWidth . ";
          --el-width-tablet: " . $elementWidth_tablet . ";
          --el-height: " . $elementHeight . ";
          --el-height-tablet: " . $elementHeight_tablet . ";
          --el-actual-height: " . $elementHeight . ";
          --el-actual-height-tablet: " . $elementHeight_tablet . ";
          --position-top: " . $posY . "em;
          --position-top-tablet: " . $posY_tablet . "em;
          --position-left: " . $posX . "em;
          --position-left-tablet: " . $posX_tablet . "em;
          --rotateDeg: " . $rotateDeg . "deg;
          --z-index: " . $elZIndex . ";
        ";

    if (isset($data['fullHeight']) && $data['fullHeight'] === true) {
      $general_vars .= '
        --el-full-height: 100vh;
      ';
    }

    if (isset($user_data['zoom']) && $user_data['zoom'] !== 100) {
      $general_vars .= '
        --el-fontSize: '.($user_data['zoom'] / 100) * $user_data['fontSize'].'rem;
      ';
    }

    if (isset($data['user_data']['useBorder']) && $data['user_data']['useBorder']) {

      $borderColor = parseColor($user_data['borderColor'], $colors);
      $borderWidth = isset($user_data['borderWidth']) ? $user_data['borderWidth'] : '0';
      $general_vars .= "
            --border-color: " . $borderColor . ";
            --border-width: " . $borderWidth . "px;
          ";
    }

    if ($data['type'] === 'image') {
      $opacity = isset($user_data['imageOpacity']) ? $user_data['imageOpacity'] / 100 : 1;
      $general_vars .= "opacity: " . $opacity . ";";
    }

    $general_vars .= isset($user_data['borderRadius']) ? "--border-radius: " . $user_data['borderRadius'] . "px;" : '';

    /* START: BY TYPE */
    switch ($element["data"]["type"]) {
      case 'text':

        /* START: Add Bg Color */
        if ($user_data['useBg']) {
          $backgroundColor = parseColor($user_data['backgroundColor'], $colors);
          $element_vars .= "--backgroundColor: " . $backgroundColor . ";";
        }
        /* END: Add Bg Color */

        /* START: Align the text Verticaly */
        if (isset($user_data['verticalAlign'])) {
          $verticalAlign = $user_data['verticalAlign'];
          $alignment_for_flex = "";
          switch ($verticalAlign) {
            case 'top':
              $alignment_for_flex = "flex-start";
              break;
            case 'middle':
              $alignment_for_flex = "center";
              break;
            case 'bottom':
              $alignment_for_flex = "flex-end";
              break;
            default:
              $alignment_for_flex = "flex-start";
              break;
          }
          $element_vars .= "
                    --text-vertical-align: " . $alignment_for_flex . ";
                  ";
        }
        /* END: Align the text Verticaly */

        /* START: Horizontal Text Alignment */
        if ($viewport === "mobile") {
          // if element is synced and has overrides, pull overrides text align data
          if (
            isset($element['data']['synced']) && $element['data']['synced'] &&
            isset($element['data']['overrides']) &&
            isset($element['data']['overrides']['textAlign'])
          ) {
            $element_vars .= "--text-align: " . $element['data']['overrides']['textAlign'] . ";";
          } else if (isset($user_data['textAlign'])) {
            // otherwise pull text align data from desktop matching element
            $element_vars .= "--text-align: " . $user_data['textAlign'] . ";";
          }
        } else {
          if (isset($user_data['textAlign'])) {
            $element_vars .= "--text-align: " . $user_data['textAlign'] . ";";
          }
        }

        /* END: Horizontal Text Alignment */

        /* START: FONT STUFF */
        if (!(isset($user_data['useFontStyle']) && $user_data['useFontStyle'])) {
          if (isset($user_data['fontFamily'])) {
            $element_vars .= "
                      --font-family: " . str_replace('"', "'", $user_data['fontFamily']) . ";
                    ";
          }
        }
        /* END: FONT STUFF */

        /* START: Set Height Auto */
        if (isset($user_data['heightAuto']) && $user_data['heightAuto'] === true) {

          if (isset($user_data['paddings'])) {
            $paddings = $user_data['paddings'];
            $vertical_paddings = $paddings['vertical'] . "em";
            $horizontal_paddings = $paddings['horizontal'] . "em";
          } else {
            $vertical_paddings = 0;
            $horizontal_paddings = 0;
          }

          $element_vars .= "
                    --el-height: auto;
                    --el-paddings: " . $vertical_paddings . " " . $horizontal_paddings . ";
                  ";
        }
        /* END: Set Height Auto */

        break;
      case 'button':
        if ($user_data['useBg']) {
          $backgroundColor = parseColor($user_data['backgroundColor'], $colors);
          $element_vars .= "--backgroundColor: " . $backgroundColor . ";";
        }

        $elementColor = parseColor($user_data['elementColor'], $colors);

        // if (!$elementColor) deb($element);
        $element_vars .= "--button-color: " . $elementColor . ";";

        /* START: Horizontal Text Alignment */
        if (isset($user_data['textAlign'])) {
          $element_vars .= "--text-align: " . $user_data['textAlign'] . ";";
        }
        /* END: Horizontal Text Alignment */

        /* START: FONT STUFF */
        if (!(isset($user_data['useFontStyle']) && $user_data['useFontStyle'])) {
          if (isset($user_data['fontFamily'])) {
            $element_vars .= "
                    --font-family: " . str_replace('"', "'", $user_data['fontFamily']) . ";
                  ";
          }
        }
        /* END: FONT STUFF */

        /* START: BUTTON HOVER STATE */
        $hoverBg = isset($user_data['hoverBg']) ? parseColor($user_data['hoverBg'], $colors) : "";
        $hoverText = isset($user_data['hoverText']) ? parseColor($user_data['hoverText'], $colors) : "";
        $hoverBorder = isset($user_data['hoverBorder']) ? parseColor($user_data['hoverBorder'], $colors) : "";

        $hover_state_data = [
          '--hover-color'            => $hoverText,
          '--hover-border-color'     => $hoverBorder,
          '--hover-background-color' => $hoverBg
        ];

        $hover_state_styles = "";
        foreach ($hover_state_data as $cssProp => $cssVal) {
          if ($cssVal) {
            $hover_state_styles .= $cssProp . ":" . $cssVal . "; ";
          }
        }
        /* END: BUTTON HOVER STATE */

        break;
      case 'shape':
        if ($user_data['useBg']) {

          $backgroundColor = parseColor($user_data['backgroundColor'], $colors);

          if (isset($user_data['useGradient']) && $user_data['useGradient']) {

            $secondColor = parseColor($user_data['secondBgColor'], $colors);

            $checkAlpha = explode(',', $secondColor);
            if (isset($checkAlpha[3]) && $checkAlpha[3] === '0)') {
              $secondColor = 'transparent';
            }

            $gradientProgression =
              isset($user_data['gradientProgression']) &&
              is_array($user_data['gradientProgression'])
              ? $user_data['gradientProgression']
              : [0, 100];

            $element_vars .= "
                    --gradient: linear-gradient(to right, " . $backgroundColor . " " . $gradientProgression[0] . "%, " . $secondColor . " " . $gradientProgression[1] . "%);
                  ";
          } else {
            $element_vars .= "
                    --backgroundColor: " . $backgroundColor . ";
                  ";
          }
        }
        break;
      case 'image':
        $element_vars .= flex_get_bg_image_data($data, $viewport);

        break;
      case 'video':
        if (isset($user_data['backgroundImage']) && strlen($user_data['backgroundImage'])) {
          $element_vars .= flex_get_bg_image_data($data, $viewport);

          if ($user_data['playButtonSize']) {
            $element_vars .= "
                    --play-button-font-size: " . $user_data['playButtonSize'] . "px;
                  ";
          }

          if ($user_data['playButtonColor']) {
            $playButtonColor = parseColor($user_data['playButtonColor'], $colors);

            $element_vars .= "
                    --play-button-color: " . $playButtonColor . ";
                  ";
          }
        }

        break;
      case 'line':
        if ($user_data['useBg']) {
          $backgroundColor = parseColor($user_data['backgroundColor'], $colors);
          $element_vars .= "
                --backgroundColor: " . $backgroundColor . ";
              ";
        }
        break;
      default:
        break;
    }

    $styles = $general_vars . $element_vars;
    if (isset($hover_state_styles) && strlen($hover_state_styles)) {
      $styles .= $hover_state_styles;
    }

    return $styles;
  }

  public function get_style_wrapper($id, $elStyles, $elStylesMobile) {
    $blockID = $this->block_id;

    $style = '';

    if ($elStyles && $elStylesMobile) {
      $style .= "
          .flex-block__element--block-" . $blockID . ".flex-block__element--" . $id . " {
            " . $elStyles . "
          }
          @media only screen and (max-width: 767px) {
            .flex-block__element--block-" . $blockID . ".flex-block__element--" . $id . " {
             " . $elStylesMobile . "
            }
          }
        ";
    } else {
      $style .= "
        .flex-block__element--block-" . $blockID . ".flex-block__element--" . $id . " {
          " . $elStyles . $elStylesMobile . "
        }
        ";
    }
    return $style;
  }

  /**
   * Returns all animation style/script tags
   */
  public function get_element_animations($element, $device = false) {
    $id = $element['id'];
    $id_and_device = $device ? $element["id"] . '__' . $device : $element["id"];
    $data = $element['data'];
    $user_data = $data['user_data'];
    $blockID = $this->block_id;

    $script = '';
    $markup = '';
    $style = '';

    if (isset($user_data["animations"]["preset"]) && $user_data["animations"]["preset"] !== false) {
      $animations = $user_data["animations"];

      // $element_and_device = $device ? $element["id"] . '_' . $device : $element["id"];
      $script .= "
          if (!window.flexData.animations['" . $blockID . "']) {
            window.flexData.animations['" . $blockID . "'] = {};
          }
          window.flexData.animations['" . $blockID . "']['" . $id_and_device . "'] = " . json_encode($user_data["animations"]) . ";
      ";

      if ($animations['initial']['enabled'] === true) {
        $vars = $animations['initial']['steps']['step0'];

        $top = useVar($vars, 'top', 0) / 16;
        $left = useVar($vars, 'left', 0) / 16;
        $opacity =  useVar($vars, 'opacity', 100) / 100;
        $scale = useVar($vars, 'opacity', 100) / 100;
        $rotate = useVar($vars, 'rotate', 0);
        $blur = useVar($vars, 'blur', 0);
        $transformOrigin = useVar($vars, 'transformOrigin', 'center');
        $clipTop = useVar($vars, 'clipTop', 0);
        $clipRight = useVar($vars, 'clipRight', 0);
        $clipBottom = useVar($vars, 'clipBottom', 0);
        $clipLeft = useVar($vars, 'clipLeft', 0);

        $style = "
            .flex-block__element--block-" . $blockID . ".flex-block__element--" . $id . " {
              --animation-top: " . $top. "rem;
              --animation-left: " . $left . "rem;
              --animation-rotate: " . $rotate . "deg;
              --animation-opacity: " . $opacity . ";
              --animation-scale: " . $scale . ";
              --animation-blur: " . $blur . "px;
              --animation-transform-origin: " . $transformOrigin . ";
              --animation-clip-top: " . $clipTop. "%;
              --animation-clip-right: " . $clipRight . "%;
              --animation-clip-bottom: " . $clipBottom . "%;
              --animation-clip-left: " . $clipLeft . "%;
        ";

        if (isset($vars["color"])) {
          $color = getStylekitColor($vars["color"]);
          $style .= "--animation-color: " . $color . ";";
        }

        $style .= "
            }
        ";

        if ($device === 'mobile') {
          $style = "
            @media only screen and (max-width: 767px) {
              " . $style . "
            }
          ";
        }

        // if (isset($vars["color"])) {
        //   $markup .= "<div class='flex-block-element-has-color-indicator'></div>";
        // }
      }
    }

    return ['markup' => $markup, 'style' => '/*ANIMATION START*/' . $style . '/*ANIMATION END*/', 'script' => $script];
  }

  /**
   * Returns all element custom HTML classes
   */
  public function get_element_classes($user_data, $desktop_element, $mobile_element) {
    $b = $this->b;
    $custom_classes = isset($user_data['customClasses']) ? $user_data['customClasses'] : '';
    if (strlen($custom_classes)) {
      $custom_classes = str_replace(' ', '', $custom_classes);
      $custom_classes = str_replace(',', ' ', $custom_classes);
    }

    // Add Block ID to Class
    $custom_classes .= ' ' . $b . '__element--block-' . $this->block_id . ' ';

    // Add "has animation" modifier
    if (isset($user_data["animations"]["preset"]) && $user_data["animations"]["preset"] !== false) {
      $custom_classes .= ' ' . $b . '__element--has-animation ';
      $custom_classes .= ' ' . $b . '__element--action-type-' . $user_data["animations"]["presetConfig"]["actionType"] . ' ';
      $custom_classes .= ' ' . $b . '__element--animation-type-' . $user_data["animations"]["preset"] . ' ';

      // Add "has color Animation" modifier
      $animations = $user_data["animations"];
      $vars = $animations['initial']['steps']['step0'];
      if (isset($vars["color"])) {
        $custom_classes .= ' ' . $b . '__element--has-color-animation ';
      }
    }

    // Add Em Strong Fix for Text and Button
    if (isset($user_data['editorTagsFix']) && $user_data['editorTagsFix'] === true) {
      $custom_classes .= ' ' . $b . '__element--editorTagsFix';
    }

    // Has vertical alignment
    if ($desktop_element && isset($desktop_element['data']['verticalAlignment'])) {
      $custom_classes .= ' ' . $b . '__element--desktop-vertical-alignment-' . $desktop_element['data']['verticalAlignment'];
    }
    if ($mobile_element && isset($mobile_element['data']['verticalAlignment'])) {
      $custom_classes .= ' ' . $b . '__element--mobile-vertical-alignment-' . $mobile_element['data']['verticalAlignment'];
    }

    return $custom_classes;
  }

  public function generate_element_markup($element, $device, $desktop_element = false, $mobile_element = false, $more_classes  = '') {
    $data = $element['data'];
    $user_data = $data['user_data'];

    if ( isset($user_data['zoom'] ) && $user_data['zoom'] !== 100) {
      $more_classes .='el-has-zoom';
    }; 

    $should_encode = !$this->is_common($element['id']);

    $custom_classes = $this->get_element_classes($user_data, $desktop_element, $mobile_element);
    $custom_classes .= ' '.$more_classes;

    $el_markup = elementTypesRenderElement($data['type'], $this->b, $element, $custom_classes, '', $this->colors, $device, array("block_id" => $this->block_id, 'current_block' => $this->block));

    // Do not encode when server-side rendering is enforced.
    if ($this->force_render) $should_encode = false;

    return $should_encode ? $this->encode_markup($el_markup, $device, $element['id']) : [$el_markup, ''];
  }

  public function is_common($key) {
    return in_array($key, $this->common_elements);
  }

  public function is_mobile($key) {
    return in_array($key, $this->only_mobile);
  }

  public function is_desktop($key) {
    return in_array($key, $this->only_desktop);
  }

  public function is_async($key) {
    return in_array($key, $this->mobile_async);
  }

  public function encode_markupPHP($element, $device, $id) {
    $el_markup = '<div class="flex-element_encoded" data-flex-device="' . $device . '" data-element-id="' . $id . '" data-element="' . htmlentities($element, ENT_QUOTES, 'UTF-8') . '"></div>';
    return [$el_markup, ''];
  }

  function get_element_id($element_id) {
    // return $this->block_id . '__' . $element_id;
    return 'flex-block__element--' . $element_id;
  }

  // public function encode_markupJS($element, $device, $id) {

  //   $unique_id = $this->get_element_id($id);
  //   $content_encoded = htmlentities($element, ENT_QUOTES, 'UTF-8');
  //   $el_markup = '<div class="flex-element_encoded" data-flex="' . $device . '" data-element-id="' . $unique_id . '" data-element="' . $content_encoded . '"></div>';

  //   $el_script = 'window.flexBlocks[`' . $device . '`][`' . $unique_id . '`] = `' . $content_encoded . '`;';

  //   return [$el_markup, $el_script];
  // }

  public function encode_markup($element, $device, $id) {

    // Unfortunately we have to encode it, as element markup can contain all sorts of script/style tags
    $content_encoded = htmlentities($element, ENT_QUOTES, 'UTF-8');

    $unique_id = $this->get_element_id($id);
    $el_markup = '<script data-element-id="' . $unique_id . '" data-element-device="' . $device . '" type="text/template">' . $content_encoded . '</script>';

    $el_script = '';
    return [$el_markup, $el_script];
  }

  function wrap_element_data($markup, $element_type, $style = '', $script = '') {
    return ['markup' => $markup, 'style' => $style, 'script' => $script, 'type' => $element_type];
  }

  function inherit_from_desktop($desktop, $mobile) {

    if (!isset($desktop['data']['user_data']) || !is_array($desktop['data']['user_data'])) $desktop['data']['user_data'] = [];
    if (!isset($mobile['data']['user_data']) || !is_array($mobile['data']['user_data'])) $mobile['data']['user_data'] = [];

    $mobile['data']['user_data'] = array_merge($desktop['data']['user_data'], $mobile['data']['user_data']);
    $mobile['data'] = array_merge($desktop['data'], $mobile['data']);
    return $mobile;
  }

  /**
   * Main render file
   */
  public function render_element($element_key) {

    $b = $this->b;
    $el_markup = "";
    $el_scripts = '';
    $z_indexes_per_device = $this->get_block_prop(["zIndexMaps"], null);
    $z_index_desktop = get_deep_value($z_indexes_per_device, ['desktop', $element_key], 1);
    $z_index_mobile = get_deep_value($z_indexes_per_device, ['mobile', $element_key], $z_index_desktop);

    $element = false;

    // For common elements and ONLY desktop, render "desktop version"
    if ($this->is_common($element_key)) {

      $element = $this->elements[$element_key];
      $mobile_element = $this->mobile_elements[$element_key];
      // Some desktop elements require desktop element
      $element['mobile_element'] = $mobile_element;
      $mobile_element = $this->inherit_from_desktop($element, $mobile_element);

      $more_classes = ' flex-block__element--synced ';
      if (isset($mobile_element['data']['mobileAnimationDisabled']) && $mobile_element['data']['mobileAnimationDisabled'] === true) {
        $more_classes = 'flex-block__element--mobile-animation-disabled';
      }
      $el_markup_arr = $this->generate_element_markup($element, 'desktop', $element, $mobile_element, $more_classes);
      $el_markup .= $el_markup_arr[0];

      $element_styles = $this->get_element_styles($element, $z_index_desktop, 'desktop');

      // It's synced, so options come from desktop
      $mobile_element['data']['user_data'] = $element['data']['user_data'];

      $element_styles_mob = $this->get_element_styles($mobile_element, $z_index_mobile, 'mobile');
      $element_styles_all =  $this->get_style_wrapper($element['id'], $element_styles, $element_styles_mob);

      $anime = $this->get_element_animations($element);
      $el_markup .= $anime['markup'];
      $element_styles_all .= $anime['style'];
      $el_scripts .= $anime['script'];

      $el_markup = $this->wrap_element_data($el_markup, 'common', $element_styles_all, $el_scripts);
    } else if ($this->is_desktop($element_key)) {
      $element = $this->elements[$element_key];
      $el_markup_arr = $this->generate_element_markup($element, 'desktop', $element, false);
      $el_markup .= $el_markup_arr[0];
      $el_scripts .= $el_markup_arr[1];

      $element_styles = $this->get_element_styles($element, $z_index_desktop, 'desktop');

      $element_styles_all = $this->get_style_wrapper($element['id'], $element_styles, '');
      $anime = $this->get_element_animations($element);
      $el_markup .= $anime['markup'];
      $element_styles_all .= $anime['style'];
      $el_scripts .= $anime['script'];

      $el_markup = $this->wrap_element_data($el_markup, 'only_desktop', $element_styles_all, $el_scripts);
    } else if ($this->is_mobile($element_key)) {
      $element = $this->mobile_elements[$element_key];
      $el_markup_arr = $this->generate_element_markup($element, 'mobile', false, $element);
      $el_markup .= $el_markup_arr[0];
      $el_scripts .= $el_markup_arr[1];

      $element_styles = $this->get_element_styles($element, $z_index_mobile, 'mobile');

      $element_styles_all = $this->get_style_wrapper($element['id'], '', $element_styles);
      $anime = $this->get_element_animations($element);
      $el_markup .= $anime['markup'];
      $element_styles_all .= $anime['style'];
      $el_scripts .= $anime['script'];

      $el_markup = $this->wrap_element_data($el_markup, 'only_mobile', $element_styles_all, $el_scripts);
    } else if ($this->is_async($element_key)) {
      $element = $this->elements[$element_key];
      $mobile_element = $this->mobile_elements[$element_key];

      // Some mobile elements require desktop element
      $mobile_element['desktopElement'] = $element;

      $el_markup_arr = $this->generate_element_markup($element, 'desktop', $element, $mobile_element);
      $el_markup_mobile_arr = $this->generate_element_markup($mobile_element, 'mobile', $element, $mobile_element);

      $el_markup .= $el_markup_arr[0] . $el_markup_mobile_arr[0];
      $el_scripts .= $el_markup_arr[1] .  $el_markup_mobile_arr[1];

      $element_styles = $this->get_element_styles($element, $z_index_desktop, 'desktop');
      $element_styles_mobile = $this->get_element_styles($mobile_element, $z_index_mobile, 'mobile');

      $element_styles_all = $this->get_style_wrapper($element['id'], $element_styles, $element_styles_mobile);
      $anime = $this->get_element_animations($element);
      $anime_mobile = $this->get_element_animations($mobile_element, 'mobile');

      $el_markup .= $anime['markup'];
      $element_styles_all .= $anime['style'];
      $el_scripts .= $anime['script'];

      $el_markup .= $anime_mobile['markup'];
      $element_styles_all .= $anime_mobile['style'];
      $el_scripts .= $anime_mobile['script'];

      $el_markup = $this->wrap_element_data($el_markup, 'async', $element_styles_all, $el_scripts);
    }

    return $el_markup;
  }
}
