// Copyright (c) 2023 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


import React from 'react';
import { createStyles } from './WithStyles';

import Tone3000Dialog from './Tone3000Dialog';

import TextInfoDialog from './TextInfoDialog';
import Tone3000HelpDialog from './Tone3000HelpDialog';
import GuitarMLHelpDialog from './GuitarMlHelpDialog';
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
import LinkEx from './LinkEx';
import DraggableButtonBase from './DraggableButtonBase';
import CloseIcon from '@mui/icons-material/Close';
import { Theme } from '@mui/material/styles';
import CreateNewFolderIcon from '@mui/icons-material/CreateNewFolder';
import RenameDialog from './RenameDialog';
import Divider from '@mui/material/Divider';
import MenuItem from '@mui/material/MenuItem';
import Menu from '@mui/material/Menu';
import MoreIcon from '@mui/icons-material/MoreVert';
import { PiPedalModel, PiPedalModelFactory, FileEntry, BreadcrumbEntry, FileRequestResult } from './PiPedalModel';
import { isDarkMode } from './DarkMode';
import Button from '@mui/material/Button';
import ButtonEx from './ButtonEx';
import FileUploadIcon from '@mui/icons-material/FileUpload';
import FileDownloadIcon from '@mui/icons-material/FileDownload';
import AudioFileIcon from '@mui/icons-material/AudioFile';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import FolderIcon from '@mui/icons-material/Folder';
import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
import DialogActions from '@mui/material/DialogActions';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import IconButtonEx from './IconButtonEx';
import OldDeleteIcon from './OldDeleteIcon';
import Toolbar from '@mui/material/Toolbar';
import WithStyles from './WithStyles';
import { withStyles } from "tss-react/mui";
import CircularProgress from '@mui/material/CircularProgress';
import { pathConcat, pathParentDirectory, pathFileName, pathFileNameOnly, pathExtension } from './FileUtils'; './FileUtils';


import ResizeResponsiveComponent from './ResizeResponsiveComponent';
import { UiFileProperty } from './Lv2Plugin';
import Typography from '@mui/material/Typography';
import DialogEx from './DialogEx';
import UploadFileDialog from './UploadFileDialog';
import OkCancelDialog from './OkCancelDialog';
import HomeIcon from '@mui/icons-material/Home';
import FilePropertyDirectorySelectDialog from './FilePropertyDirectorySelectDialog';
import { getAlbumArtUri, getTrackTitle } from './AudioFileMetadata';


const ToobNamModelFileUrl = "http://two-play.com/plugins/toob-nam#modelFile";
const ToobMlModelFileUrl = "http://two-play.com/plugins/toob-ml#modelFile";

const AUTOSCROLL_TICK_DELAY = 30;
const AUTOSCROLL_THRESHOLD = 48;
const AUTOSCROLL_SCROLL_PER_SECOND = 500;
const AUTOSCROLL_SCROLL_RATE = AUTOSCROLL_SCROLL_PER_SECOND * AUTOSCROLL_TICK_DELAY / 1000;


interface Point {
    x: number;
    y: number;
}


const styles = (theme: Theme) => createStyles({
    secondaryText: {
        color: theme.palette.text.secondary
    },
});

const audioFileExtensions: { [name: string]: boolean } = {
    ".wav": true,
    ".flac": true,
    ".mp3": true,
    ".m4a": true,
    ".ogg": true,
    ".aac": true,
    ".au": true,
    ".snd": true,
    ".mid": true,
    ".rmi": true,
    ".mp4": true,
    ".aif": true,
    ".aifc": true,
    ".aiff": true,
    ".ra": true
};

function screenToClient(currentTarget: HTMLElement, e: React.PointerEvent | React.MouseEvent): Point {
    let rect = currentTarget.getBoundingClientRect();
    const x = e.clientX + rect.left;
    const y = e.clientY + rect.right;
    return { x: x, y: y };
}
function isAudioFile(filename: string) {
    let npos = filename.lastIndexOf('.');
    let nposSlash = filename.lastIndexOf('/');
    if (nposSlash >= npos) {
        return false;
    }
    let extension = filename.substring(npos);
    return audioFileExtensions[extension] !== undefined;
}

export interface FilePropertyDialogProps extends WithStyles<typeof styles> {
    open: boolean,
    fileProperty: UiFileProperty,
    instanceId: number,
    selectedFile: string,
    onOk: (fileProperty: UiFileProperty, selectedItem: string) => void,
    onApply: (fileProperty: UiFileProperty, selectedItem: string) => void,
    onCancel: () => void
};
export interface FilePropertyDialogState {
    reordering: boolean;
    loading: boolean;
    dragState: DragState | null;
    showProgress: boolean;
    fullScreen: boolean;
    selectedFile: string;
    selectedFileIsDirectory: boolean;
    selectedFileProtected: boolean;
    hasSelection: boolean;
    hasFileSelection: boolean;
    canDelete: boolean;
    fileResult: FileRequestResult;
    navDirectory: string;
    windowWidth: number;
    windowHeight: number;
    currentDirectory: string;
    isProtectedDirectory: boolean;
    columns: number;
    columnWidth: number;
    openUploadFileDialog: boolean;
    openConfirmDeleteDialog: boolean;
    confirmCopyDialogState: {
        fileName: string;
        oldFilePath: string;
        newFilePath: string;
    } | null;
    menuAnchorEl: null | HTMLElement;
    menuMultiselectAnchorEl: null | HTMLElement;
    newFolderDialogOpen: boolean;
    renameDialogOpen: boolean;
    moveDialogOpen: boolean;
    copyDialogOpen: boolean;
    initialSelection: string;
    previousSelection: string;
    multiSelect: boolean,
    selectedFiles: string[],
    openTone3000Dialog: boolean,
    openTone3000Help: boolean,
    openGuitarMlHelp: boolean,

    textFileName?: string;

};

interface DragState {
    activeItem: string;
    height: number;
    dragElement: HTMLElement;
    dragElementDy: number;
    from: number;
    to: number;
    lastMousePoint: Point;
};

