const _this = {};

export const init_module = (e) => {
  _this.func = e.func;
  _this.glb = e.glb;
  _this.SESSION_OBJ = e.SESSION_OBJ;
  _this.APP_OBJ = e.APP_OBJ;
  _this.IS_DOCKER = e.IS_DOCKER;
  _this.IS_API_SERVER = e.IS_API_SERVER;
  _this.IS_PROCESS_SERVER = e.IS_PROCESS_SERVER;
};

export const live_preview_loader = async function (SESSION_ID) {
  _this.glb.DEBUG_MODE = true;
  // $(_this.SESSION_OBJ[SESSION_ID].root_element).css(
  //   "background-color",
  //   "white"
  // ); // removed Aug 15,25

  const ret = await init_studio_websocket(SESSION_ID);
  if (ret.error) {
    $(_session.root_element).show();
    _this.func.UI.utils.progressScreen.show(SESSION_ID, 'Connection closed :(', null, true);
    throw 'Connection closed :(';
  }

  var data = ret.e;
  if (data.service === 'init_runtime_websocket') {
    await _this.func.UI.main.embed_loader(SESSION_ID);
  } else {
    var data = e;
    const call_embed = function () {
      _session.prog_id = data.data.id;

      console.log('call_embed', data.data.id);
      _this.func.UI.screen.call_embed(SESSION_ID, data.data.id);
    };

    if (data.service === 'run_program_command') {
      if (_this.func.UI.utils.get_url_attribute(SESSION_ID, 'prog') || (typeof live_preview_getCookie !== 'undefined' && live_preview_getCookie('prog_id') && data.data.id !== live_preview_getCookie('prog_id') && live_preview_getCookie('gtp_prog_dynamic') === '0' && _this.func.utils.get_device()))
        return; // in case live preview is a program specific mode

      try {
        throw 'run new program, terminate prev execution';
      } catch (ex) {
        call_embed();
      }
    }

    if (data.service === 'debug_command') {
      _this.func.utils.debug.read_command(data.data);
      return;
    }
  }
};

