var projectName = "";
var projectPictures = [
  // {
  //   "filepath": "",
  //   "filename": "",
  //   "size": 0
  // }
];
var projectEditorData;
var bomAccessories = []; //like projectPictures structure
var assemblyAccessories = []; //like projectPictures structure
var otherAccessories = []; //like projectPictures structure
var profileName = "";
var profilePictures = [];
var profileEditorData;
var bakRequestData;
function deepCloneList(list) {
  return JSON.parse(JSON.stringify(list || []));
}
function safeDecode(value) {
  if (!value) return '';
  try {
    return decodeURIComponent(value);
  } catch (err) {
    return value;
  }
}

function getBase64ImageFormat(base64Str) {
  if (!isBase64String(base64Str)) return null;
  const trimmed = base64Str.trim();
  const dataUrlMatch = trimmed.match(/^data:(.*?);base64,/i);
  let payload = trimmed;
  if (dataUrlMatch) {
    const mime = (dataUrlMatch[1] || '').toLowerCase();
    payload = trimmed.slice(dataUrlMatch[0].length);
    if (mime.indexOf('image/png') >= 0) return 'png';
    if (mime.indexOf('image/jpeg') >= 0 || mime.indexOf('image/jpg') >= 0) return 'jpg';
  }
  const cleanPayload = payload.replace(/\s+/g, '');
  try {
    const header = atob(cleanPayload.slice(0, 16));
    const bytes = header.split('').map(ch => ch.charCodeAt(0));
    if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47) return 'png';
    if (bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[2] === 0xFF) return 'jpg';
  } catch (err) {
    return null;
  }
  return null;
}

function normalizeCoverName(value) {
  const decoded = safeDecode(value);
  if (!decoded) return '';
  const segments = decoded.split(/[/\\]/);
  return segments[segments.length - 1];
}
function normalizeRequestPayload(raw) {
  const safe = raw || {};
  const model = safe.model || {};
  const file = safe.file || {};
  const profile = safe.profile || {};
  return {
    model: {
      name: model.name || "",
      description: model.description || "",
      preview_img: deepCloneList(model.preview_img),
    },
    file: {
      BOM: deepCloneList(file.BOM),
      Assembly: deepCloneList(file.Assembly),
      Other: deepCloneList(file.Other),
    },
    profile: {
      name: profile.name || "",
      description: profile.description || "",
      preview_img: deepCloneList(profile.preview_img),
    },
  };
}
function getCurrentRequestData() {
  getProjectName();
  getProjectDescription();
  getProfileName();
  getProfilePictures();
  getProfileDescription();
  return {
    model: {
      name: encodeURIComponent(projectName || ""),
      description: encodeURIComponent(projectEditorData || ""),
      preview_img: deepCloneList(projectPictures),
    },
    file: {
      BOM: deepCloneList(bomAccessories),
      Assembly: deepCloneList(assemblyAccessories),
      Other: deepCloneList(otherAccessories),
    },
    profile: {
      name: encodeURIComponent(profileName || ""),
      description: encodeURIComponent(profileEditorData || ""),
      preview_img: deepCloneList(profilePictures),
    },
  };
}
const editorUploadRequests = new Map();
$(function () {
  TranslatePage();
  addAccessoryBtnListener();
  addPictureUploadListener(
    'projectImageInput',
    'imageList',
    projectPictures,
    ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/jpg'],
    4 * 1024 * 1024
  );
  addAccessoryUploadListener(
    'bom-input',
    'bom-list',
    bomAccessories,
    {
      '.xls': 10 * 1024 * 1024,
      '.xlsx': 10 * 1024 * 1024,
      '.pdf': 50 * 1024 * 1024
    },
    10
  );
  addAccessoryUploadListener(
    'assembly-guide-input',
    'assembly-list',
    assemblyAccessories,
    {
      '.jpg': 10 * 1024 * 1024,
      '.png': 10 * 1024 * 1024,
      '.pdf': 50 * 1024 * 1024
    },
    25
  );
  addAccessoryUploadListener(
    'other-input',
    'other-list',
    otherAccessories,
    {
      '.txt': 10 * 1024 * 1024,
    },
    10
  );
  addPictureUploadListener(
    'ProfileImageInput',
    'profileImageList',
    profilePictures,
    ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/jpg'],
    4 * 1024 * 1024
  );
  // TestProjectData.model.preview_img=[];
  // updateInfo(TestProjectData);
  RequestProjectInfo();
});
function isFormEmpty() {
  getProjectName();
  getProjectDescription();
  getProfileName();
  getProfilePictures();
  getProfileDescription();
  const noBasics = !projectName.trim() && !projectEditorData?.trim();
  const noProjectPics = projectPictures.length === 0;
  const noAttachments = bomAccessories.length === 0 &&
                         assemblyAccessories.length === 0 &&
                         otherAccessories.length === 0;
  const noProfileInfo = !profileName.trim() &&
                        !profileEditorData?.trim() &&
                        profilePictures.length === 0;
  return noBasics && noProjectPics && noAttachments && noProfileInfo;
}
function isChange() {
  if (!bakRequestData && isFormEmpty()) {
    return false;
  }
  const baseline = normalizeRequestPayload(bakRequestData);
  const current = normalizeRequestPayload(getCurrentRequestData());
  return JSON.stringify(baseline) !== JSON.stringify(current);
}
function saveInfo() {
  let modelData = {};
  getProjectName();
  if (!projectName) {
    showToast("The project name is empty.");
    return;
  }
  modelData["name"] = encodeURIComponent(projectName);
  if (projectPictures.length <= 0) {
    showToast("The project pictures is empty.");
    return;
  }
  modelData["preview_img"] = projectPictures;
  getProjectDescription();
  if (!projectEditorData) {
    showToast("The project description is empty.");
    return;
  }
  modelData["description"] = encodeURIComponent(projectEditorData);
  let fileData = {
    BOM: bomAccessories,
    Assembly: assemblyAccessories,
    Other: otherAccessories
  };
  getProfileName();
  getProfilePictures();
  getProfileDescription();
  let profileData = {
    name: encodeURIComponent(profileName),
    preview_img: profilePictures,
    description: encodeURIComponent(profileEditorData)
  };
  let updateData = {
    model: modelData,
    file: fileData,
    profile: profileData
  };
  var tSend={};
	tSend['sequence_id']=Math.round(new Date() / 1000);
	tSend['command']="update_3mf_info";
  tSend['model'] = updateData;
		
	SendWXMessage( JSON.stringify(tSend) );	
}