export default withStyles(
    class FilePropertyDialog extends ResizeResponsiveComponent<FilePropertyDialogProps, FilePropertyDialogState> {

        getNavDirectoryFromFile(selectedFile: string, fileProperty: UiFileProperty): string {
            if (selectedFile === "") {
                return "";
            }
            return pathParentDirectory(selectedFile);
        }

        isTracksDirectory(): boolean {
            return this.state.currentDirectory.startsWith("/var/pipedal/audio_uploads/shared/audio/Tracks");
        }
        isFolderArtwork(filePath: string): boolean {
            if (!this.isTracksDirectory()) return false;
            let extension = pathExtension(filePath);
            return extension === ".png" || extension === ".jpg" || extension === ".jpeg";
        }

        constructor(props: FilePropertyDialogProps) {
            super(props);


            this.model = PiPedalModelFactory.getInstance();

            let selectedFile = props.selectedFile;
            this.state = {
                reordering: false,
                loading: false,
                dragState: null,
                showProgress: false,
                fullScreen: this.getFullScreen(),
                selectedFile: selectedFile,
                selectedFileProtected: true,
                windowWidth: this.windowSize.width,
                windowHeight: this.windowSize.height,
                selectedFileIsDirectory: false,
                navDirectory: this.getNavDirectoryFromFile(selectedFile, props.fileProperty),
                currentDirectory: "",
                hasSelection: false,
                hasFileSelection: false,
                canDelete: false,
                columns: 0,
                columnWidth: 1,
                fileResult: new FileRequestResult(),
                isProtectedDirectory: true,
                openUploadFileDialog: false,
                openConfirmDeleteDialog: false,
                confirmCopyDialogState: null,
                menuAnchorEl: null,
                menuMultiselectAnchorEl: null,
                newFolderDialogOpen: false,
                renameDialogOpen: false,
                moveDialogOpen: false,
                copyDialogOpen: false,
                initialSelection: this.props.selectedFile,
                previousSelection: this.props.selectedFile,
                multiSelect: false,
                selectedFiles: [],
                openTone3000Dialog: false,
                openTone3000Help: false,
                openGuitarMlHelp: false
            };
            this.requestScroll = true;
        }
        getFullScreen() {
            return document.documentElement.clientWidth < 450 || document.documentElement.clientHeight < 450;
        }

        hProgressTimeout: number | null = null;

        private scrollRef: HTMLDivElement | null = null;

        cancelProgressTimeout() {
            if (this.hProgressTimeout !== null) {
                window.clearTimeout(this.hProgressTimeout);
                this.hProgressTimeout = null;
            }

        }
        onScrollRef(element: HTMLDivElement | null) {
            this.scrollRef = element;
            this.maybeScrollIntoView();
        }
        private maybeScrollIntoView() {
            if (this.scrollRef !== null && this.state.fileResult.files.length !== 0 && this.mounted && this.requestScroll) {
                this.requestScroll = false;
                let options: ScrollIntoViewOptions = { block: "nearest" };
                options.block = "nearest";

                this.scrollRef.scrollIntoView(options);
            }
        }
        private mounted: boolean = false;
        private model: PiPedalModel;

        transformDragPoint(element: HTMLElement, point: Point): Point {
            // STUB: will have to deal with scroll offset at some point.
            return point;
        }

        private requestFiles(navPath: string) {
            if (!this.props.open) {
                return;
            }
            if (this.props.fileProperty.directory === "") {
                return;
            }
            this.setState({
                loading: true,
                showProgress: false,
                fileResult: new FileRequestResult(),
                hasFileSelection: false,
                hasSelection: false,
            });
            this.hProgressTimeout = window.setTimeout(() => {
                if (this.mounted) {
                    this.setState({ showProgress: true });
                }
            }, 1000);
            this.model.requestFileList2(navPath, this.props.fileProperty)
                .then((filesResult) => {
                    if (this.mounted) {
                        this.cancelProgressTimeout();
                        filesResult.files.splice(0, 0, { pathname: "", displayName: "<none>", isDirectory: false, isProtected: true });

                        let fileEntry = this.getFileEntry(filesResult.files, this.state.selectedFile);
                        this.setState({
                            loading: false,
                            showProgress: false,
                            isProtectedDirectory: filesResult.isProtected,
                            fileResult: filesResult,
                            hasSelection: !!fileEntry,
                            hasFileSelection: !!fileEntry && (!fileEntry.isDirectory) && (!fileEntry.isProtected),
                            selectedFileProtected: fileEntry ? fileEntry.isProtected : true,
                            navDirectory: navPath,
                            currentDirectory: filesResult.currentDirectory
                        });
                    }
                }).catch((error) => {
                    this.cancelProgressTimeout();
                    if (!this.mounted) return;
                    if (navPath !== "") // deleted sample directory maybe?
                    {
                        this.navigate("");
                    } else {
                        this.setState({
                            loading: false,
                            showProgress: false,
                            fileResult: new FileRequestResult(),
                            isProtectedDirectory: false,
                            navDirectory: "",
                            currentDirectory: ""
                        });
                        this.model.showAlert(error.toString())
                    }
                });
        }

        private lastDivRef: HTMLDivElement | null = null;

        longPressStartPoint: Point | null = null;

        maxPosition: number = 0;

        handleMultisSelectClose() {
            this.setState({
                multiSelect: false, selectedFiles: [], menuMultiselectAnchorEl: null
            });
        }


        handleLongPressStart(
            currentTarget: HTMLButtonElement,
            e: React.PointerEvent<HTMLButtonElement> | React.MouseEvent<HTMLButtonElement>,
            fileEntry: FileEntry): boolean {
            if (this.state.reordering) {

                if (!this.isTracksDirectory()) {
                    return false;
                }
                let element = currentTarget;
                let index = parseInt(element.getAttribute("data-position") as string, 10);
                if (this.listContainerElementRef) {
                    this.longPressStartPoint = screenToClient(currentTarget, e);
                    let dragState: DragState = {
                        activeItem: fileEntry.pathname,
                        dragElement: element,
                        height: element.clientHeight,
                        dragElementDy: 0,
                        from: index,
                        to: index,
                        lastMousePoint: { ...this.longPressStartPoint }
                    };
                    this.setState({ dragState: dragState });
                }

                return true;
            } else if (!this.state.multiSelect) {
                if (fileEntry.isProtected) {
                    return false;
                }
                this.setState(
                    {
                        multiSelect: true,
                        selectedFiles: [fileEntry.pathname],
                    }
                );
                return true;
            }
            return false;

        }
        dragTarget: HTMLButtonElement | null = null;


        updateReorderPosition(element: HTMLButtonElement, point: Point) {
            if (this.longPressStartPoint) {
                let dragDy = point.y - this.longPressStartPoint.y;
                let height = element.clientHeight;

                let dragFromPosition = parseInt(element.getAttribute("data-position") as string, 10);

                let elementOffset = dragFromPosition * height;
                let newOffset = elementOffset + dragDy;
                if (newOffset < 0) {
                    dragDy = -elementOffset;
                    newOffset = 0;
                }
                let maxOffset = (this.maxPosition - 1) * height;
                if (newOffset > maxOffset) {
                    dragDy = maxOffset - elementOffset;
                    newOffset = maxOffset;
                }
                let toPosition = Math.floor((newOffset + height / 2) / height);
                if (toPosition < 0) {
                    toPosition = 0;
                }
                if (toPosition > this.maxPosition) {
                    toPosition = this.maxPosition;
                }
                // console.log("drag from " + dragFromPosition + " to " + toPosition + " dy: " + dragDy);
                this.setState({
                    dragState: {
                        activeItem: element.getAttribute("data-pathname") || "",
                        dragElement: element,
                        height: height,
                        dragElementDy: dragDy,
                        from: dragFromPosition,
                        to: toPosition,
                        lastMousePoint: point
                    }
                });
            }
        }
        handleLongPressMove(e: React.PointerEvent<HTMLButtonElement>) {
            if (this.state.reordering) {
                if (!this.isTracksDirectory()) {
                    return;
                }
                if (this.listContainerElementRef) {
                    let element = e.target as HTMLButtonElement;
                    let point = screenToClient(e.currentTarget, e);
                    this.updateReorderPosition(element, point);
                    this.startAutoScroll()

                }
            }
        }
        handleReorderFiles(from: number, to: number) {

            // Update the local state with the new order so that we don't 
            // flash the old order while waiting for a server update.
            let oldFileResult = this.state.fileResult;
            let newFileResult = new FileRequestResult();
            newFileResult.files = oldFileResult.files.slice();
            newFileResult.isProtected = oldFileResult.isProtected;
            newFileResult.currentDirectory = oldFileResult.currentDirectory;
            newFileResult.breadcrumbs = oldFileResult.breadcrumbs.slice();

            let ixFrom = -1;
            let ixTo = -1;
            let ix = 0;
            for (let i = 0; i < newFileResult.files.length; ++i) {
                let fileEntry = newFileResult.files[i];
                if (fileEntry.metadata) {
                    if (ix === from) {
                        ixFrom = i;
                    }
                    if (ix === to) {
                        ixTo = i;
                    }
                    ++ix;
                }
            }
            if (ixTo === -1 || ixFrom === -1) {
                return;
            }
            if (ixTo == ixFrom) return;
            if (ixTo < ixFrom) {
                // move up.
                newFileResult.files.splice(ixTo, 0, newFileResult.files[ixFrom]);
                newFileResult.files.splice(ixFrom + 1, 1);
            } else {
                // move down.
                newFileResult.files.splice(ixTo + 1, 0, newFileResult.files[ixFrom]);
                newFileResult.files.splice(ixFrom, 1);
            }

            this.setState({ fileResult: newFileResult, dragState: null });

            // Now send the reorder request to the server.
            this.model.moveAudioFile(
                this.state.currentDirectory, from, to);
        }
        handleLongPressEnd(e: React.PointerEvent<HTMLButtonElement> | React.MouseEvent<HTMLButtonElement>, fileEntry: FileEntry) {
            if (this.state.reordering) {
                if (!this.isTracksDirectory()) {
                    return;
                }
                let dragState = this.state.dragState;
                if (dragState && dragState.to != dragState.from) {
                    this.handleReorderFiles(dragState.from, dragState.to);

                }
                // prevent onClick from firing.
                e.preventDefault();
                e.stopPropagation();

                this.setState({ dragState: null });
                this.stopAutoScroll();
            } else if (!this.state.multiSelect) {
                let selectedFile = fileEntry.pathname;
                if (selectedFile === "") {
                    return;
                }
                this.setState({
                    multiSelect: true,
                    selectedFiles: [selectedFile],
                });
                e.stopPropagation();
                e.preventDefault();
            }
        }
        onMeasureRef(div: HTMLDivElement | null) {
            this.lastDivRef = div;
            if (div) {
                let width = div.offsetWidth;
                if (width === 0) return;
                let columns = 1;
                let columnWidth = (width - 40) / columns;
                if (columns !== this.state.columns || columnWidth !== this.state.columnWidth) {
                    this.setState({ columns: columns, columnWidth: columnWidth });
                }
            }
        }
        onWindowSizeChanged(width: number, height: number): void {

            this.setState({
                fullScreen: this.getFullScreen(),
                windowWidth: width,
                windowHeight: height
            })
            if (this.lastDivRef !== null) {
                this.onMeasureRef(this.lastDivRef);
            }

        }

        private requestScroll: boolean = false;

        componentDidMount() {
            super.componentDidMount();
            this.mounted = true;
            this.requestFiles(this.state.navDirectory)
            this.requestScroll = true;
        }
        componentWillUnmount() {
            this.stopAutoScroll();
            this.cancelProgressTimeout();

            super.componentWillUnmount();
            this.mounted = false;
            this.lastDivRef = null;
        }
        componentDidUpdate(prevProps: Readonly<FilePropertyDialogProps>, prevState: Readonly<FilePropertyDialogState>, snapshot?: any): void {
            super.componentDidUpdate?.(prevProps, prevState, snapshot);
            if (prevProps.open !== this.props.open
            ) {
                if (this.props.open) {
                    let selectedFile = this.props.selectedFile;
                    let navDirectory = this.getNavDirectoryFromFile(selectedFile, this.props.fileProperty);
                    this.setState({
                        selectedFile: selectedFile,
                        selectedFileIsDirectory: false,
                        selectedFileProtected: true,
                        newFolderDialogOpen: false,
                        renameDialogOpen: false,
                        moveDialogOpen: false,
                        copyDialogOpen: false,
                        navDirectory: navDirectory
                    });
                    this.requestFiles(navDirectory)
                    this.requestScroll = true;
                }
            }

        }

        private isDirectory(path: string): boolean {
            for (var fileEntry of this.state.fileResult.files) {
                if (fileEntry.pathname === path) {
                    return fileEntry.isDirectory;
                }
            }
            return false;
        }
        private isFileInList(files: FileEntry[], file: string) {

            let hasSelection = false;
            if (file === "") return true;
            for (var listFile of files) {
                if (listFile.pathname === file) {
                    hasSelection = true;
                    break;
                }
            }
            return hasSelection;
        }
        private getFileEntry(files: FileEntry[], file: string): FileEntry | undefined {

            if (file === "") return undefined;
            for (var listFile of files) {
                if (listFile.pathname === file) {
                    return listFile;
                }
            }
            return undefined;
        }


        handleFileClick(event: React.MouseEvent<HTMLElement>, fileEntry: FileEntry) {
            if (this.state.reordering) {
                return;
            }
            if (event.ctrlKey || this.state.multiSelect) {
                if (fileEntry.isProtected) {
                    // <none> cannot be multiselected.
                    return;
                }
                if (this.state.multiSelect) {
                    let selectedFiles = this.state.selectedFiles.slice();
                    let ix = selectedFiles.indexOf(fileEntry.pathname);
                    if (ix === -1) {
                        selectedFiles.push(fileEntry.pathname);
                    } else {
                        selectedFiles.splice(ix, 1);
                    }
                    if (selectedFiles.length === 0) {
                        this.setState({
                            multiSelect: false,
                            selectedFiles: []
                        });
                    } else {
                        this.setState({
                            multiSelect: true,
                            selectedFiles: selectedFiles
                        });
                    }
                } else {
                    let selectedFiles = [fileEntry.pathname];
                    if (this.state.hasSelection) {
                        if (this.state.selectedFile !== "") {
                            selectedFiles.push(this.state.selectedFile);
                        }
                    }
                    this.setState({
                        multiSelect: true,
                        selectedFiles: selectedFiles
                    });
                }
                event.stopPropagation();
                event.preventDefault();
                return;
            }
            if (event.shiftKey) {
                event.stopPropagation();
                event.preventDefault();
                return;

            }
            this.requestScroll = true;
            if (!fileEntry.isDirectory) {
                if (!this.isFolderArtwork(fileEntry.pathname)) {
                    this.handleApply(this.props.fileProperty, fileEntry.pathname);
                }
            }
            this.setState({
                selectedFile: fileEntry.pathname,
                selectedFileIsDirectory: fileEntry.isDirectory,
                selectedFileProtected: fileEntry.isProtected,
                hasSelection: this.isFileInList(this.state.fileResult.files, fileEntry.pathname),
                hasFileSelection: !fileEntry.isDirectory && !fileEntry.isDirectory && !fileEntry.isProtected
            })
        }
        onDoubleClickValue(selectedFile: string) {
            if (this.state.reordering) {
                return;
            }
            this.openSelectedFile();
        }
        handleApply(fileProperty: UiFileProperty, selectedItem: string) {
            if (this.state.previousSelection == selectedItem) {
                return;
            }
            if (!this.isLicenseFile(selectedItem) && !this.isFolderArtwork(selectedItem)) {
                this.props.onApply(fileProperty, selectedItem);
            }
            this.setState({ previousSelection: selectedItem });
        }


        handleMenuOpen(event: React.MouseEvent<HTMLElement>) {
            this.setState({ menuAnchorEl: event.currentTarget });

        }
        handleMenuClose() {
            this.setState({ menuAnchorEl: null });
        }
        handleMenuMultiSelectOpen(event: React.MouseEvent<HTMLElement>) {
            this.setState({ menuMultiselectAnchorEl: event.currentTarget });

        }
        handleMenuMultiSelectClose() {
            this.setState({ menuMultiselectAnchorEl: null });
        }
        canDeleteEntry(fileEntry: FileEntry): boolean {
            return fileEntry.isProtected;
        }
        handleDelete() {
            if (this.state.selectedFileProtected && !this.state.multiSelect) {
                return;
            }
            this.setState({ openConfirmDeleteDialog: true });
        }
        handleDownloadFile() {
            if (this.state.selectedFileProtected || !this.state.hasFileSelection) {
                return;
            }
            let file = this.state.selectedFile;
            this.model.downloadAudioFile(file);
        }
        async handleConfirmDelete() {
            this.setState({ openConfirmDeleteDialog: false });
            let files: string[] = [];
            if (this.state.multiSelect) {
                files = this.state.selectedFiles;
            } else {
                if (this.state.selectedFileProtected) {
                    return;
                }
                files = [this.state.selectedFile];
            }
            let selectedFile = (!this.state.multiSelect && this.state.hasSelection) ? this.state.selectedFile : undefined;
            let newSelectedFiles = this.state.selectedFiles.slice();
            let multiSelect = this.state.multiSelect;
            let resultFiles = this.state.fileResult.files;

            try {
                for (let file of files) {
                    if (!multiSelect) {
                        if (selectedFile) {
                            let position = -1;
                            for (let i = 0; i < resultFiles.length; ++i) {
                                let file = resultFiles[i];
                                if (file.pathname === selectedFile) {
                                    position = i;
                                    break;
                                }
                            }
                            if (position >= 0 && position < resultFiles.length - 1) {
                                selectedFile = this.state.fileResult.files[position + 1].pathname;
                            } else if (position === this.state.fileResult.files.length - 1) {
                                if (position !== 0) {
                                    selectedFile = this.state.fileResult.files[position - 1].pathname;
                                } else {
                                    selectedFile = undefined;
                                }
                            } else {
                                selectedFile = undefined;
                            }
                        }
                    } else {
                        let ix = newSelectedFiles.indexOf(file);
                        if (ix >= 0) {
                            newSelectedFiles.splice(ix, 1);
                        }
                    }
                    await this.model.deleteUserFile(file);
                }
            } catch (e: any) {
                this.model.showAlert(e);
            }

            // update state for what we've done.
            if (!this.mounted) {
                return;
            }
            if (multiSelect) {
                if (newSelectedFiles.length === 0) {
                    this.setState({
                        multiSelect: false,
                        selectedFiles: [],
                    });
                } else {
                    this.setState({
                        multiSelect: true,
                        selectedFiles: newSelectedFiles,
                    });
                }
            } else {
                if (selectedFile) {
                    this.setState({
                        selectedFile: selectedFile
                    });
                }
            }
            this.requestFiles(this.state.navDirectory);
        }

        navigate(relativeDirectory: string) {
            this.requestFiles(relativeDirectory);
            this.setState({ navDirectory: relativeDirectory });
        }
        handleBreadcrumbNavigate(relativeDirectory: string) {
            if (!this.state.reordering && !this.state.multiSelect) {
                this.navigate(relativeDirectory);
            }
        }
        getCompactTrackTitle(fileEntry: FileEntry): string {
            let metadata = fileEntry.metadata;
            let title = getTrackTitle(fileEntry.pathname, metadata);
            if (!metadata) {
                return title;
            }

            if (metadata.album !== "") {
                title += " (" + metadata.album + ")";
            }
            return title;
        }


        getAlbumTitle(fileEntry: FileEntry): string {
            let artist = fileEntry.metadata?.artist || "";
            if (artist == "") {
                artist = fileEntry.metadata?.albumArtist || "";
            }
            let album = fileEntry.metadata?.album || "";
            let joiner = (artist !== "" && album !== "") ? " - " : "";
            return album + joiner + artist;
        }
        getTrackThumbnail(fileEntry: FileEntry): string {
            return getAlbumArtUri(this.model, fileEntry.metadata, fileEntry.pathname);
        }
        renderBreadcrumbs() {
            let breadcrumbs: React.ReactElement[] = [(
                <Button variant="text"
                    color="inherit"
                    key="h" onClick={() => { this.handleBreadcrumbNavigate(""); }}
                    style={{ textTransform: "none", flexShrink: 0 }}
                    startIcon={(<HomeIcon sx={{ mr: 0.6 }} fontSize="inherit" />)}
                >
                    <Typography style={{}} variant="body2" noWrap>Home</Typography>
                </Button>
            )
            ];

            for (let i = 1; i < this.state.fileResult.breadcrumbs.length - 1; ++i) {
                let breadcrumb: BreadcrumbEntry = this.state.fileResult.breadcrumbs[i];

                breadcrumbs.push((
                    <Typography key={"x" + (i)} variant="body2" style={{ marginTop: 6, marginBottom: 6 }} noWrap>/</Typography>

                )
                );
                breadcrumbs.push((
                    <Button variant="text"
                        key={"bc" + i}
                        color="inherit"
                        onClick={() => { this.handleBreadcrumbNavigate(breadcrumb.pathname) }}
                        style={{ minWidth: 0, textTransform: "none", flexShrink: 0 }}
                    >
                        <Typography variant="body2" style={{}} noWrap>{breadcrumb.displayName}</Typography>


                    </Button>
                ));
            }
            if (this.state.fileResult.breadcrumbs.length > 1) {
                let lastdirectory = this.state.fileResult.breadcrumbs[this.state.fileResult.breadcrumbs.length - 1];
                breadcrumbs.push((
                    <Typography key={"xLast"} variant="body2" style={{ userSelect: "none", marginTop: 6, marginBottom: 6 }} noWrap>/</Typography>

                )
                );
                breadcrumbs.push((
                    <div key={"bc-last"} style={{ flexShrink: 0, paddingLeft: 8, paddingRight: 8, paddingTop: 6, paddingBottom: 6, overflowX: "clip" }}>
                        <Typography noWrap
                            style={{}} variant="body2"
                            color="text.secondary"> {lastdirectory.displayName}</Typography>
                    </div>
                ));

            }
            return (
                <div>

                    <div aria-label="breadcrumb"
                        style={{ marginLeft: 16, marginBottom: 8, marginRight: 8, textOverflow: "", display: "flex", flexFlow: "row wrap", alignItems: "center", justifyContent: "start" }}>
                        {breadcrumbs}
                    </div>
                </div>
            );
        }

        getDefaultPath(): string {
            try {
                let storage = window.localStorage;
                let result = storage.getItem("fpDefaultPath");
                if (result) {
                    return result;
                }
            } catch (e) {

            }

            return this.state.navDirectory;
        }
        setDefaultPath(path: string) {
            try {
                let storage = window.localStorage;
                storage.setItem("fpDefaultPath", path);
            } catch (e) {

            }
        }
        hasSelectedFileOrFolder(): boolean {
            return this.state.hasSelection && this.state.selectedFile !== "";
        }
        getFileExtensionList(uiFileProperty: UiFileProperty): string {
            let result = "";
            for (var fileType of uiFileProperty.fileTypes) {
                if (fileType.fileExtension !== "" && fileType.fileExtension !== ".zip") {
                    if (result !== "") result = result + ",";
                    result += fileType.fileExtension;
                }
            }
            return result;
        }


        private getIcon(fileEntry: FileEntry, largeIcon: boolean) {
            let style = largeIcon
                ? {
                    flex: "0 0 auto", opacity: 0.7, width: 32, height: 32,
                    marginLeft: 16, marginRight: 24, marginTop: 16, marginBottom: 16,
                }
                : { flex: "0 0 auto", opacity: 0.7, marginRight: 8, marginLeft: 8 };




            if (fileEntry.pathname === "") {
                return (<InsertDriveFileOutlinedIcon style={style} />);
            }
            if (fileEntry.isDirectory) {
                return (
                    <FolderIcon style={style} />
                );
            }
            if (isAudioFile(fileEntry.pathname)) {
                return (<AudioFileIcon style={style} />);
            }
            return (<InsertDriveFileIcon style={style} />);
        }

        private listContainerElementRef: HTMLDivElement | null = null;
        private scrollContainerElementRef: HTMLDivElement | null = null;
        private autoScrollTimer: number | null = null;

        handleAutoScrollTick() {
            let dragState = this.state.dragState;
            if (!dragState) return;

            let scrollContainer = this.scrollContainerElementRef;
            if (!scrollContainer) {
                return;
            }
            let scrollBounds_ = scrollContainer.getBoundingClientRect();
            let scrollClientTop = scrollBounds_.top;
            let scrollClientBottom = scrollClientTop + scrollContainer.clientHeight;
            let dragBounds = dragState.dragElement?.getBoundingClientRect();

            let dy = 0;
            let y = scrollContainer.scrollTop;
            if (dragBounds.top < scrollClientTop + AUTOSCROLL_THRESHOLD) {
                dy = -AUTOSCROLL_SCROLL_RATE;

                y = scrollContainer.scrollTop + dy;
                if (y < 0) y = 0;
                dy = y - scrollContainer.scrollTop;
            } else if (dragBounds.bottom > scrollClientBottom - AUTOSCROLL_THRESHOLD) {
                dy = AUTOSCROLL_SCROLL_RATE;

                y = scrollContainer.scrollTop + dy;
                let maxScrollTop = scrollContainer.scrollHeight - scrollContainer.clientHeight;
                if (y > maxScrollTop) {
                    y = maxScrollTop;
                }
                dy = y - scrollContainer.scrollTop;
            } else {
                return;
            }
            if (this.longPressStartPoint === null) {
                return;
            }
            if (dy === 0) {
                return;
            }
            this.longPressStartPoint = {
                x: this.longPressStartPoint.x,
                y: this.longPressStartPoint.y - dy
            };
            let dragElementDy = dragState.lastMousePoint.y - this.longPressStartPoint.y;
            let originalY = dragState.from * dragState.height;
            let newY = originalY + dragElementDy;
            if (newY < 0) {
                newY = 0;
                dragElementDy = -originalY;
            }
            let maxDy = (this.maxPosition - 1) * dragState.height;
            if (newY > maxDy) {
                newY = maxDy;
                dragElementDy = maxDy - originalY;
            }
            let newTo = Math.floor((newY + dragState.height / 2) / dragState.height);


            let newDragState = { ...dragState };

            newDragState.dragElementDy = dragElementDy;
            newDragState.to = newTo;

            // console.log("autoscroll from " + dragState.from + " to " + newTo + " dy: " + dragElementDy);
            // avoid jitter.
            newDragState.dragElement.style.top = (newDragState.dragElementDy + 5) + "px";

            this.setState({ dragState: newDragState });
            scrollContainer.scrollTo({ left: 0, top: y, behavior: "instant" });
            this.startAutoScroll();

        }


        stopAutoScroll() {
            if (this.autoScrollTimer !== null) {
                clearTimeout(this.autoScrollTimer);
                this.autoScrollTimer = null;
            }
        }
        startAutoScroll() {
            this.stopAutoScroll();

            this.autoScrollTimer = setTimeout(
                () => { this.handleAutoScrollTick() }, AUTOSCROLL_TICK_DELAY);
        }

        handleTone3000Dialog(e: React.MouseEvent<HTMLButtonElement>) {
            e.stopPropagation();
            e.preventDefault();

            this.setState({ openTone3000Dialog: true });

        }


        handleGuitarMlHelp(e: React.MouseEvent<HTMLButtonElement>) {
            e.stopPropagation();
            e.preventDefault();

            this.setState({ openGuitarMlHelp: true });

        }


        handleTone3000Help(e: React.MouseEvent<HTMLButtonElement>) {
            e.stopPropagation();
            e.preventDefault();

            this.setState({ openTone3000Help: true });

        }

        render() {
            const isTracksDirectory = this.isTracksDirectory();
            const isToobNamModelFile = this.props.fileProperty.patchProperty === ToobNamModelFileUrl;
            const isToobMLModelFile = this.props.fileProperty.patchProperty === ToobMlModelFileUrl;

            const classes = withStyles.getClasses(this.props);
            let columnWidth = this.state.columnWidth;
            let okButtonText = "Select";
            if (this.state.hasSelection && this.state.selectedFileIsDirectory) {
                okButtonText = "Open";
            }
            let protectedDirectory = this.state.fileResult.isProtected;
            let protectedItem = true;
            if (this.state.hasSelection) {
                protectedItem = this.state.selectedFileProtected;
            }
            let canMove = this.hasSelectedFileOrFolder() && !protectedItem;
            let canRename = this.hasSelectedFileOrFolder() && !protectedItem && !isTracksDirectory;
            let canReorder = isTracksDirectory;
            let needsDivider = canMove || canRename || canReorder;
            let trackPosition = 0;
            let compactVertical = this.state.windowHeight < 700;
            let canSelectFile = this.state.hasSelection && !this.isFolderArtwork(this.state.selectedFile);


            return this.props.open &&
                (
                    <DialogEx
                        fullScreen={this.state.fullScreen}
                        onClose={() => {
                            if (this.state.reordering) {
                                this.setState({ reordering: false, dragState: null });
                            } else {
                                this.props.onCancel();
                            }
                        }}
                        onEnterKey={() => {
                            if (this.state.reordering) {
                                this.setState({ reordering: false, dragState: null });
                            } else if (this.state.multiSelect) {
                                this.setState({ multiSelect: false, selectedFiles: [] });
                            } else {
                                this.openSelectedFile();
                            }
                        }}
                        open={this.props.open} tag="fileProperty"
                        fullWidth maxWidth="md"
                        PaperProps={
                            this.state.fullScreen ?
                                {}
                                :
                                {
                                    style: {
                                        minHeight: "90%",
                                        maxHeight: "90%",
                                    }
                                }}
                    >
                        <DialogTitle style={{
                            paddingBottom: 0,
                            background: this.state.reordering || this.state.multiSelect ?
                                (isDarkMode() ? "#523" : "#EDF")
                                : undefined,
                            marginBottom: 8, paddingTop: 0
                        }} >
                            {this.state.reordering && (
                                <Toolbar style={{ padding: 0 }}>
                                    <IconButtonEx
                                        tooltip="Close"
                                        edge="start"
                                        color="inherit"
                                        aria-label="cancel"
                                        style={{ opacity: 0.6 }}
                                        onClick={() => { this.setState({ reordering: false, dragState: null }); }
                                        }
                                    >
                                        <CloseIcon style={{ width: 24, height: 24 }} />
                                    </IconButtonEx>
                                    <Typography noWrap component="div" sx={{ flexGrow: 1 }}>
                                        Reorder files
                                    </Typography>
                                </Toolbar>

                            )}
                            {this.state.multiSelect && (
                                <Toolbar style={{ padding: 0 }}>
                                    <IconButtonEx
                                        tooltip="Close"
                                        edge="start"
                                        color="inherit"
                                        aria-label="cancel"
                                        style={{ opacity: 0.6 }}
                                        onClick={() => {
                                            this.handleMultisSelectClose();
                                        }}
                                    >
                                        <CloseIcon style={{ width: 24, height: 24 }} />
                                    </IconButtonEx>
                                    <Typography noWrap component="div" sx={{ flexGrow: 1 }}>
                                        {this.state.selectedFiles.length.toString() + " files selected."}
                                    </Typography>
                                    <Button variant="dialogSecondary"
                                        startIcon={<OldDeleteIcon />}
                                        onClick={() => {
                                            this.handleDelete();
                                        }}
                                    >
                                        Delete
                                    </Button>
                                    <IconButtonEx
                                        tooltip="More..."
                                        aria-label="display more actions"
                                        edge="end"
                                        color="inherit"
                                        style={{ opacity: 0.6 }}
                                        onClick={(ev) => { this.handleMenuMultiSelectOpen(ev); }}
                                        disabled={protectedDirectory}

                                    >
                                        <MoreIcon />
                                    </IconButtonEx>
                                    <Menu
                                        id="menu-multi-select"
                                        anchorEl={this.state.menuMultiselectAnchorEl}
                                        anchorOrigin={{
                                            vertical: 'bottom',
                                            horizontal: 'right',
                                        }}
                                        keepMounted
                                        transformOrigin={{
                                            vertical: 'top',
                                            horizontal: 'right',
                                        }}
                                        open={this.state.menuMultiselectAnchorEl !== null}
                                        onClose={() => { this.handleMenuMultiSelectClose(); }}
                                    >
                                        {canMove && (<MenuItem onClick={() => { this.handleMenuMultiSelectClose(); this.handleMove(); }}>Move</MenuItem>)}
                                        {canMove && (<MenuItem onClick={() => { this.handleMenuMultiSelectClose(); this.handleCopy(); }}>Copy</MenuItem>)}
                                    </Menu>

                                </Toolbar>
                            )}
                            {!(this.state.reordering || this.state.multiSelect) && (
                                <>
                                    <Toolbar style={{ padding: 0 }}>
                                        <IconButtonEx
                                            tooltip="Back"
                                            edge="start"
                                            color="inherit"
                                            aria-label="back"
                                            style={{ opacity: 0.6 }}
                                            onClick={() => { this.props.onCancel(); }}
                                        >
                                            <ArrowBackIcon style={{ width: 24, height: 24 }} />
                                        </IconButtonEx>
                                        <Typography noWrap component="div" sx={{ flexGrow: 1 }}>
                                            {this.props.fileProperty.label}
                                        </Typography>

                                        <IconButtonEx
                                            tooltip="New folder"
                                            aria-label="new folder"
                                            edge="end"
                                            color="inherit"
                                            style={{ opacity: 0.6, marginRight: 8 }}
                                            onClick={(ev) => { this.onNewFolder(); }}
                                            disabled={protectedDirectory}
                                        >
                                            <CreateNewFolderIcon />
                                        </IconButtonEx>


                                        <IconButtonEx
                                            tooltip="More..."
                                            aria-label="display more actions"
                                            edge="end"
                                            color="inherit"
                                            style={{ opacity: 0.6 }}
                                            onClick={(ev) => { this.handleMenuOpen(ev); }}
                                            disabled={protectedDirectory}

                                        >
                                            <MoreIcon />
                                        </IconButtonEx>
                                        <Menu
                                            id="menu-appbar"
                                            anchorEl={this.state.menuAnchorEl}
                                            anchorOrigin={{
                                                vertical: 'bottom',
                                                horizontal: 'right',
                                            }}
                                            keepMounted
                                            transformOrigin={{
                                                vertical: 'top',
                                                horizontal: 'right',
                                            }}
                                            open={this.state.menuAnchorEl !== null}
                                            onClose={() => { this.handleMenuClose(); }}
                                        >
                                            <MenuItem onClick={() => { this.handleMenuClose(); this.onNewFolder(); }}>New folder</MenuItem>
                                            {(needsDivider) && (<Divider />)}
                                            {canMove && (<MenuItem onClick={() => { this.handleMenuClose(); this.handleMove(); }}>Move</MenuItem>)}
                                            {canMove && (<MenuItem onClick={() => { this.handleMenuClose(); this.handleCopy(); }}>Copy</MenuItem>)}
                                            {canRename && this.hasSelectedFileOrFolder() && !protectedItem && (<MenuItem onClick={() => {
                                                this.handleMenuClose(); this.onRename();
                                            }}>Rename</MenuItem>)}
                                            {canReorder && (
                                                <MenuItem onClick={() => { this.handleMenuClose(); this.onReorder(); }}>Reorder</MenuItem>)}
                                        </Menu>
                                    </Toolbar>
                                </>

                            )}
                            <div style={{ flex: "0 0 auto", marginLeft: 0, marginRight: 0, overflowX: "clip" }}>
                                {this.renderBreadcrumbs()}
                            </div>

                        </DialogTitle>
                        <DialogContent dividers style={{
                            paddingLeft: 0, paddingRight: 0, paddingTop: 0, colorScheme: (isDarkMode() ? "dark" : "light"),
                            position: "relative"
                        }}
                            ref={(element: HTMLDivElement | null) => { this.scrollContainerElementRef = element; }}
                        >

                            <div style={{
                                paddingLeft: 16, paddingRight: 16, paddingTop: 8, paddingBottom: 8,
                                display: this.state.loading ? "flex" : "none", flexFlow: "column nowrap",
                                justifyContent: "stretch", alignItems: "center",
                                position: "absolute", top: 0, left: 0, right: 0, bottom: 0, zIndex: 10
                            }}
                            >
                                {this.state.showProgress && (
                                    <>
                                        <div style={{ flex: "1 1 1px" }} />
                                        <CircularProgress />
                                        <div style={{ flex: "2 2 1px" }} />
                                    </>
                                )}
                            </div>

                            <div
                                ref={(element) => { this.listContainerElementRef = element; this.onMeasureRef(element); }}
                                style={{
                                    flex: "1 1 100%", display: "flex", flexFlow: "row wrap",
                                    position: "relative", justifyContent: "flex-start", alignContent: "flex-start",
                                    paddingLeft: 16, paddingBottom: 16, paddingTop: 16,
                                }}>
                                {
                                    (this.state.columns !== 0) && // don't render until we have number of columns derived from layout.
                                    this.state.fileResult.files.map(
                                        (value: FileEntry, index: number) => {

                                            if (this.state.reordering && !value.metadata) {
                                                return null; // don't render non-track files when reordering.
                                            }
                                            if (this.state.reordering && this.isFolderArtwork(value.pathname)) {
                                                return null;
                                            }
                                            let displayValue = value.displayName;
                                            if (displayValue === "") {
                                                displayValue = "<none>";
                                            }
                                            let dataPosition = "";
                                            let myTrackPosition = trackPosition;

                                            if (value.metadata && value.metadata.track) {
                                                dataPosition = trackPosition.toString();
                                                ++trackPosition;
                                                this.maxPosition = trackPosition;
                                            }
                                            let selected = false;
                                            if (this.state.reordering) {
                                                selected = false;
                                            } else if (this.state.multiSelect) {
                                                selected = this.state.selectedFiles.indexOf(value.pathname) !== -1;
                                            } else {
                                                selected = value.pathname === this.state.selectedFile;
                                            }
                                            let selectBg = selected ? "rgba(0,0,0,0.15)" : "rgba(0,0,0,0.0)";
                                            if (isDarkMode()) {
                                                selectBg = selected ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.0)";
                                            }
                                            let scrollRef: ((element: HTMLDivElement | null) => void) | undefined = undefined;
                                            if (selected) {
                                                scrollRef = (element) => { this.onScrollRef(element) };
                                            }
                                            let dragOffset = 0;
                                            let dragLeft = 0;
                                            let dragState = this.state.dragState;
                                            let zIndex: number | undefined = undefined;
                                            let background: string | undefined = undefined;
                                            if (dragState && value.metadata) {
                                                if (myTrackPosition !== dragState.from) {
                                                    if (myTrackPosition >= dragState.to && myTrackPosition < dragState.from) {
                                                        dragOffset = dragState.height;
                                                    } else if (myTrackPosition > dragState.from && myTrackPosition <= dragState.to) {
                                                        dragOffset = -dragState.height;
                                                    }
                                                } else {
                                                    dragOffset = dragState.dragElementDy + 5;
                                                    dragLeft = 5;
                                                    zIndex = 1000;
                                                    background = isDarkMode() ? "#555" : "#EEF";
                                                }
                                            }
                                            let dragButtonStyle: React.CSSProperties = {
                                                width: columnWidth, flex: "0 0 auto", height: (value.metadata && !compactVertical) ? 64 : 48,
                                                position: "relative",
                                                top: dragOffset,
                                                left: dragLeft,
                                                zIndex: zIndex,
                                                background: background,
                                            };
                                            return (
                                                <DraggableButtonBase key={value.pathname}
                                                    longPressDelay={this.state.reordering ? 300 : undefined}
                                                    data-position={dataPosition}
                                                    data-pathname={
                                                        value.metadata ? value.metadata.fileName : null
                                                    }
                                                    instantMouseLongPress={this.state.reordering}

                                                    style={dragButtonStyle}
                                                    onClick={(e) => this.handleFileClick(e, value)}
                                                    onDoubleClick={() => { this.onDoubleClickValue(value.pathname); }}

                                                    onLongPressEnd={(e) => {
                                                        this.handleLongPressEnd(e, value);
                                                    }
                                                    }
                                                    onLongPressStart={(currentTarget, e) => {
                                                        return this.handleLongPressStart(currentTarget, e, value);
                                                    }
                                                    }
                                                    onLongPressMove={(e) => {
                                                        this.handleLongPressMove(e);
                                                    }
                                                    }

                                                >
                                                    <div ref={scrollRef} style={{ position: "absolute", background: selectBg, width: "100%", height: "100%", borderRadius: 4 }} />
                                                    {value.metadata ?
                                                        (!compactVertical ?
                                                            (
                                                                <div style={{
                                                                    display: "flex", flexFlow: "row nowrap", textOverflow: "ellipsis",
                                                                    justifyContent: "start", alignItems: "center", width: "100%", height: "100%"
                                                                }}>
                                                                    {
                                                                        this.isFolderArtwork(value.pathname) ? (
                                                                            <img
                                                                                onDragStart={(e) => { e.preventDefault(); }}
                                                                                src={this.getTrackThumbnail(value)}
                                                                                style={{ width: 24, height: 24, margin: 20, borderRadius: 4 }} />
                                                                        ) : (
                                                                            <img
                                                                                onDragStart={(e) => { e.preventDefault(); }}
                                                                                src={this.getTrackThumbnail(value)}
                                                                                style={{ width: 48, height: 48, margin: 8, borderRadius: 4 }} />

                                                                        )
                                                                    }
                                                                    <div style={{
                                                                        flex: "1 1 1px", display: "flex", flexFlow: "column nowrap",
                                                                        marginLeft: 8,
                                                                        textOverflow: "ellipsis", justifyContent: "center", alignItems: "stretch"
                                                                    }}>
                                                                        <Typography noWrap
                                                                            className={classes.secondaryText}
                                                                            variant="body2"
                                                                            style={{ flex: "0 0 auto", textOverflow: "ellipsis", textAlign: "left", marginBottom: 4 }}>
                                                                            {getTrackTitle(value.pathname, value.metadata)}
                                                                        </Typography>
                                                                        <Typography noWrap className={classes.secondaryText}
                                                                            variant="body2"
                                                                            color="textSecondary"
                                                                            style={{ flex: "0 0 auto", textOverflow: "ellipsis", textAlign: "left", fontSize: "0.95em" }}>
                                                                            {this.getAlbumTitle(value)}

                                                                        </Typography>
                                                                    </div>
                                                                </div>
                                                            ) : (
                                                                <div style={{
                                                                    display: "flex", flexFlow: "row nowrap", textOverflow: "ellipsis",
                                                                    justifyContent: "start", alignItems: "center", width: "100%", height: "100%"
                                                                }}>
                                                                    <img
                                                                        onDragStart={(e) => { e.preventDefault(); }}
                                                                        src={this.getTrackThumbnail(value)}
                                                                        style={{ width: 24, height: 24, margin: 8, borderRadius: 4 }} />
                                                                    <div style={{
                                                                        flex: "1 1 1px", display: "flex", flexFlow: "column nowrap",
                                                                        marginLeft: 8,
                                                                        textOverflow: "ellipsis", justifyContent: "center", alignItems: "stretch"
                                                                    }}>
                                                                        <Typography noWrap
                                                                            className={classes.secondaryText}
                                                                            variant="body2"
                                                                            style={{ flex: "0 0 auto", textOverflow: "ellipsis", textAlign: "left", marginBottom: 4 }}>
                                                                            {this.getCompactTrackTitle(value)}
                                                                        </Typography>
                                                                    </div>
                                                                </div>
                                                            )

                                                        ) : (
                                                            <div style={{ display: "flex", flexFlow: "row nowrap", textOverflow: "ellipsis", justifyContent: "start", alignItems: "center", width: "100%", height: "100%" }}>
                                                                {this.getIcon(value, this.isTracksDirectory() && !compactVertical)}
                                                                <Typography noWrap className={classes.secondaryText} variant="body2" style={{ flex: "1 1 auto", textAlign: "left" }}>{displayValue}</Typography>
                                                            </div>
                                                        )}

                                                </DraggableButtonBase>
                                            );
                                        }
                                    )
                                }
                            </div>
                        </DialogContent>
                        {(!this.state.reordering && !this.state.multiSelect) && (
                            <>
                                <DialogActions style={{ justifyContent: "stretch", width: "100%" }}>
                                    <div style={{ display: "flex", flexFlow: "column nowrap", width: "100%", alignItems: "stretch", }}>
                                        {isToobNamModelFile && (
                                            <div style={{
                                                display: "flex", flexFlow: "row nowrap", justifyContent: "center",
                                                alignItems: "center", width: "100%"
                                            }}>
                                                <Button
                                                    onClick={(e) => { this.handleTone3000Dialog(e); }}
                                                >
                                                    Download model files from Tone3000
                                                </Button>
                                                <IconButtonEx tooltip="Help"
                                                    onClick={(e) => { this.handleTone3000Help(e); }} aria-label="help" edge="end" color="inherit" style={{ opacity: 0.6, marginLeft: 8 }}
                                                >
                                                    <HelpOutlineIcon />
                                                </IconButtonEx>
                                            </div>

                                        )}
                                        {isToobMLModelFile && (
                                            <div style={{
                                                display: "flex", flexFlow: "row nowrap", justifyContent: "center", width: "100%",
                                                alignItems: "center"
                                            }}>
                                                <Typography variant="body2" >
                                                    Download model files from <LinkEx
                                                        href="https://github.com/GuitarML/ToneLibrary/releases/download/v1.0/Proteus_Tone_Packs.zip" target="_blank">GuitarML</LinkEx>
                                                </Typography>
                                                <IconButtonEx tooltip="Help"
                                                    onClick={(e) => { this.handleGuitarMlHelp(e); }} aria-label="help" edge="end" color="inherit" style={{ opacity: 0.6, marginLeft: 8 }}
                                                >
                                                    <HelpOutlineIcon />
                                                </IconButtonEx>
                                            </div>

                                        )}


                                        {this.state.windowWidth > 500 ? (
                                            <div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "column nowrap" }}>

                                                <div style={{ flex: "1 1 100%", display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
                                                    <IconButtonEx style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
                                                        tooltip="Delete selected file or folder"
                                                        disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
                                                        onClick={() => this.handleDelete()} >
                                                        <OldDeleteIcon fontSize='small' />
                                                    </IconButtonEx>

                                                    <ButtonEx tooltip="Upload file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
                                                        onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
                                                    >
                                                        <div>Upload</div>
                                                    </ButtonEx>

                                                    <ButtonEx tooltip="Download file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
                                                        onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}
                                                    >
                                                        <div>Download</div>
                                                    </ButtonEx>

                                                    <div style={{ flex: "1 1 auto" }}>&nbsp;</div>

                                                    <Button variant="dialogSecondary" onClick={() => {
                                                        this.handleApply(this.props.fileProperty, this.state.initialSelection);
                                                        this.props.onCancel();
                                                    }} aria-label="cancel">
                                                        Cancel
                                                    </Button>
                                                    <Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
                                                        onClick={() => { this.openSelectedFile(); }}

                                                        disabled={(!canSelectFile)
                                                        } aria-label="select"
                                                    >
                                                        {okButtonText}
                                                    </Button>
                                                </div>
                                            </div>
                                        ) : (
                                            <div style={{ width: "100%" }}>
                                                <div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
                                                    <IconButtonEx tooltip="Delete" style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
                                                        disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
                                                        onClick={() => this.handleDelete()} >
                                                        <OldDeleteIcon fontSize='small' />
                                                    </IconButtonEx>

                                                    <Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
                                                        onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
                                                    >
                                                        <div>Upload</div>
                                                    </Button>

                                                    <Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
                                                        onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}

                                                    >
                                                        <div>Download</div>
                                                    </Button>

                                                    <div style={{ flex: "1 1 auto" }}>&nbsp;</div>
                                                </div>
                                                <div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
                                                    <div style={{ flex: "1 1 auto" }}>&nbsp;</div>

                                                    <Button variant="dialogSecondary" onClick={() => { this.props.onCancel(); }} aria-label="cancel">
                                                        Cancel
                                                    </Button>
                                                    <Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
                                                        onClick={() => { this.openSelectedFile(); }}

                                                        disabled={!canSelectFile}
                                                        aria-label="select"
                                                    >
                                                        {okButtonText}
                                                    </Button>
                                                </div>
                                            </div>
                                        )}
                                    </div>
                                </DialogActions>
                            </>
                        )}
                        <UploadFileDialog
                            open={this.state.openUploadFileDialog}
                            onClose=
                            {
                                () => {
                                    this.setState({ openUploadFileDialog: false });
                                }
                            }
                            isTracksDirectory={this.isTracksDirectory()}
                            uploadPage={
                                "uploadUserFile?directory=" + encodeURIComponent(this.state.currentDirectory)
                                + "&id=" + this.props.instanceId.toString()
                                + "&property=" + encodeURIComponent(this.props.fileProperty.patchProperty)
                                + "&ext="
                                + encodeURIComponent(this.getFileExtensionList(this.props.fileProperty))
                            }
                            onUploaded={() => { this.requestFiles(this.state.navDirectory); }}
                            fileProperty={this.props.fileProperty}


                        />
                        <OkCancelDialog open={this.state.openConfirmDeleteDialog}

                            text={
                                this.state.multiSelect ?
                                    ("Are you sure you want to delete the selected files and directories and all their contents?")
                                    : (this.state.selectedFileIsDirectory
                                        ? "Are you sure you want to delete the selected directory and all its contents?"
                                        : "Are you sure you want to delete the selected file?"
                                    )
                            }
                            okButtonText="Delete"
                            onOk={() => this.handleConfirmDelete()}
                            onClose={() => {
                                this.setState({ openConfirmDeleteDialog: false });
                            }}

                        />
                        <OkCancelDialog open={this.state.confirmCopyDialogState !== null}
                            text={
                                "The target file already exists. Would you like to overwrite it?"}
                            okButtonText="Overwrite"
                            onOk={() => { this.handleConfirmCopy(); }}
                            onClose={() => {
                                this.setState({ confirmCopyDialogState: null });
                            }}

                        />

                        {
                            this.state.newFolderDialogOpen && (
                                <RenameDialog open={this.state.newFolderDialogOpen} defaultName=""
                                    onOk={(newName) => { this.setState({ newFolderDialogOpen: false }); this.onExecuteNewFolder(newName) }}
                                    onClose={() => { this.setState({ newFolderDialogOpen: false }); }}
                                    title="New folder"
                                    acceptActionName="OK"
                                />
                            )
                        }
                        {
                            this.state.renameDialogOpen && (
                                <RenameDialog open={this.state.renameDialogOpen} defaultName={this.renameDefaultName()}
                                    title="Rename"
                                    onOk={(newName) => { this.setState({ renameDialogOpen: false }); this.onExecuteRename(newName) }}
                                    onClose={() => { this.setState({ renameDialogOpen: false }); }}
                                    acceptActionName="OK"
                                />

                            )
                        }
                        {
                            (this.state.moveDialogOpen || this.state.copyDialogOpen)
                            && (
                                (
                                    <FilePropertyDirectorySelectDialog
                                        open={this.state.moveDialogOpen || this.state.copyDialogOpen}
                                        dialogTitle={this.state.moveDialogOpen ? "Move to" : "Copy Link to"}
                                        uiFileProperty={this.props.fileProperty}
                                        defaultPath={this.getDefaultPath()}
                                        selectedFile={this.state.selectedFile}
                                        excludeDirectory={
                                            this.isDirectory(this.state.selectedFile)
                                                ? this.state.selectedFile : ""}
                                        onClose={() => { this.setState({ moveDialogOpen: false, copyDialogOpen: false }); }}
                                        onOk={
                                            (path) => {
                                                this.setState({ moveDialogOpen: false, copyDialogOpen: false });
                                                this.setDefaultPath(path);
                                                if (this.state.copyDialogOpen) {
                                                    this.onExecuteCopy(path);
                                                } else {
                                                    this.onExecuteMove(path);
                                                }
                                            }
                                        }

                                    />
                                )
                            )
                        }
                        {this.state.openTone3000Dialog && (
                            <Tone3000Dialog
                                open={this.state.openTone3000Dialog}
                                onCancel={() => this.setState({ openTone3000Dialog: false })}
                                onDownload={(toneUrl: string) => {
                                    alert("Donload tone url: " + toneUrl);
                                    this.setState({ openTone3000Dialog: false });
                                }
                                }
                            />
                        )}
                        {this.state.openTone3000Help && (
                            <Tone3000HelpDialog
                                open={this.state.openTone3000Help}
                                onClose={() => this.setState({ openTone3000Help: false })}
                            />
                        )}
                        {this.state.openGuitarMlHelp && (
                            <GuitarMLHelpDialog
                                open={this.state.openGuitarMlHelp}
                                onClose={() => this.setState({ openGuitarMlHelp: false })}
                            />
                        )}
                        {this.state.textFileName !== undefined && (
                            <TextInfoDialog open={true}
                                title={pathFileNameOnly(this.state.textFileName)}
                                fileName={this.state.textFileName} onClose={() => this.setState({ textFileName: undefined })}
                            />
                        )}
                    </DialogEx>
                );
        }
        isLicenseFile(fileName: string) {
            let extension = pathExtension(fileName);
            if (extension === ".txt" || extension === ".md") {
                return true;
            }
            let fileNameOnly = pathFileNameOnly(fileName);
            if (fileNameOnly.toUpperCase() === "LICENSE" || fileNameOnly.toUpperCase() === "README") {
                return true;
            }
            return false;

        }
        handleShowTextFile(fileName: string) {
            this.setState({ textFileName: fileName });
        }

        openSelectedFile(): void {
            if (this.state.multiSelect || this.state.reordering) {
                return;
            }
            if (this.isFolderArtwork(this.state.selectedFile)) {
                return;
            }
            if (this.isDirectory(this.state.selectedFile)) {
                this.requestFiles(this.state.selectedFile);
                this.setState({ navDirectory: this.state.selectedFile });
            } else {
                if (this.isLicenseFile(this.state.selectedFile)) {
                    this.handleShowTextFile(this.state.selectedFile);
                } else {
                    this.props.onOk(this.props.fileProperty, this.state.selectedFile);
                }
            }
        }

        private renameDefaultName(): string {
            let name = this.state.selectedFile;
            if (name === "") return "";
            if (this.isDirectory(name)) {
                return pathFileName(name);
            } else {
                return pathFileNameOnly(name);
            }
        }
        private handleMove(): void {
            this.setState({ moveDialogOpen: true });
        }
        private handleCopy(): void {
            this.setState({ copyDialogOpen: true });
        }
        private async onExecuteMoveMulti(newDirectory: string) {
            let files = this.state.selectedFiles.slice();
            let newFileList = this.state.selectedFiles.slice();
            try {
                for (let file of files) {
                    let fileName = pathFileName(file);
                    let oldFilePath = pathConcat(this.state.navDirectory, fileName);
                    let newFilePath = pathConcat(newDirectory, fileName);

                    await this.model.renameFilePropertyFile(oldFilePath, newFilePath, this.props.fileProperty);
                    let index = newFileList.indexOf(file);
                    if (index !== -1) {
                        newFileList.splice(index, 1);
                    }

                }
            } catch (e) {
                this.model.showAlert(e);
            }
            // update state to refelect what we did.
            if (!this.mounted) {
                return;
            }
            if (newFileList.length === 0) {
                this.setState({
                    multiSelect: false,
                    selectedFiles: []
                });
            } else {
                this.setState({
                    multiSelect: true,
                    selectedFiles: newFileList,
                });
            }
            this.requestFiles(this.state.navDirectory);
        }
        private onExecuteMove(newDirectory: string) {
            if (this.state.multiSelect) {
                this.onExecuteMoveMulti(newDirectory);
                return;
            }
            let fileName = pathFileName(this.state.selectedFile);
            let oldFilePath = pathConcat(this.state.navDirectory, fileName);
            let newFilePath = pathConcat(newDirectory, fileName);

            this.model.renameFilePropertyFile(oldFilePath, newFilePath, this.props.fileProperty)
                .then(() => {
                    this.requestFiles(this.state.navDirectory);
                })
                .catch((e) => {
                    this.model.showAlert(e.toString());
                });

        }
        private handleConfirmCopy() {
            if (this.state.confirmCopyDialogState) {
                this.model.copyFilePropertyFile(
                    this.state.confirmCopyDialogState.oldFilePath,
                    this.state.confirmCopyDialogState.newFilePath,
                    this.props.fileProperty, true)
                    .then((filename) => {
                        this.requestFiles(this.state.navDirectory);
                    })
                    .catch((e) => {
                        this.model.showAlert(e.toString());
                    });
                this.setState({ confirmCopyDialogState: null });
            }
        }
        private async onExecuteCopyMulti(newDirectory: string) {

            let files = this.state.selectedFiles.slice();
            let fileProperty = this.props.fileProperty;
            try {
                for (let fileName of files) {
                    let oldFilePath = pathConcat(this.state.navDirectory, fileName);
                    let newFilePath = pathConcat(newDirectory, fileName);

                    await this.model.copyFilePropertyFile(oldFilePath, newFilePath, fileProperty, true);
                }
            } catch (e) {
                this.model.showAlert(e);
            }
            // update state to reflect what we did.
            if (!this.mounted) {
                return;
            }
            this.requestFiles(this.state.navDirectory);
        }
        private onExecuteCopy(newDirectory: string) {
            this.setState({ copyDialogOpen: false });
            if (this.state.multiSelect) {
                this.onExecuteCopyMulti(newDirectory);
                return;
            }
            let fileName = pathFileName(this.state.selectedFile);
            let oldFilePath = pathConcat(this.state.navDirectory, fileName);
            let newFilePath = pathConcat(newDirectory, fileName);

            this.model.copyFilePropertyFile(oldFilePath, newFilePath, this.props.fileProperty, false)
                .then((filename) => {
                    if (filename === "") {
                        // the file already exists. prompt for overrwrite.
                        this.setState({
                            confirmCopyDialogState: {
                                fileName: fileName,
                                oldFilePath: oldFilePath,
                                newFilePath
                            }
                        });

                    } else {
                        this.requestFiles(this.state.navDirectory);
                        this.setState({
                            selectedFile: filename,
                            selectedFileIsDirectory: this.isDirectory(filename),
                            selectedFileProtected: false
                        });
                        this.requestScroll = true;
                    }
                })
                .catch((e) => {
                    this.model.showAlert(e.toString());
                });

        }

        private onRename(): void {
            this.setState({ renameDialogOpen: true });
        }
        private onReorder(): void {
            this.setState({ reordering: true });
        }
        private onExecuteRename(newName: string) {
            let newPath: string = "";
            let oldPath: string = "";
            if (this.isDirectory(this.state.selectedFile)) {
                let oldName = pathFileName(this.state.selectedFile);
                if (oldName === newName) return;
                oldPath = pathConcat(this.state.navDirectory, oldName);
                newPath = pathConcat(this.state.navDirectory, newName);;
            } else {
                let oldName = pathFileNameOnly(this.state.selectedFile);
                if (oldName === newName) return;
                let extension = pathExtension(this.state.selectedFile);
                oldPath = pathConcat(this.state.navDirectory, oldName + extension);
                newPath = pathConcat(this.state.navDirectory, newName + extension);
            }
            this.model.renameFilePropertyFile(oldPath, newPath, this.props.fileProperty)
                .then((newPath) => {
                    this.setState({ selectedFile: newPath });
                    this.requestFiles(this.state.navDirectory);
                    this.requestScroll = true

                })
                .catch((e) => {
                    this.model.showAlert(e.toString());
                });
        }

        private onNewFolder(): void {
            this.setState({ newFolderDialogOpen: true });
        }

        private onExecuteNewFolder(newName: string) {
            this.model.createNewSampleDirectory(pathConcat(this.state.navDirectory, newName), this.props.fileProperty)
                .then((newPath) => {
                    this.setState({
                        selectedFile: newPath,
                        selectedFileIsDirectory:
                            true, selectedFileProtected: false
                    });
                    this.requestFiles(this.state.navDirectory);
                    this.requestScroll = true
                })
                .catch((e) => {
                    this.model.showAlert(e.toString());
                }
                );
        }
    },
    styles);