const init_studio_websocket = async function (SESSION_ID) {
  return new Promise(async function (resolve, reject) {
    var _session = _this.SESSION_OBJ[SESSION_ID];
    var app_id = _session.app_id;

    const set_connected = async function (stat) {
      var datasource_changes = {
        [0]: {
          ['data_system']: { SYS_GLOBAL_BOL_CONNECTED: stat },
        },
      };
      await func.datasource.update(SESSION_ID, datasource_changes);
    };

    const peer_actions_module = await func.common.get_module(SESSION_ID, 'xuda-peer-actions-module.esm.js');

    var preview_name = _session.url_params.preview_name || _session.opt.preview_name;

    // const peer = new Peer();
    const connect_peer = function () {
      const peer = new Peer(SESSION_ID, {
        host: `${_session.domain}`,
        path: '/peer',
        secure: true,
        config: {
          iceServers: [
            { urls: 'stun:stun.l.google.com:19302' },
            { urls: 'stun:stun.l.google.com:5349' },
            { urls: 'stun:stun1.l.google.com:3478' },
            { urls: 'stun:stun1.l.google.com:5349' },
            { urls: 'stun:stun2.l.google.com:19302' },
            { urls: 'stun:stun2.l.google.com:5349' },
            { urls: 'stun:stun3.l.google.com:3478' },
            { urls: 'stun:stun3.l.google.com:5349' },
            { urls: 'stun:stun4.l.google.com:19302' },
            { urls: 'stun:stun4.l.google.com:5349' },
          ],
        },
      });
      STUDIO_PEER = peer;

      peer.on('open', function (peer_id) {
        var peer_token = _session.gtp_token;
        const connect_peer = function () {
          if ($('.loader').length) {
            $('.loader_msg').html('Awaiting connection to Xuda Studio');
          }

          const conn = peer.connect(peer_token, {
            metadata: {
              url_params: _session.url_params,
              client_info: _session.SYS_GLOBAL_OBJ_CLIENT_INFO,
              preview_name,
              user_info: _session.login_info,
              live_token_id: _session.opt.live_token_id,
              live_token_stat: _session.opt.live_token_stat,
            },
          });
          conn.on('open', (e) => {
            STUDIO_PEER_CONN_SEND_METHOD = conn.send.bind(conn);
            STUDIO_PEER_CONN_ID = conn.connectionId;

            set_connected(1);

            conn.on('data', (data) => {
              // console.log(data);

              if (data.service === 'auth' && data.data.live_token_stat === 2) {
                if (SESSION_OBJ[SESSION_ID].system_ready) {
                  conn.send({
                    service: 'system_ready',
                    // id: peer_id,
                  });
                  if (STUDIO_PEER_CONN_MSG_QUEUE.length) {
                    for (let val of STUDIO_PEER_CONN_MSG_QUEUE) {
                      val.id = conn.connectionId;
                      conn.send(val);
                    }
                    STUDIO_PEER_CONN_MSG_QUEUE = [];
                  }
                }
                return connect_to_ws(peer_id);
              }

              peer_actions_module.peer_actions(SESSION_ID, STUDIO_PEER_CONN_SEND_METHOD, data);
            });

            conn.on('error', (e) => {
              console.error('peer conn', e);
            });

            conn.on('close', function (conn) {
              set_connected(0);
              $('body').removeClass('live_preview_online');
              if (_session.opt.live_token_stat === 2) {
                func.UI.utils.progressScreen.show(SESSION_ID, 'Connection lost. Attempting to reconnect...', false, true);

                console.log('Connection lost. Attempting to reconnect...');
                setTimeout(function () {
                  // connect_peer();
                  location.reload();
                }, 5000); // wait 5 seconds before trying to reconnect
              }
            });
          });
          conn.on('error', (e) => {
            console.error('peer conn', e);
          });
          // TBD
          peer.on('call', function (call) {
            const displayMediaStreamConstraints = {
              video: {
                cursor: 'always',
              },
              audio: true,
              preferCurrentTab: true,
            };

            const success = function (stream) {
              call.answer(stream);
            };

            const error = function (error) {
              call.answer(error);
            };

            if (navigator.mediaDevices.getDisplayMedia) {
              navigator.mediaDevices.getDisplayMedia(displayMediaStreamConstraints).then(success).catch(error);
            } else {
              navigator.getDisplayMedia(displayMediaStreamConstraints).then(success).catch(error);
            }

            // try {
            //   navigator.mediaDevices
            //     .getDisplayMedia( displayMediaStreamConstraints)
            //     .then(function (stream) {
            //       call.answer(stream); // Answer the call with an A/V stream.
            //     });
            // } catch (err) {
            //   call.answer();
            // }
          });
          // connect_to_ws(peer_id); // temp to remove
        };
        connect_peer();
      });
      peer.on('error', function (e) {
        if ($('.loader').length) {
          $('.loader_msg').html('Session has expired. Please renew the token session in Studio and reload.');
        }
      });

      // peer.on("call", function (call) {
      //   const displayMediaStreamConstraints = {
      //     video: {
      //       cursor: "always",
      //     },
      //     audio: true,
      //     preferCurrentTab: true,
      //   };

      //   const success = function (stream) {
      //     call.answer(stream);
      //   };

      //   const error = function (error) {
      //     call.answer(error);
      //   };

      //   if (navigator.mediaDevices.getDisplayMedia) {
      //     navigator.mediaDevices
      //       .getDisplayMedia(displayMediaStreamConstraints)
      //       .then(success)
      //       .catch(error);
      //   } else {
      //     navigator
      //       .getDisplayMedia(displayMediaStreamConstraints)
      //       .then(success)
      //       .catch(error);
      //   }

      //   // try {
      //   //   navigator.mediaDevices
      //   //     .getDisplayMedia( displayMediaStreamConstraints)
      //   //     .then(function (stream) {
      //   //       call.answer(stream); // Answer the call with an A/V stream.
      //   //     });
      //   // } catch (err) {
      //   //   call.answer();
      //   // }
      // });
      /////////////////////////////////
    };
    const broadcast_channel = function () {
      const channel = new BroadcastChannel(_session.gtp_token);

      STUDIO_PEER = channel;

      channel.onmessage = (event) => {
        if (event.data.session_id !== SESSION_ID) return;

        // if (event.data.service === "ping") {
        //   return channel.postMessage({
        //     service: "pong",
        //     session_id: SESSION_ID,
        //   });
        // }

        peer_actions_module.peer_actions(SESSION_ID, STUDIO_PEER_CONN_SEND_METHOD, event.data);
      };

      channel.postMessage({
        service: 'connection',
        metadata: {
          url_params: _session.url_params,
          client_info: _session.SYS_GLOBAL_OBJ_CLIENT_INFO,
          preview_name,
          user_info: _session.login_info,
          session_id: SESSION_ID,
        },
      });
      STUDIO_PEER_CONN_SEND_METHOD = channel.postMessage.bind(channel);
      set_connected(1);

      window.addEventListener('onCloseWindow', (event) => {
        console.log('onCloseWindow');
        channel.postMessage({
          service: 'disconnected',
          session_id: SESSION_ID,
          type: 'onCloseWindow',
        });
      });

      window.addEventListener('beforeunload', (event) => {
        console.log('beforeunload');
        channel.postMessage({
          service: 'disconnected',
          session_id: SESSION_ID,
          type: 'beforeunload',
        });
      });
      window.addEventListener('unload', (event) => {
        console.log('beforeunload');
        channel.postMessage({
          service: 'disconnected',
          session_id: SESSION_ID,
          type: 'unload',
        });
      });

      // return connect_to_ws(SESSION_ID);
      resolve({ e: { service: 'init_runtime_websocket' } });
    };

    const connect_to_ws = function (peer_id) {
      try {
        const url = 'https://' + _session.domain;

        STUDIO_WEBSOCKET = io(url, {
          secure: true,
          reconnection: _this.glb.debug_js ? false : true,
          // reconnectionDelay:10000,
          // reconnectionDelayMax: 30000,
          rejectUnauthorized: false,
          path: '/ws/socket.io',
        });

        const ws_data = {
          service: 'init',
          id: peer_id,
          uid: _session.USR_OBJ._id,
          source: 'runtime',
          app_id: app_id,
          preview_name: preview_name,
          client_info: _session.SYS_GLOBAL_OBJ_CLIENT_INFO,
          session_id: SESSION_ID,
          gtp_token: _session.gtp_token,
          app_token: _session.app_token,
        };

        STUDIO_WEBSOCKET.on('connect', () => {
          STUDIO_WEBSOCKET_CONNECTION_ID = STUDIO_WEBSOCKET.id;

          STUDIO_WEBSOCKET.emit('join-room', ws_data);
          resolve({ e: { service: 'init_runtime_websocket' } });
        });

        STUDIO_WEBSOCKET.on('studio-connected', (data) => {
          if (!data.reconnected) {
            STUDIO_WEBSOCKET.emit('init-studio', {
              ...ws_data,
              reconnected: true,
            });
          }
          $(_session.root_element).addClass('live_preview_connected');
          // $("body").addClass("live_preview_connected");
        });
        STUDIO_WEBSOCKET.on('user-disconnected', (data) => {
          $(_session.root_element).removeClass('live_preview_connected');
          $(_session.root_element).removeClass('live_preview_online');
        });

        STUDIO_WEBSOCKET.on('disconnect', () => {
          console.log('disconnect', STUDIO_WEBSOCKET.id); // undefined
        });

        STUDIO_WEBSOCKET.io.on('reconnect', (attempt) => {
          console.log('reconnect', attempt);
        });
      } catch (e) {
        // connection to ws broken
        resolve(true);
      }
    };

    if (_session.local_live_preview === 'true') {
      return broadcast_channel();
    }

    connect_peer();
  });
};

