/**
 * Teambuilder panel
 *
 * @author Guangcong Luo <guangcongluo@gmail.com>
 * @license AGPLv3
 */

import { PS, PSRoom, type RoomID, type Team } from "./client-main";
import { PSPanelWrapper, PSRoomPanel } from "./panels";
import { PSTeambuilder, TeamBox } from "./panel-teamdropdown";
import { Dex, PSUtils, toID, type ID } from "./battle-dex";
import { Teams } from "./battle-teams";
import { BattleLog } from "./battle-log";
import preact from "../js/lib/preact";
import { TeamEditorState } from "./battle-team-editor";

class PSTextarea extends preact.Component<{ initialValue?: string, name?: string }> {
	updateSize = () => {
		const textbox = this.base!.querySelector('textarea')!;
		const textboxTest = this.base!.querySelector<HTMLTextAreaElement>('textarea.heighttester')!;
		textboxTest.style.width = `${textbox.offsetWidth}px`;
		textboxTest.value = textbox.value;
		textbox.setAttribute('data-changed', textbox.value === this.props.initialValue ? '' : '1');
		const newHeight = Math.max(textboxTest.scrollHeight + 40, 50);
		textbox.style.height = `${newHeight}px`;
	};
	override componentDidMount(): void {
		const textbox = this.base!.querySelector('textarea')!;
		if (this.props.initialValue) {
			textbox.value = this.props.initialValue;
		}
		this.updateSize();
		window.addEventListener('resize', this.updateSize);
	}
	override componentWillUnmount(): void {
		window.removeEventListener('resize', this.updateSize);
	}
	override render() {
		return <div style="position:relative">
			<textarea
				name={this.props.name} class="textbox" onInput={this.updateSize} onKeyUp={this.updateSize}
				style="box-sizing:border-box;width:100%;resize:none"
			/>
			<div><textarea
				class="textbox heighttester"
				style="box-sizing:border-box;resize:none;height:50px;visibility:hidden;position:absolute;left:-200px"
			/></div>
		</div>;
	}
}

class TeambuilderRoom extends PSRoom {
	readonly DEFAULT_FORMAT = Dex.modid;

	/**
	 * - `""` - all
	 * - `"gen[NUMBER][ID]"` - format folder
	 * - `"gen[NUMBER]"` - uncategorized gen folder
	 * - `"[ID]/"` - folder
	 * - `"/"` - not in folder
	 */
	curFolder = '';
	curFolderKeep = '';
	searchTerms: string[] = [];
	exportMode: boolean | 'partial' = false;
	exportCode: string | null = null;

