const filer = (svpath) => {
  const modal = $("#svfiler_modal").length ? $("#svfiler_modal") : $(
    `<div id="svfiler_modal" class="modal" tabindex="-1">`
      + `<div class="modal-dialog modal-lg">`
        + `<div id="result_form" class="modal-content novalidate">`
          + `<div class="modal-header">`
            + `<div class="input-group p-1">`
              + `<button class="btn btn-outline-secondary dropdown-toggle filer_server_bot" type="button" data-bs-toggle="dropdown" aria-expanded="false">Server</button>`
              + `<ul class="dropdown-menu filer_server"><li class="mb-3 p-3">`
                + `<div class="col-12">`
                  + `<div class="input-group">`
                    + `<label class="input-group-text text-decoration-underline"><span class="text-danger">*</span>host</label>`
                    + `<input name="filer_host" type="text" class="form-control filer_host" param_data_type="str" param_data_multi="false" required>`
                  + `</div>`
                + `</div>`
                + `<div class="col-12">`
                  + `<div class="input-group">`
                    + `<label class="input-group-text text-decoration-underline"><span class="text-danger">*</span>port</label>`
                    + `<input name="filer_port" type="text" class="form-control filer_port" param_data_type="int" param_data_multi="false" required>`
                  + `</div>`
                + `</div>`
                + `<div class="col-12">`
                  + `<div class="input-group">`
                    + `<label class="input-group-text text-decoration-underline"><span class="text-danger">*</span>password</label>`
                    + `<input name="filer_password" type="text" class="form-control filer_password" param_data_type="str" param_data_multi="false" required>`
                    + `<input name="filer_svname" type="hidden" class="filer_svname">`
                  + `</div>`
                + `</div>`
              + `</li><li><hr class="dropdown-divider"></li></ul>`
              + `<input type="text" class="form-control filer_address" aria-describedby="button-addon2">`
              + `<button class="btn btn-outline-secondary filer_address_bot" type="button" id="button-addon2">`
                + `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-right-circle" viewBox="0 0 16 16">`
                  + `<path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H4.5z"/>`
                + `</svg>`
              + `</button>`
            + `</div>`
            + `<button type="button" class="btn btn_window_stack">`
              + `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-window-stack" viewBox="0 0 16 16">`
                + `<path d="M4.5 6a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1ZM6 6a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1Zm2-.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Z"/>`
                + `<path d="M12 1a2 2 0 0 1 2 2 2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2 2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h10ZM2 12V5a2 2 0 0 1 2-2h9a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1Zm1-4v5a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8H3Zm12-1V5a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v2h12Z"/>`
              + `</svg>`
            + `</button>`
            + `<button type="button" class="btn btn_window">`
                + `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-window" viewBox="0 0 16 16">`
                  + `<path d="M2.5 4a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1zm2-.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0zm1 .5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z"/>`
                  + `<path d="M2 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H2zm13 2v2H1V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1zM2 14a1 1 0 0 1-1-1V6h14v7a1 1 0 0 1-1 1H2z"/>`
                + `</svg>`
            + `</button>`
            + `<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" style="margin-left: 0px;"></button>`
          + `</div>`
          + `<div class="modal-body row">`
            + `<ul class="tree-menu tree_ul overflow-auto border col-4" style="height:calc(100vh - 240px)"></ul>`
            + `<div class="file-list drop-area overflow-auto col-8 p-1" style="height:calc(100vh - 240px)"></div>`
            + `<div class="progress p-0 d-none" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">`
              + `<div class="progress-bar progress-bar-striped progress-bar-animated bg-success" style="width: 0%"></div>`
            + `</div>`
            + `<a class="filer_download d-none" href="#">.</a>`
          + `</div>`
          + `<div class="modal-footer">`
            + `<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>`
          + `</div>`
        + `</div>`
      + `</div>`
    + `</div>`
  );
  const loading = $("#loading").length ? $("#loading") : $(
    `<div id="loading" class="position-absolute top-0 start-0 w-100 h-100 d-none" style="background:rgba(0, 0, 0, 0.3);z-index:10000;">`
      + `<div class="text-center position-absolute top-50 start-50 w-100 translate-middle">`
        + `<div class="spinner-border text-light" role="status">`
          + `<span class="sr-only"></span>`
        + `</div>`
      + `</div>`
    + `</div>`
  );
  const show_loading = () => {loading.removeClass('d-none');}
  const hide_loading = () => {
    loading.addClass('d-none');
    modal.find('.progress').addClass('d-none');
  }
  const calc_size = (size) => {
    const kb = 1024
    const mb = Math.pow(kb, 2)
    const gb = Math.pow(kb, 3)
    const tb = Math.pow(kb, 4)
    let target = null
    let unit = 'B'
    if (size >= tb) {
      target = tb
      unit = 'TB'
    } else if (size >= gb) {
      target = gb
      unit = 'GB'
    } else if (size >= mb) {
      target = mb
      unit = 'MB'
    } else if (size >= kb) {
      target = kb
      unit = 'KB'
    }
    const res = target !== null ? Math.floor((size / target) * 100) / 100 : size
    return `${res} ${unit}`
  };
  // ツリー表示 ================================================================
  const tree = (svpath, current_ul_elem) => {
    show_loading();
    opt = get_server_opt();
    opt["mode"] = "client";
    opt["cmd"] = "file_list";
    opt["svpath"] = svpath;
    eel.exec_cmd("file_list", opt)().then(async res => {
      current_ul_elem.html('');
      if(res["warn"]) {
        alert(res["warn"]);
        hide_loading();
        return;
      }
      list_tree = res[0]['success'];
      hide_loading();
      // 左側ペイン
      Object.keys(list_tree).map((key) => {
        node = list_tree[key];
        if(!node['is_dir']) return;
        children = node['children'];
        current_li_elem = modal.find(`#${key}`);
        if (current_li_elem.length > 0) {
          current_li_elem.find('.folder-close').remove();
        } else {
          current_li_elem = $(`<li id="${key}" data_path="${node['path']}"/>`);
          current_ul_elem.append(current_li_elem);
        }
        current_a_elem = $(`<a href="#" class="folder-open">${node['name']}</a>`);
        current_li_elem.append(current_a_elem);
        mk_func = (_p, _e) => {return ()=>{
          tree(_p, _e);
          event.stopPropagation();
        }}
        current_a_elem.off("click").on("click", mk_func(node['path'], current_ul_elem));
        Object.keys(children).map((k, i) => {
          n = children[k];
          if(!n['is_dir']) return;
          ul_elem = $(`<ul class="tree_ul"/>`).append(`<li id="${k}" data_path="${n['path']}"><a href="#" class="folder-close">${n['name']}</a></li>`);
          current_li_elem.append(ul_elem);
          modal.find(`#${k}`).off("click");
          modal.find(`#${k}`).on("click", mk_func(n['path'], current_ul_elem));
        });
      });
      // 右側ペイン
      list_tree_keys = Object.keys(list_tree);
      if (list_tree_keys.length > 0) {
        node = list_tree[list_tree_keys[list_tree_keys.length-1]];
        modal.find(".filer_address").val(node['path']);
        table = $(`<table class="table table-bordered table-hover table-sm">`
            + `<thead class="table-dark bg-dark"><tr><th scope="col">-</th><th scope="col">name</th><th scope="col">size</th><th scope="col">last</th></tr></thead>`
          + `</table>`);
        table_body = $(`<tbody></tbody>`);
        modal.find('.file-list').html('');
        modal.find('.file-list').append(table);
        table.append(table_body);
        children = node['children'];
        if(children) {
          // ツリー表示関数の生成
          mk_tree = (_p, _e) => {return ()=>tree(_p, _e)}
          // 削除関数の生成
          mk_delete = (_p, _e, is_dir) => {return ()=>{
            if(confirm(`「${_p}」を削除しますか？${is_dir?"\n【注意】ディレクトリの場合は中身も削除されます。":""}`)) {
              remote = is_dir ? 'file_rmdir' : 'file_remove';
              show_loading();
              opt = get_server_opt();
              opt["mode"] = "client";
              opt["cmd"] = remote;
              opt["svpath"] = _p;
              eel.exec_cmd(remote, opt)().then(async res => {
                if(res["warn"]) {
                  alert(res["warn"]);
                  hide_loading();
                  return;
                }
                hide_loading();
                tree(res[0]["success"]["path"], _e);
              }).then(() => {
                hide_loading();
              }, (error) => {
                console.log(error);
                hide_loading();
              });
            }
          }};
          mk_blob = (base64) => {
            bin = atob(base64.replace(/^.*,/, ''));
            buffer = new Uint8Array(bin.length);
            for (i=0; i<bin.length; i++) buffer[i] = bin.charCodeAt(i);
            blob = new Blob([buffer.buffer], {type: 'application/octet-stream'});
            return blob;
          }
          // ダウンロード関数の生成
          mk_download = (_p) => {return ()=>{
            show_loading();
            opt = get_server_opt();
            opt["mode"] = "client";
            opt["cmd"] = "file_download";
            opt["svpath"] = _p;
            eel.exec_cmd("file_download", opt)().then(async res => {
              if(res["warn"]) {
                alert(res["warn"]);
                hide_loading();
                return;
              }
              blob = mk_blob(res[0]['success']['data']);
              link = modal.find('.filer_download');
              link.attr('download', res[0]['success']['name']);
              link.get(0).href = window.URL.createObjectURL(blob);
              link.get(0).click();
              URL.revokeObjectURL(link.get(0).href);
            }).then(() => {
              hide_loading();
            }, (error) => {
              console.log(error);
              hide_loading();
            });
          }};
          // フォルダ作成関数の生成
          mk_mkdir = (_p, _e, is_dir) => {return ()=>{
            _p = _p.substring(0, _p.lastIndexOf('/')+1);
            prompt_text = prompt("Enter a new folder name.");
            if(prompt_text) {
              show_loading();
              opt = get_server_opt();
              opt["mode"] = "client";
              opt["cmd"] = "file_mkdir";
              opt["svpath"] = `${_p}/${prompt_text}`;
              eel.exec_cmd("file_mkdir", opt)().then(async res => {
                if(res["warn"]) {
                  alert(res["warn"]);
                  hide_loading();
                  return;
                }
                hide_loading();
                tree(res[0]["success"]["path"], _e);
              }).then(() => {
                hide_loading();
              }, (error) => {
                console.log(error);
                hide_loading();
              });
            }
          }};
          // ファイルリストの生成
          mk_tr = (_p, _e, is_dir) => {
            png = is_dir ? 'folder-close.png' : 'file.png';
            dt = is_dir ? '-' : new Date(n['last']).toLocaleDateString("ja-JP", {
              year:"numeric", month:"2-digit", day:"2-digit", hour:"2-digit", minute:"2-digit", second:"2-digit"
            });
            tr = $(`<tr>`
                + `<td><img src="/assets/tree-menu/image/${png}"></td>`
                + `<td>`
                  + `<div class="droudown">`
                    + `<a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">${n['name']}</a>`
                    + `<ul class="dropdown-menu"/>`
                  + `</div>`
                + `</td>`
                + `<td class="text-end">${calc_size(n['size'])}</td>`
                + `<td class="text-end">${dt}</td>`
              + `</tr>`);
            if (is_dir) {
              tr.find('.dropdown-menu').append(`<li><a class="dropdown-item open" href="#">Open</a></li>`);
              tr.find('.dropdown-menu').append(`<li><a class="dropdown-item mkdir" href="#">Create Folder</a></li>`);
              tr.find('.dropdown-menu').append(`<li><a class="dropdown-item delete" href="#">Delete</a></li>`);
            } else {
              tr.find('.dropdown-menu').append(`<li><a class="dropdown-item download" href="#">Download</a></li>`);
              tr.find('.dropdown-menu').append(`<li><a class="dropdown-item mkdir" href="#">Create Folder</a></li>`);
              tr.find('.dropdown-menu').append(`<li><a class="dropdown-item delete" href="#">Delete</a></li>`);
            }
            tr.find('.open').off('click').on('click', mk_tree(_p, _e));
            tr.find('.delete').off('click').on('click', mk_delete(_p, _e, is_dir));
            tr.find('.mkdir').off('click').on('click', mk_mkdir(_p, _e, is_dir));
            tr.find('.download').off('click').on('click', mk_download(_p));
            return tr;
          };
          // ディレクトリを先に表示
          Object.keys(children).map((k, i) => {
            n = children[k];
            if(!n['is_dir'] || node['path']==n['path']) return;
            tr = mk_tr(n['path'], current_ul_elem, n['is_dir']);
            table_body.append(tr);
          });
          // ファイルを表示
          Object.keys(children).map((k, i) => {
            n = children[k];
            if(n['is_dir']) return;
            tr = mk_tr(n['path'], current_ul_elem, n['is_dir']);
            table_body.append(tr);
          });
        }
      }
    }).then(() => {
      hide_loading();
    }, (error) => {
      console.log(error);
      hide_loading();
    });
  }
  // サーバー一覧を取得 ========================================================
  const get_server_opt = () => {
    filer_host = modal.find('.filer_host').val();
    filer_port = modal.find('.filer_port').val();
    filer_password = modal.find('.filer_password').val();
    filer_svname = modal.find('.filer_svname').val();
    if (!filer_host) {
      filer_host = localStorage.getItem('filer_host');
      filer_host = filer_host ? filer_host : 'localhost';
      modal.find('.filer_host').val(filer_host);
    }
    if (!filer_port) {
      filer_port = localStorage.getItem('filer_port');
      filer_port = filer_port ? filer_port : 6379;
      modal.find('.filer_port').val(filer_port);
    }
    if (!filer_password) {
      filer_password = localStorage.getItem('filer_password');
      filer_password = filer_password ? filer_password : 'password';
      modal.find('.filer_password').val(filer_password);
    }
    if (!filer_svname) {
      filer_svname = localStorage.getItem('filer_svname');
      filer_svname = filer_svname ? filer_svname : 'server';
      modal.find('.filer_svname').val(filer_svname);
    }
    return {"host":filer_host, "port":filer_port, "password":filer_password, "svname":filer_svname};
  }
  const load_server_list = () => {
    show_loading();
    modal.find('.filer_svnames').remove();
    opt = get_server_opt();
    opt['mode'] = 'server';
    opt['cmd'] = 'list';
    delete opt['svname'];
    eel.exec_cmd("server_list", opt)().then(async res => {
      if(res["warn"]) {
        alert(res["warn"]);
        hide_loading();
        return;
      }
      if(res.length<=0 || !res[0][`success`]) {
        hide_loading();
        return;
      }
      res[0][`success`].forEach(elem => {
        a_elem = $(`<a class="dropdown-item" href="#" data-host="${opt['host']}" data-port="${opt['port']}" data-password="${opt['password']}" data-svname="${elem['svname']}">${elem['svname']} ( ${opt['host']}:${opt['port']} )</a>`);
        mk_func = (elem) => {return ()=>{
          modal.find('.filer_host').val(elem.attr('data-host'));
          modal.find('.filer_port').val(elem.attr('data-port'));
          modal.find('.filer_password').val(elem.attr('data-password'));
          modal.find('.filer_svname').val(elem.attr('data-svname'));
          localStorage.setItem('filer_host', elem.attr('data-host'));
          localStorage.setItem('filer_port', elem.attr('data-port'));
          localStorage.setItem('filer_password', elem.attr('data-password'));
          localStorage.setItem('filer_svname', elem.attr('data-svname'));
          tree("/", modal.find('.tree-menu'))
        }};
        a_elem.off("click").on("click", mk_func(a_elem));
        li_elem = $(`<li class="filer_svnames"></li>`).append(a_elem);
        modal.find('.filer_server').append(li_elem);
      });
      modal.find('.filer_server').find('.dropdown-item:first').click();
    }).then(() => {
      hide_loading();
    }, (error) => {
      console.log(error);
      hide_loading();
    });
  }
  // ファイルアップロード ========================================================
  const upload = async (event) => {
    show_loading();
    const progress = (_min, _max, _now, _text, _show, _cycle) => {
      prog_elem = modal.find('.progress');
      bar_elem = prog_elem.find('.progress-bar');
      if(_show) prog_elem.removeClass('d-none');
      else prog_elem.addClass('d-none');
      prog_elem.attr('aria-valuemin', _min);
      prog_elem.attr('aria-valuemax', _max);
      prog_elem.attr('aria-valuenow', _now);
      if (!_cycle) {
        par = Math.floor((_now / (_max-_min)) * 10000) / 100
        bar_elem.css('left', 'auto').css('width', `${par}%`);
        bar_elem.text(`${par}% ( ${_now} / ${_max} ) ${_text}`);
        clearTimeout(progress_handle);
      } else {
        maxwidth = prog_elem.css('width');
        maxwidth = parseInt(maxwidth.replace('px', ''));
        left = bar_elem.css('left');
        if (!left || left=='auto') left = 0;
        else left = parseInt(left.replace('px', ''));
        if (left > maxwidth) left = -200;
        left += 2;
        bar_elem.css('width', `200px`).css('position', 'relative').css('left', `${left}px`);
        bar_elem.text(`Server processing...`);
        var progress_handle = setTimeout(() => {
          if (!loading.is('.d-none')) progress(_min, _max, _now, _text, _show, _cycle);
        }, 20);
      }
    }
    // https://qiita.com/KokiSakano/items/a122bc0a1a368c697643
    const files = [];
    const searchFile = async (entry) => {
      // ファイルのwebkitRelativePathにパスを登録する
      if (entry.isFile) {
        const file = await new Promise((resolve) => {
          entry.file((file) => {
            Object.defineProperty(file, "webkitRelativePath", {
              // fullPathは/から始まるので二文字目から抜き出す
              value: entry.fullPath.slice(1),
            });
            resolve(file);
          });
        });
        files.push(file);
        // ファイルが現れるまでこちらの分岐をループし続ける
      } else if (entry.isDirectory) {
        const dirReader = entry.createReader();
        let allEntries = [];
        const getEntries = () =>
          new Promise((resolve) => {
            dirReader.readEntries((entries) => {
              resolve(entries);
            });
          });
        // readEntriesは100件ずつの取得なので、再帰で0件になるまで取ってくるようにする
        // https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryReader/readEntries
        const readAllEntries = async () => {
          const entries = await getEntries();
          if (entries.length > 0) {
            allEntries = [...allEntries, ...entries];
            await readAllEntries();
          }
        };
        await readAllEntries();
        for (const entry of allEntries) {
          await searchFile(entry);
        }
      }
    };
    const items = event.originalEvent.dataTransfer.items;
    const calcFullPathPerItems = Array.from(items).map((item) => {
      return new Promise((resolve) => {
        const entry = item.webkitGetAsEntry();
        if (!entry) {
          resolve;
          return;
        }
        resolve(searchFile(entry));
      });
    });
    await Promise.all(calcFullPathPerItems);

    //const files = event.originalEvent.dataTransfer.files;
    const formData = new FormData();
    Object.keys(files).map((key) => {
      formData.append('files', files[key], files[key].webkitRelativePath);
    });
    svpath = modal.find('.filer_address').val();
    // https://developer.mozilla.org/ja/docs/Web/API/fetch
    param = {method: 'POST', body: formData};
    opt = get_server_opt();
    $.ajax({ // fetchだとxhr.upload.onprogressが使えないため、$.ajaxを使用
      url: `/filer/upload?host=${encodeURI(opt['host'])}&port=${encodeURI(opt['port'])}&password=${encodeURI(opt['password'])}&svname=${encodeURI(opt['svname'])}&svpath=${encodeURI(svpath)}`,
      type: 'POST',
      processData: false,
      contentType: false,
      async: true,
      data: formData,
      xhr: function() {
        var xhr = $.ajaxSettings.xhr();
        xhr.upload.onprogress = function(e) {
          if (e.lengthComputable) {
            progress(0, e.total, e.loaded, '', true, e.total==e.loaded);
          }
        };
        return xhr;
      },
      success: function(data) {
        alert(data);
        tree(svpath, modal.find('.tree-menu'));
      },
      error: function(data) {
        console.log(data);
        alert(data);
        tree(svpath, modal.find('.tree-menu'));
      }
    });
  }
  // モーダル表示 ==============================================================
  modal.modal('show');
  modal.find('.btn_window_stack').click(function(){
    modal.find('.btn_window_stack').css('margin-left', '0px').hide();
    modal.find('.btn_window').css('margin-left', 'auto').show();
    modal.find('.modal-dialog').removeClass('modal-fullscreen');
  });
  modal.find('.btn_window').click(function(){
    modal.find('.btn_window_stack').css('margin-left', 'auto').show();
    modal.find('.btn_window').css('margin-left', '0px').hide();
    modal.find('.modal-dialog').addClass('modal-fullscreen');
  });
  modal.find('.btn_window_stack').css('margin-left', '0px').hide();
  modal.find('.btn_window').css('margin-left', 'auto').show();
  modal.find('.filer_address').val(svpath);
  modal.find('.filer_address_bot').click(()=>{
    tree(modal.find('.filer_address').val(), modal.find('.tree-menu'));
  })
  modal.find('.modal-dialog').draggable({cursor:"move"});
  modal.find('.drop-area').on('dragover', (event) => {
    modal.find('.drop-area').addClass('dragover');
    event.preventDefault();
  });
  modal.find('.drop-area').on('dragleave', (event) => {
    modal.find('.drop-area').removeClass('dragover');
    event.preventDefault();
  });
  modal.find('.drop-area').on('drop', (event) => {
    upload(event);
    modal.find('.drop-area').removeClass('dragover');
    event.preventDefault();
  });
  modal.find('.filer_host').on('change', (event) => {load_server_list();});
  modal.find('.filer_port').on('change', (event) => {load_server_list();});
  modal.find('.filer_password').on('change', (event) => {load_server_list();});
  
  $("body").append(loading);
  load_server_list();
}