export const send_STUDIO_WEBSOCKET = function (SESSION_ID, service) {
  var _session = _this.SESSION_OBJ[SESSION_ID];
  const data = {
    service: service,
    data: _session.USR_OBJ,
    id: STUDIO_PEER_CONN_ID,
    uid: _session.USR_OBJ._id,
    source: 'runtime',
    app_id: _session.app_id,
    gtp_token: _session.gtp_token,
    app_token: _session.app_token,
  };
  if (!STUDIO_PEER_CONN_SEND_METHOD) {
    STUDIO_PEER_CONN_MSG_QUEUE.push(data);
    return;
  }
  STUDIO_PEER_CONN_SEND_METHOD(data);
};

func.UI.screen.live_preview_hot_module_reload = async function (SESSION_ID, doc) {
  // const $elm = func.UI.utils.find_in_element_data(
  //   "xuData",
  //   $(SESSION_OBJ[SESSION_ID].root_element),
  //   "prog_id",
  //   doc._id
  // );

  // console.log($elm);
  const $elm = func.UI.utils.find_in_element_data('xuPanelData', $(SESSION_OBJ[SESSION_ID].root_element), 'parent_element_ui_id');
  var panels_obj = {};

  if (!$elm.length) {
    // refresh screens
    for await (const [key, val] of Object.entries(SESSION_OBJ[SESSION_ID].DS_GLB)) {
      if (val.prog_id === doc._id) {
        await func.action.execute(SESSION_ID, 'act_reload', val);
        break;
      }
    }
    return;
  }
  // refresh panels
  for await (const [elem_key, elem_val] of Object.entries($elm)) {
    if (elem_key === 'length') break;
    var $div = $(elem_val);
    let xuData = $div.data().xuData;

    if (!$div.data().xuPanelData) continue;

    let dsSession = xuData.paramsP.dsSessionP;
    var _session = SESSION_OBJ[SESSION_ID];
    let _ds = _session?.DS_GLB[dsSession];
    if (!_ds) continue;

    if (_ds.prog_id !== doc._id) {
      continue;
    }

    const parent_element_ui_id = $div.data().xuPanelData.parent_element_ui_id;
    if (!panels_obj[parent_element_ui_id]) {
      panels_obj[parent_element_ui_id] = {
        _ds,
        $div,
        ids: [],
      };
    }
    panels_obj[parent_element_ui_id].ids.push($div.attr('xu-ui-id'));
  }

  for await (const [parent_element_ui_id, panel_val] of Object.entries(panels_obj)) {
    var $div_elm = panel_val.$div
      .parent()
      .parent()
      .find("[xu-ui-id='" + parent_element_ui_id + "']");

    // restore original panel
    try {
      const $org_panel = panel_val.$div.data().xuPanelData.$panel_div; //panel_val.$div.clone(true);

      const new_$div = await func.UI.screen.render_ui_tree(
        SESSION_ID,
        $div_elm,
        _.cloneDeep(panel_val.$div.data().xuPanelData.node),
        {},
        $org_panel.data().xuData.paramsP,
        null,
        null,
        $org_panel.data().xuData.key,
        null,
        $org_panel.data().xuData.parent_node,
        null,
        $org_panel.data().xuData.$root_container,
      );
      // remove old panel content
      $.each(panel_val.ids, async function (key, val) {
        $("[xu-ui-id='" + val + "']").remove();
      });
    } catch (error) {
      debugger;
    }
  }

  return panels_obj;
};