	override clientCommands = this.parseClientCommands({
		'newteam'(target) {
			const isBox = ` ${target} `.includes(' box ');
			if (` ${target} `.includes(' bottom ')) {
				PS.teams.push(this.createTeam(null, isBox));
			} else {
				PS.teams.unshift(this.createTeam(null, isBox));
			}
			PS.teams.save();
			this.update(null);
		},
		'deleteteam'(target) {
			const team = PS.teams.byKey[target];
			if (!team) return this.errorReply(`Team not found: ${target}`);

			PS.teams.delete(team);
			PS.teams.save();
			this.update(null);
		},
		'copyteam'(target) {
			const team = PS.teams.byKey[target];
			if (!team) return this.errorReply(`Team not found: ${target}`);

			TeamEditorState.copyTeam(team);

			PS.update();
			this.update(null);
		},
		'pasteteamabove,moveteamabove'(target, cmd) {
			const team = PS.teams.byKey[target];
			if (target !== '-' && !team) return this.errorReply(`Team not found: ${target}`);

			const index = team ? PS.teams.list.indexOf(team) : PS.teams.list.length;
			const folder = this.curFolder?.endsWith('/') ? this.curFolder.slice(0, -1) : '';
			TeamEditorState.pasteTeam(index, cmd === 'moveteamabove', folder);
			PS.teams.save();

			PS.update();
			this.update(null);
		},
		'undeleteteam'() {
			PS.teams.undelete();
			PS.teams.save();
			this.update(null);
		},
		'backup'() {
			this.setExportMode(!this.exportMode);
			this.update(null);
		},
		'createfolder'(name) {
			if (name.includes('/') || name.includes('\\')) {
				PS.alert("Names can't contain slashes, since they're used as a folder separator.");
				name = name.replace(/[\\/]/g, '');
			}
			if (name.includes('|')) {
				PS.alert("Names can't contain the character |, since they're used for storing teams.");
				name = name.replace(/\|/g, '');
			}
			if (!name) return this.errorReply('Name required');

			this.curFolderKeep = `${name}/`;
			this.curFolder = `${name}/`;
			this.update(null);
		},
		'renamefolder'(name) {
			if (!name) return this.errorReply('New name required');
			if (!this.curFolder.endsWith('/')) return this.errorReply('Not in a folder');

			if (name.includes('/') || name.includes('\\')) {
				PS.alert("Names can't contain slashes, since they're used as a folder separator.");
				name = name.replace(/[\\/]/g, '');
			}
			if (name.includes('|')) {
				PS.alert("Names can't contain the character |, since they're used for storing teams.");
				name = name.replace(/\|/g, '');
			}

			const oldFolder = this.curFolder.slice(0, -1);
			for (const team of PS.teams.list) {
				if (team.folder !== oldFolder) continue;
				team.folder = name;
			}
			if (this.curFolderKeep === this.curFolder) this.curFolderKeep = `${name}/`;
			this.curFolder = `${name}/`;
			PS.teams.save();
			this.update(null);
		},
		'deletefolder'() {
			if (!this.curFolder.endsWith('/')) return this.errorReply('Not in a folder');

			const oldFolder = this.curFolder.slice(0, -1);
			for (const team of PS.teams.list) {
				if (team.folder !== oldFolder) continue;
				team.folder = '';
			}
			if (this.curFolderKeep === this.curFolder) this.curFolderKeep = '';
			this.curFolder = '';
			PS.teams.save();
			this.update(null);
		},
		'convertfoldertoprefix'() {
			if (!this.curFolder.endsWith('/')) return this.errorReply('Not in a folder');

			const oldFolder = this.curFolder.slice(0, -1);
			for (const team of PS.teams.list) {
				if (team.folder !== oldFolder) continue;
				team.folder = '';
				team.name = `${oldFolder} ${team.name}`;
			}
			if (this.curFolderKeep === this.curFolder) this.curFolderKeep = '';
			this.curFolder = '';
			PS.teams.save();
			this.update(null);
		},
	});
	override sendDirect(msg: string): void {
		PS.alert(`Unrecognized command: ${msg}`);
	}

	setExportMode(exportMode: boolean) {
		const partial = this.searchTerms.length || this.curFolder ? 'partial' : true;
		const newExportMode = exportMode ? partial : false;

		if (newExportMode === this.exportMode) return;
		this.exportMode = newExportMode;
		this.exportCode = null;
	}
	createTeam(copyFrom?: Team | null, isBox = false): Team {
		if (copyFrom) {
			return {
				name: `Copy of ${copyFrom.name}`,
				format: copyFrom.format,
				folder: copyFrom.folder,
				packedTeam: copyFrom.packedTeam,
				iconCache: null,
				isBox: copyFrom.isBox,
				key: '',
			};
		} else {
			const format = this.curFolder && !this.curFolder.endsWith('/') ? this.curFolder as ID : this.DEFAULT_FORMAT;
			const folder = this.curFolder.endsWith('/') ? this.curFolder.slice(0, -1) : '';
			return {
				name: `${isBox ? "Box" : "Untitled"} ${PS.teams.list.length + 1}`,
				format,
				folder,
				packedTeam: '',
				iconCache: null,
				isBox,
				key: '',
			};
		}
	}
	updateSearch = (value: string) => {
		if (!value) {
			this.searchTerms = [];
		} else {
			this.searchTerms = value.split(",").map(q => q.trim().toLowerCase());
		}
	};
	matchesSearch = (team: Team) => {
		if (this.searchTerms.length === 0) return true;
		const normalized = team.packedTeam.toLowerCase();
		return this.searchTerms.every(term => normalized.includes(term));
	};
}