function updateInfo(p3MF) {
  projectName = DOMPurify.sanitize(decodeURIComponent(p3MF.model.name));
  projectPictures.length = 0;
  Array.prototype.push.apply(projectPictures, p3MF.model.preview_img || []);
  let projectCover = normalizeCoverName(p3MF.model.cover_img || "");
  for (let i = 0; i < projectPictures.length; i++) {
    const pictureName = normalizeCoverName(projectPictures[i].filename);
    if (projectCover && pictureName === projectCover){
      const [item] = projectPictures.splice(i, 1);
      projectPictures.unshift(item);
      break;
    }
  }
  projectEditorData = decodeURIComponent(p3MF.model.description) || '';
  bomAccessories.length = 0;
  Array.prototype.push.apply(bomAccessories, p3MF.file.BOM || []);
  assemblyAccessories.length = 0;
  Array.prototype.push.apply(assemblyAccessories, p3MF.file.Assembly || []);
  otherAccessories.length = 0;
  Array.prototype.push.apply(otherAccessories, p3MF.file.Other || []);
  profileName = DOMPurify.sanitize(decodeURIComponent(p3MF.profile.name));
  profilePictures.length = 0;
  Array.prototype.push.apply(profilePictures, p3MF.profile.preview_img || []);
  let profileCover = normalizeCoverName(p3MF.profile.cover_img || "");
  for (let i = 0; i < profilePictures.length; i++) {
    const pictureName = normalizeCoverName(profilePictures[i].filename);
    if (profileCover && pictureName === profileCover){
      const [item] = profilePictures.splice(i, 1);
      profilePictures.unshift(item);
      break;
    }
  }
  profileEditorData = decodeURIComponent(p3MF.profile.description) || '';
  setProjectName();
  setProjectPictrues();
  setProjectDescription();
  setAccessor();
  setProfileName();
  setProfilePictures();
  setProfileDescription();
}
function setProjectName() {
	$("#projectNameInput").val(projectName);
}
function getProjectName() {
	projectName = $("#projectNameInput").val();
}
function setProjectPictrues() {
  setPictures('imageList', projectPictures);
}
function getProjectPictures() {
  return projectPictures;
}
function setProjectDescription() {
  if (window.projectEditor) {
    projectEditor.setData(projectEditorData || '');
  }
}
function getProjectDescription() {
  if (window.projectEditor) {
    projectEditorData = projectEditor.getData();
  }
}
function setAccessor() {
  setAccessories('bom-list', bomAccessories);
  setAccessories('assembly-list', assemblyAccessories);
  setAccessories('other-list', otherAccessories);
}
function setProfileName() {
  $("#ProfileNameInput").val(profileName);
}
function getProfileName() {
  profileName = $("#ProfileNameInput").val();
}
function setProfilePictures() {
  setPictures('profileImageList', profilePictures);
}
function getProfilePictures() {
  return profilePictures;
}
function setProfileDescription() {
  if (window.profileEditor) {
    profileEditor.setData(profileEditorData || '');
  }
}
function getProfileDescription() {
  if (window.profileEditor) {
    profileEditorData = profileEditor.getData();
  }
}
//Pictures tool
function setPictures(id, picturesList) {
  let updateHtml = "";
  for (let i = 0; i < picturesList.length; i++) {
    let pic_filepath = picturesList[i].filepath;
    if(picturesList[i].base64) {
      pic_filepath = picturesList[i].base64;
    }
    if (i == 0) {
      let html = `<div class="imagePreview" data-index="${i}" style="background-image: url('${pic_filepath}')">
  <img src="img/img_del.svg" />
  <div class="modelCover">Model cover</div>
</div>`;
      updateHtml = html + updateHtml;
    }else {
      let html = `<div class="imagePreview" data-index="${i}" style="background-image: url('${pic_filepath}')">
  <img src="img/img_del.svg" />
  <div class="setModelCover">Set as cover</div>
</div>`;
      updateHtml += html;
    }
  }
  $(`#${id}`).html(updateHtml);
  $(`#${id}`).off('click', 'img');
  $(`#${id}`).on('click', 'img', function (event) {
    let index = parseInt($(this).parent().data('index'));
    removePictureAt(index, picturesList);
    setPictures(id, picturesList);
  });
  $(`#${id}`).off('click', '.setModelCover');
  $(`#${id}`).on('click', '.setModelCover', function (event) {
    let index = parseInt($(this).parent().data('index'));
    const [item] = picturesList.splice(index, 1);
    picturesList.unshift(item);
    setPictures(id, picturesList);
  });
}
function removePictureAt(index, picturesList) {
  if (index < 0 || index >= picturesList.length) {
    return;
  }
  picturesList.splice(index, 1);  
}
function addPictureUploadListener(inputId, showPictrueId, picturesList, allowTypes, maxSize, maxCount) {
  const input = document.getElementById(inputId);
  // input.removeEventListener('click');
  input.addEventListener('click', () => {
    input.value = '';
  });
  // input.removeEventListener('change');
  input.addEventListener('change', function (event) {
    const [file] = event.target.files;
    if (!file) return;
    // Validate file type
    if (allowTypes && !allowTypes.includes(file.type)) {
      showToast(GetCurrentTextByKey("t144"));
      return;
    }
    // Validate file size
    if (maxSize && file.size > maxSize) {
      showToast(GetCurrentTextByKey("t145"));
      return;
    }
    if (picturesList.length >= maxCount) {
      showToast(GetCurrentTextByKey("t146"));
      return;
    }
    uploadFileToCpp(file).then(result => {
      if (result && result.path) {
        fileToThumbnailBase64(file).then((base64) => {
          picturesList.push({
          "filepath": result.path,
          "filename": file.name,
          "size": file.size,
          "base64": base64
          });
          setPictures(showPictrueId, picturesList);
          // showToast('add file1'+projectPictures.length);
        });
        
      }
    }).catch(error => {
      showToast(GetCurrentTextByKey("t147"));
    });
  });
}
//accessories tool
function addAccessoryBtnListener() {
  $("#accessories-btn").on("click", function (e) {
    e.stopPropagation();
    $(".accessory-rule-wrapper").toggle();
  });
  $(document).on('click', function (e) {
  if (!$(e.target).closest('#accessories-btn, .accessory-rule-wrapper').length) {
    $('.accessory-rule-wrapper').hide();
  }
});1
}
function setAccessories(id, accessoriesList) {
  const $container = $(`#${id}`);
  $container.empty();
  if (accessoriesList.length > 0) {
    $container.prev().show();
  }else {
    $container.prev().hide();
  }
  for (let i = 0; i < accessoriesList.length; i++) {
    let acc_filepath = accessoriesList[i].filepath;
    let type = "";
    if (accessoriesList[i].type) {
      type = getFileTail(accessoriesList[i].type);
    }else {
      type = getFileType(acc_filepath);
    }
    let iconPath = `img/icon_${type}.svg`;
    let $attachment = $('<div>').addClass('attachment').attr('data-index', i);
    $attachment.data('path', acc_filepath || '');
    $attachment.append($('<img>').addClass('attachment-icon').attr('src', iconPath));
    $attachment.append(document.createTextNode(decodeURIComponent(accessoriesList[i].filename)));
    $attachment.append($('<img>').addClass('attachment-delete').attr('src', 'img/del.svg'));
    $container.append($attachment);
  }
  $container.prev().children('label').text(accessoriesList.length);
  $container.off('click', '.attachment-delete');
  $container.on('click', '.attachment-delete', function (event) {
    event.stopPropagation();
    let index = parseInt($(this).parent().data('index'));
    removeAccessoryAt(index, accessoriesList);
    setAccessories(id, accessoriesList);
  });
  $container.off('click', '.attachment');
  $container.on('click', '.attachment', function (event) {
    if ($(event.target).closest('.attachment-delete').length) return;
    const path = $(this).data('path');
    OnClickOpenFile(event, path);
  });
}
function removeAccessoryAt(index, accessoriesList) {
  if (index < 0 || index >= accessoriesList.length) {
    return;
  }
  accessoriesList.splice(index, 1);  
}
function getFileTail(filetype) {
  switch(filetype) {
    case 'application/pdf':
      return 'pdf';
    case 'text/plain':
      return 'txt';
    case 'image/jpeg':
      return 'jpg';
    case 'image/png':
      return 'png';
    case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
      return 'xcl';
    case 'application/vnd.ms-excel':
      return 'xcl';
    default:
      return '';
  }
}
function getFileType(filepath) {
  let parts = filepath.split('.');
  if (parts.length > 1) {
    switch (parts[parts.length - 1].toLowerCase()) {
      case 'pdf':
        return 'pdf';
      case 'txt':
        return 'txt';
      case 'jpg':
        return 'jpg';
      case 'jpet':
        return 'jpg';
      case 'png':
        return 'png';
      case 'xlsx':
      case 'xls':
        return 'xcl';

    }
  }
  return getBase64ImageFormat(filepath);
}
function addAccessoryUploadListener(inputId, showAccessoryId, accessoriesList, allowTypes, maxCount) {
  const input = document.getElementById(inputId);
  input.addEventListener('click', () => {
    input.value = '';
  });
  input.addEventListener('change', function (event) {
    const [file] = event.target.files;
    if (!file) return;
    const lowerName = file.name.toLowerCase();
    const matchedExt = Object.keys(allowTypes).find(ext => lowerName.endsWith(ext));
    if (!matchedExt) {
      showToast(GetCurrentTextByKey("t144"));
      return;
    }
    const maxSize = allowTypes[matchedExt];
    if (maxSize && file.size > maxSize) {
      showToast(GetCurrentTextByKey("t145"));
      return;
    }
    if (accessoriesList.length >= maxCount) {
      showToast(GetCurrentTextByKey("t146"));
      return;
    }
    uploadFileToCpp(file).then(result => {
      if (result && result.path) {
        accessoriesList.push({
          "filepath": result.path,
          "filename": file.name,
          "size": file.size,
          "type": file.type
        });
        setAccessories(showAccessoryId, accessoriesList);
      }
    }).catch(error => {
      showToast(GetCurrentTextByKey("t147"));
    });
  });
}
//common tool
function generateSequenceId() {
  return `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
}
function uploadFileToCpp(file) {
  return new Promise((resolve, reject) => {
    if (!(file instanceof File)) {
      reject(new Error('invalid file object'));
      return;
    }
    const reader = new FileReader();
    const sequenceId = generateSequenceId();
    reader.onload = () => {
      let result = reader.result;
      let base64Payload = '';
      if (typeof result === 'string') {
        const commaIndex = result.indexOf(',');
        base64Payload = commaIndex >= 0 ? result.substring(commaIndex + 1) : result;
      } else if (result instanceof ArrayBuffer) {
        const bytes = new Uint8Array(result);
        let binary = '';
        const chunk = 0x8000;
        for (let i = 0; i < bytes.length; i += chunk) {
          binary += String.fromCharCode.apply(null, bytes.subarray(i, i + chunk));
        }
        base64Payload = btoa(binary);
      } else {
        reject(new Error('unable to read file content'));
        return;
      }
      const message = {
        sequence_id: sequenceId,
        command: 'editor_upload_file',
        data: {
          filename: file.name || '',
          size: file.size || 0,
          type: file.type || '',
          base64: base64Payload
        }
      };
      editorUploadRequests.set(sequenceId, { resolve, reject });
      const canCallHost =
        typeof window !== 'undefined' &&
        typeof window.wx === 'object' &&
        typeof window.wx.postMessage === 'function';
      if (typeof SendWXMessage === 'function' && canCallHost) {
        try {
          SendWXMessage(JSON.stringify(message));
        } catch (error) {
          editorUploadRequests.delete(sequenceId);
          reject(error);
        }
      } else {
        editorUploadRequests.delete(sequenceId);
        const fallbackPath = URL.createObjectURL(file);
        resolve({
          path: fallbackPath,
          isMock: true
        });
      }
    };
    reader.onerror = () => {
      reject(reader.error || new Error('failed to read file'));
    };
    reader.readAsDataURL(file);
  });
}
function fileToThumbnailBase64(file, maxWidth = 200, quality = 0.85) {
  return new Promise((resolve, reject) => {
    if (!file || !file.type.startsWith('image/')) {
      reject(new Error('select a valid image file'));
      return;
    }
    const reader = new FileReader();
    reader.onload = e => {
      const img = new Image();
      img.crossOrigin = 'anonymous'; 
      img.src = e.target.result;
      img.onload = () => {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        const scale = Math.min(1, maxWidth / img.width);
        const width = img.width * scale;
        const height = img.height * scale;
        canvas.width = width;
        canvas.height = height;
        if (file.type === 'image/png' || file.type === 'image/webp') {
          ctx.clearRect(0, 0, width, height);
        } else {
          ctx.fillStyle = '#fff';
          ctx.fillRect(0, 0, width, height);
        }
        ctx.drawImage(img, 0, 0, width, height);
        const outputType = ['image/png', 'image/jpeg', 'image/webp'].includes(file.type)
          ? file.type
          : 'image/png';
        const base64 = canvas.toDataURL(outputType, quality);
        resolve(base64);
      };
      img.onerror = () => reject(new Error('invalid image file'));
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}
function handleEditorMessage(rawMessage) {
  let payload = rawMessage;
  if (typeof rawMessage === 'string') {
    try {
      payload = JSON.parse(rawMessage); 
    } catch (error) {
      return;
    }
  }
  if (!payload || typeof payload !== 'object') {
    return;
  }
  const command = payload.command;
  if (command === 'editor_upload_file_result') {
    const sequenceId = payload.sequence_id;
    if (!sequenceId || !editorUploadRequests.has(sequenceId)) {
      return;
    }
    const { resolve, reject } = editorUploadRequests.get(sequenceId);
    editorUploadRequests.delete(sequenceId);
    if (payload.error) {
      reject(new Error(payload.error));
      return;
    }
    resolve(payload.data || {});
    return;
  }
  if (command === 'save_project') {
    saveInfo();
    return;
  }
  if (command === 'discard_project') {
    window.location.href = 'index.html';
    return;
  }
  if (command === 'update_3mf_info_result') {
    if (payload.error) {
      showToast(payload.error);
      return;
    }
    bakRequestData = JSON.parse(JSON.stringify(getCurrentRequestData()));
    showToast(payload.message || 'Saved successfully.');
    window.location.href = "index.html";
  }
}
function OnClickOpenFile( evt, strFullPath ) {
  if (!strFullPath) return;
  if (evt && $(evt.target).closest('.attachment-delete').length) {
    return;
  }
  if(isBase64String(strFullPath)) {
    showBase64ImageLayer(strFullPath);
    return;
  }
	var tSend={};
	tSend['sequence_id']=Math.round(new Date() / 1000);
	tSend['command']="open_3mf_accessory";
	tSend['accessory_path']=strFullPath;
	
	SendWXMessage( JSON.stringify(tSend) );
	SendWXDebugInfo('----open accessory:  '+strFullPath);
}
function returnBtn() {
  if (isChange()) {
    confirmSave();
  }else {
    if (bakRequestData) {
      window.location.href = "index.html";
    }else {
      window.location.href = "black.html";
    }
    
  }
}
function confirmSave() {
  var tSend={};
	tSend['sequence_id']=Math.round(new Date() / 1000);
	tSend['command']="request_confirm_save_project";
		
	SendWXMessage( JSON.stringify(tSend) );	
}
function RequestProjectInfo()
{
	var tSend={};
	tSend['sequence_id']=Math.round(new Date() / 1000);
	tSend['command']="request_3mf_info";
		
	SendWXMessage( JSON.stringify(tSend) );		
}
function HandleStudio(pVal)
{
	let strCmd=pVal['command'];
	
	if(strCmd=='show_3mf_info')
	{
		updateInfo( pVal['model'] );
    bakRequestData = JSON.parse(JSON.stringify(getCurrentRequestData()));
	}
}

window.HandleEditor = handleEditorMessage;