class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
	static readonly id = 'teambuilder';
	static readonly routes = ['teambuilder'];
	static readonly Model = TeambuilderRoom;
	static readonly icon = <i class="fa fa-pencil-square-o" aria-hidden></i>;
	static readonly title = 'Teambuilder';
	clickFolder = (e: MouseEvent) => {
		const room = this.props.room;
		let elem = e.target as HTMLElement | null;
		let folder: string | null = null;
		while (elem) {
			if (elem.getAttribute('data-href')) {
				return;
			}
			if (elem.className === 'selectFolder') {
				folder = elem.getAttribute('data-value') || '';
				break;
			}
			if (elem.className === 'folderlist') {
				return;
			}
			elem = elem.parentElement;
		}
		if (folder === null) return;
		e.preventDefault();
		e.stopImmediatePropagation();
		if (folder === '++') {
			PS.prompt("Folder name?", { parentElem: elem, okButton: "Create" }).then(name => {
				name = (name || '').trim();
				if (!name) return;

				room.send(`/createfolder ${name}`, elem);
			});
			return;
		}
		room.curFolder = folder;
		this.forceUpdate();
	};
	addFormatFolder = (ev: Event) => {
		const room = this.props.room;
		const button = ev.currentTarget as HTMLButtonElement;
		const folder = toID(button.value);
		room.curFolderKeep = folder;
		room.curFolder = folder;
		button.value = '';
		this.forceUpdate();
	};
	/** undefined: not dragging, null: dragging a new team */
	getDraggedTeam(ev: DragEvent): Team | number | null {
		if (PS.dragging?.type === 'team') return PS.dragging.team;

		const dataTransfer = ev.dataTransfer;
		if (!dataTransfer) return null;

		PS.dragging = { type: '?' };
		console.log(`dragging: ${dataTransfer.types as any} | ${[...dataTransfer.files]?.map(file => file.name) as any}`);
		if (!dataTransfer.types.includes?.('Files')) return null;
		// MDN says files will be empty except on a Drop event, but the spec says no such thing
		// in practice, Chrome gives this info but Firefox doesn't
		if (dataTransfer.files[0] && !dataTransfer.files[0].name.endsWith('.txt')) return null;

		// We're dragging a file! It might be a team!
		PS.dragging = {
			type: 'team',
			team: 0,
			folder: null,
		};
		return PS.dragging.team;
	}
	dragEnterTeam = (ev: DragEvent) => {
		const draggedTeam = this.getDraggedTeam(ev);
		if (draggedTeam === null) return;

		const value = (ev.currentTarget as HTMLElement)?.getAttribute('data-teamkey');
		const team = value ? PS.teams.byKey[value] : null;
		if (!team || team === draggedTeam) return;

		const iOver = PS.teams.list.indexOf(team);
		if (typeof draggedTeam === 'number') {
			if (iOver >= draggedTeam) (PS.dragging as any).team = iOver + 1;
			(PS.dragging as any).team = iOver;
			this.forceUpdate();
			return;
		}

		const iDragged = PS.teams.list.indexOf(draggedTeam);
		if (iDragged < 0 || iOver < 0) return; // shouldn't happen

		PS.teams.list.splice(iDragged, 1);
		// by coincidence, splicing into iOver works in both directions
		// before: Dragged goes before Over, splice at i
		// after: Dragged goes after Over, splice at i - 1 + 1
		PS.teams.list.splice(iOver, 0, draggedTeam);
		this.forceUpdate();
	};
	dragEnterFolder = (ev: DragEvent) => {
		const value = (ev.currentTarget as HTMLElement)?.getAttribute('data-value') || null;
		if (value === null || PS.dragging?.type !== 'team') return;
		if (value === '++' || value === '') return;

		PS.dragging.folder = value;
		this.forceUpdate();
	};
	dragLeaveFolder = (ev: DragEvent) => {
		const value = (ev.currentTarget as HTMLElement)?.getAttribute('data-value') || null;
		if (value === null || PS.dragging?.type !== 'team') return;
		if (value === '++' || value === '') return;

		if (PS.dragging.folder === value) PS.dragging.folder = null;
		this.forceUpdate();
	};
	static extractDraggedTeam(ev: DragEvent): Promise<Team | null> | null {
		const file = ev.dataTransfer?.files?.[0];
		if (!file) return null;

		let name = file.name;
		if (name.slice(-4).toLowerCase() !== '.txt') {
			// PS.alert(`Your file "${file.name}" is not a valid team. Team files are ".txt" files.`);
			return null;
		}
		name = name.slice(0, -4);

		return file.text?.()?.then(result => {
			let sets;
			try {
				sets = Teams.import(result);
			} catch {
				PS.alert(`Your file "${file.name}" is not a valid team.`);
				return null;
			}
			let format = '';
			const bracketIndex = name.indexOf(']');
			let isBox = false;
			if (bracketIndex >= 0) {
				format = name.slice(1, bracketIndex);
				if (!format.startsWith('gen')) format = 'gen6' + format;
				if (format.endsWith('-box')) {
					format = format.slice(0, -4);
					isBox = true;
				}
				name = name.slice(bracketIndex + 1).trim();
			}
			return {
				name,
				format: format as ID,
				folder: '',
				packedTeam: Teams.pack(sets),
				iconCache: null,
				key: '',
				isBox,
			} satisfies Team;
		});
	}
	static addDraggedTeam(ev: DragEvent, folder?: string) {
		let index: number = (PS.dragging as any)?.team;
		if (typeof index !== 'number') index = 0;
		return this.extractDraggedTeam(ev)?.then(team => {
			if (!team) {
				return;
			}
			if (folder?.endsWith('/')) {
				team.folder = folder.slice(0, -1);
			} else if (folder) {
				team.format = folder as ID;
			}
			PS.teams.push(team);
			PS.teams.list.pop();
			PS.teams.list.splice(index, 0, team);
			PS.teams.save();
			PS.join('teambuilder' as RoomID);
			PS.update();
		});
	}
	dropFolder = (ev: DragEvent) => {
		const value = (ev.currentTarget as HTMLElement)?.getAttribute('data-value') || null;
		if (value === null || PS.dragging?.type !== 'team') return;
		if (value === '++' || value === '') return;

		PS.dragging.folder = null;
		let team = PS.dragging.team;

		if (typeof team === 'number') {
			TeambuilderPanel.addDraggedTeam(ev, value);
			return;
		}

		if (value.endsWith('/')) {
			team.folder = value.slice(0, -1);
		} else {
			team.format = value as ID;
		}
		PS.teams.save();
		ev.stopImmediatePropagation();
		this.forceUpdate();
	};
	static handleDrop(ev: DragEvent) {
		let draggingTeam = false;
		if (PS.dragging?.type === 'team' && typeof PS.dragging?.team === 'object') {
			PS.teams.save();
			draggingTeam = true;
		}
		return !!this.addDraggedTeam(ev, (PS.rooms['teambuilder'] as TeambuilderRoom)?.curFolder) || draggingTeam;
	}
	updateSearch = (ev: KeyboardEvent) => {
		const target = ev.currentTarget as HTMLInputElement;
		this.props.room.updateSearch(target.value);
		this.forceUpdate();
	};
	clearSearch = () => {
		const target = this.base!.querySelector<HTMLInputElement>('input[type="search"]');
		if (!target) return;
		target.value = '';
		this.props.room.updateSearch('');
	};
	renderFolder(value: string) {
		const { room } = this.props;
		const cur = room.curFolder === value;
		let children;
		const folderOpenIcon = cur ? 'fa-folder-open' : 'fa-folder';
		if (value.endsWith('/')) {
			// folder
			children = [
				<i class={`fa ${folderOpenIcon}${value === '/' ? '-o' : ''}`}></i>,
				value.slice(0, -1) || '(uncategorized)',
			];
		} else if (value === '') {
			children = [
				<em>(all)</em>,
			];
		} else if (value === '++') {
			children = [
				<i class="fa fa-plus" aria-hidden></i>,
				<em>(add folder)</em>,
			];
		} else {
			children = [
				<i class={`fa ${folderOpenIcon}-o`}></i>,
				value.slice(4) || '(uncategorized)',
			];
		}

		// folders were <div>s rather than <button>s because in theory it has
		// less weird interactions with HTML5 drag-and-drop (looking at Firefox)
		// modern browsers don't seem to have these bugs, so we're going to make
		// them buttons for now
		const active = (PS.dragging as any)?.folder === value ? ' active' : '';
		if (cur) {
			return <div
				class="folder cur" data-value={value}
				onDragEnter={this.dragEnterFolder} onDragLeave={this.dragLeaveFolder} onDrop={this.dropFolder}
			>
				<div class="folderhack3">
					<div class="folderhack1"></div><div class="folderhack2"></div>
					<button class={`selectFolder${active}`} data-value={value}>{children}</button>
				</div>
			</div>;
		}
		return <div
			class="folder" data-value={value}
			onDragEnter={this.dragEnterFolder} onDragLeave={this.dragLeaveFolder} onDrop={this.dropFolder}
		>
			<button class={`selectFolder${active}`} data-value={value}>{children}</button>
		</div>;
	}
	saveExport = (e: MouseEvent) => {
		const value = this.base!.querySelector<HTMLTextAreaElement>('textarea[name="import"]')?.value;
		if (!value) return alert('Textarea not found');
		if (this.props.room.exportMode !== true) return alert('Wrong export mode');

		const teams = PSTeambuilder.importTeamBackup(value);
		// const visibleTeams = this.visibleTeams();
		// alert(`${teams.length} teams imported, ${visibleTeams.length} teams visible now`);
		PS.teams.list = [];
		PS.teams.byKey = {};
		for (const team of teams) PS.teams.push(team);
		// TODO: say what changed

		const room = this.props.room;
		room.exportMode = false;
		PS.teams.save();
		room.update(null);
	};
	renameFolder = (ev: MouseEvent) => {
		const { room } = this.props;
		const oldFolder = room.curFolder.slice(0, -1);
		const elem = ev.currentTarget as HTMLElement;
		ev.stopImmediatePropagation();
		ev.preventDefault();
		PS.prompt(`Rename \`\`${oldFolder}\`\` to?`, { defaultValue: oldFolder, okButton: "Rename", parentElem: elem }).then(name => {
			name = (name || '').trim();
			if (!name) return;
			if (name === oldFolder) return;

			room.send(`/renamefolder ${name}`, elem);
		});
	};
	promptDeleteFolder = (ev: MouseEvent) => {
		const { room } = this.props;
		const oldFolder = room.curFolder.slice(0, -1);
		const elem = ev.currentTarget as HTMLElement;
		ev.stopImmediatePropagation();
		ev.preventDefault();
		PS.confirm(`Delete \`\`${oldFolder}\`\`? (doesn't delete teams)`, {
			okButton: "Delete", otherButtons: <button class="button" data-cmd="/closeand /inopener /convertfoldertoprefix">Convert to prefix</button>,
			parentElem: elem,
		}).then(result => {
			if (result) room.send(`/deletefolder`, elem);
		});
	};
	renderFolderList() {
		const room = this.props.room;
		// The folder list isn't actually saved anywhere:
		// it's regenerated anew from the team list every time.

		// (This is why folders you create will automatically disappear
		// if you leave them without adding anything to them.)

		const folderTable: { [folder: string]: 1 | undefined } = { '': 1 };
		const folders: string[] = [];
		for (const team of PS.teams.list) {
			const folder = team.folder;
			if (folder && !(`${folder}/` in folderTable)) {
				folders.push(`${folder}/`);
				folderTable[`${folder}/`] = 1;
				if (!('/' in folderTable)) {
					folders.push('/');
					folderTable['/'] = 1;
				}
			}

			const format = team.format || room.DEFAULT_FORMAT;
			if (!(format in folderTable)) {
				folders.push(format);
				folderTable[format] = 1;
			}
		}
		if (room.curFolderKeep.endsWith('/') || room.curFolder.endsWith('/')) {
			if (!('/' in folderTable)) {
				folders.push('/');
				folderTable['/'] = 1;
			}
		}
		if (!(room.curFolderKeep in folderTable)) {
			folderTable[room.curFolderKeep] = 1;
			folders.push(room.curFolderKeep);
		}
		if (!(room.curFolder in folderTable)) {
			folderTable[room.curFolder] = 1;
			folders.push(room.curFolder);
		}

		PSUtils.sortBy(folders, folder => [
			folder.endsWith('/') ? 10 : -parseInt(folder.charAt(3), 10),
			folder,
		]);

		let renderedFormatFolders = [
			<div class="foldersep"></div>,
			<div class="folder"><button
				name="format" value="" data-selecttype="teambuilder"
				class="selectFolder" data-href="/formatdropdown" onChange={this.addFormatFolder}
			>
				<i class="fa fa-plus" aria-hidden></i><em>(add format folder)</em>
			</button></div>,
		];

		let renderedFolders: preact.ComponentChild[] = [];

		/** 0 = folder, 1-9 = format generation */
		let gen = -1;
		for (let format of folders) {
			const newGen = format.endsWith('/') ? 0 : parseInt(format.charAt(3), 10);
			if (gen !== newGen) {
				gen = newGen;
				if (gen === 0) {
					renderedFolders.push(...renderedFormatFolders);
					renderedFormatFolders = [];
					renderedFolders.push(<div class="foldersep"></div>);
					renderedFolders.push(<div class="folder"><h3>Folders</h3></div>);
				} else {
					renderedFolders.push(<div class="folder"><h3>Gen {gen}</h3></div>);
				}
			}
			renderedFolders.push(this.renderFolder(format));
		}
		renderedFolders.push(...renderedFormatFolders);

		return <div class="folderlist" onClick={this.clickFolder}>
			<div class="folderlistbefore"></div>

			{this.renderFolder('')}
			{renderedFolders}
			<div class="foldersep"></div>
			{this.renderFolder('++')}

			<div class="folderlistafter"></div>
		</div>;
	}
	visibleTeams(teams?: Team[]): Team[];
	visibleTeams(teams: (Team | null)[]): (Team | null)[];
	visibleTeams(teams: (Team | null)[] = PS.teams.list): (Team | null)[] {
		const { room } = this.props;

		if (room.curFolder) {
			if (room.curFolder.endsWith('/')) {
				const filterFolder = room.curFolder.slice(0, -1);
				teams = teams.filter(team => !team || team.folder === filterFolder);
			} else {
				const filterFormat = room.curFolder;
				teams = teams.filter(team => !team || team.format === filterFormat);
			}
		}
		if (!room.searchTerms.length) return teams;

		const filteredTeams = teams.filter(team => !team || room.matchesSearch(team));
		return filteredTeams;
	}
	cancelClipboard = () => {
		TeamEditorState.clipboard = null;
		this.forceUpdate();
	};

	renderTeamPane() {
		const room = this.props.room;

		let teams: (Team | null)[] = PS.teams.list.slice();
		let isDragging = false;
		if (PS.dragging?.type === 'team' && typeof PS.dragging.team === 'number') {
			teams.splice(PS.dragging.team, 0, null);
			isDragging = true;
		} else if (PS.teams.deletedTeams.length) {
			const undeleteIndex = PS.teams.deletedTeams[PS.teams.deletedTeams.length - 1][1];
			teams.splice(undeleteIndex, 0, null);
		}

		let filterFolder: string | null = null;
		let filterFormat: string | null = null;
		let teamTerm = 'team';
		if (room.curFolder) {
			if (room.curFolder.endsWith('/')) {
				filterFolder = room.curFolder.slice(0, -1);
				teamTerm = 'team in folder';
			} else {
				filterFormat = room.curFolder;
				if (filterFormat !== Dex.modid) teamTerm = BattleLog.formatName(filterFormat) + ' team';
			}
		}

		const filteredTeams = this.visibleTeams(teams);

		if (room.exportMode) {
			return <div class="teampane">
				<p>
					<button data-cmd="/backup" class="button">
						<i class="fa fa-caret-left" aria-hidden></i> Back
					</button> {}
					{room.exportMode !== true && <button class="button" disabled>
						<i class="fa fa-save" aria-hidden></i> Save (not allowed for partial exports)
					</button>}
					{room.exportMode === true && <button onClick={this.saveExport} class="button">
						<i class="fa fa-save" aria-hidden></i> Save changes
					</button>}
				</p>
				<PSTextarea
					name="import" initialValue={(room.exportCode ??= PS.teams.packAll(filteredTeams.filter(Boolean) as Team[]))}
				/>
			</div>;
		}

		const clipboard = window.TeamEditorState ? TeamEditorState.clipboard : null;
		const clipboardTeams = clipboard?.teams;
		return <div class="teampane">
			{window.TeamEditorState && TeamEditorState.renderClipboard(this.cancelClipboard)}
			{filterFolder ? (
				<h2>
					<i class="fa fa-folder-open" aria-hidden></i> {filterFolder} {}
					<button class="button small" style="margin-left:5px" onClick={this.renameFolder}>
						<i class="fa fa-pencil" aria-hidden></i> Rename
					</button> {}
					<button class="button small" style="margin-left:5px" onClick={this.promptDeleteFolder}>
						<i class="fa fa-times" aria-hidden></i> Remove
					</button>
				</h2>
			) : filterFolder === '' ? (
				<h2><i class="fa fa-folder-open-o" aria-hidden></i> Teams not in any folders</h2>
			) : filterFormat ? (
				<h2><i class="fa fa-folder-open-o" aria-hidden></i> {filterFormat} <small>({teams.length})</small></h2>
			) : (
				<h2>All Teams <small>({teams.length})</small></h2>
			)}
			<p>
				<button data-cmd="/newteam" class="button big">
					<i class="fa fa-plus-circle" aria-hidden></i> New {teamTerm}
				</button> {}
				<button data-cmd="/newteam box" class="button">
					<i class="fa fa-archive" aria-hidden></i> New box
				</button>
				<input
					type="search" class="textbox" placeholder="Search teams"
					style="margin-left:5px;" onKeyUp={this.updateSearch}
				></input>
			</p>
			<ul class="teamlist">
				{!teams.length ? (
					<li><em>you have no teams lol</em></li>
				) : !filteredTeams.length && room.searchTerms.length ? (
					<li><em>you have no teams matching <code>{room.searchTerms.join(", ")}</code></em></li>
				) : !filteredTeams.length ? (
					<li><em>you have no teams in this folder</em></li>
				) : filteredTeams.map(team => team ? (
					<li
						key={team.key} onDragEnter={this.dragEnterTeam} data-teamkey={team.key}
						class={clipboardTeams?.[team.key] ? 'cur' : ''}
					>
						{clipboardTeams && <div>
							<button class="button notifying" data-cmd={`/pasteteamabove ${team.key}`}>
								<i class="fa fa-clipboard" aria-hidden></i> Paste copy here
							</button> {}
							<button class="button notifying" data-cmd={`/moveteamabove ${team.key}`} disabled={clipboard.readonly}>
								<i class="fa fa-arrow-right" aria-hidden></i> Move here
							</button>
						</div>}
						<TeamBox team={team} onClick={this.clearSearch} /> {}
						{clipboardTeams && !clipboardTeams[team.key] && <button data-cmd={`/copyteam ${team.key}`} class="option">
							<i class="fa fa-copy" aria-hidden></i> + Clipboard
						</button>}
						{clipboardTeams?.[team.key] && <button data-cmd={`/copyteam ${team.key}`} class="option">
							<i class="fa fa-times" aria-hidden></i> Deselect
						</button>}
						{!clipboardTeams && <button data-cmd={`/copyteam ${team.key}`} class="option" aria-label="Copy/move" title="Copy/move">
							<i class="fa fa-copy" aria-hidden></i>
						</button>} {}
						{!clipboardTeams && !team.uploaded && <button data-cmd={`/deleteteam ${team.key}`} class="option">
							<i class="fa fa-trash" aria-hidden></i> Delete
						</button>} {}
						{team.uploaded?.private ? (
							<i class="fa fa-cloud gray"></i>
						) : team.uploaded ? (
							<i class="fa fa-globe gray"></i>
						) : team.teamid ? (
							<i class="fa fa-plug gray"></i>
						) : (
							null
						)}
					</li>
				) : isDragging ? (
					<li key="dragging">
						<div class="team"></div>
					</li>
				) : (
					<li key="undelete">
						<button data-cmd="/undeleteteam" class="option">
							<i class="fa fa-undo" aria-hidden></i> Undo delete
						</button>
					</li>
				))}
				{clipboardTeams && <div>
					<button class="button notifying" data-cmd="/pasteteamabove -">
						<i class="fa fa-clipboard" aria-hidden></i> Paste copy here
					</button> {}
					<button class="button notifying" data-cmd="/moveteamabove -" disabled={clipboard.readonly}>
						<i class="fa fa-arrow-right" aria-hidden></i> Move here
					</button>
				</div>}
			</ul>
			<p>
				<button data-cmd="/newteam bottom" class="button">
					<i class="fa fa-plus-circle" aria-hidden></i> New {teamTerm}
				</button> {}
				<button data-cmd="/newteam box bottom" class="button">
					<i class="fa fa-archive" aria-hidden></i> New box
				</button>
			</p>
			<p>
				<button data-cmd="/backup" class="button">
					<i class="fa fa-file-code-o" aria-hidden></i> Backup
					{room.searchTerms.length ? ' search results' : room.curFolder ? ' folder' : ''}
				</button>
			</p>
		</div>;
	}
	override render() {
		const room = this.props.room;

		return <PSPanelWrapper room={room} noScroll>
			<div class="folderpane">
				{this.renderFolderList()}
			</div>
			{this.renderTeamPane()}
		</PSPanelWrapper>;
	}
}

PS.addRoomType(TeambuilderPanel);
