import React from 'react';
import AddMount from '../AddMount/AddMount';
import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button';
import {connect} from 'react-redux';
import './MountItems.css';
import Modal from '../../components/UI/Modal/Modal';
import MountItem from './MountItem/MountItem';
import IPCContainer from '../IPCContainer/IPCContainer';
import {
  resetMountsState,
  setAllowMount,
  setAutoMountProcessed,
  setBusy,
  setMounted,
  setMountState,
  setProviderState
} from '../../redux/actions/mount_actions';
import {notifyError} from '../../redux/actions/error_actions';

const Constants = require('../../constants');

class MountItems extends IPCContainer {
  retryIntervals = {};
  activeDetections = [];

  state = {
    DisplayRetry: false,
    RetryItems: {},
  };

  addMountsBusy = provider => {
    this.props.setMountsBusy(true);
    this.activeDetections.push(provider);
  };

  cancelRetryMount = (provider, stateCallback) => {
    clearInterval(this.retryIntervals[provider]);
    delete this.retryIntervals[provider];

    if (stateCallback) {
      let retryItems = {
        ...this.state.RetryItems,
      };
      delete retryItems[provider];
      this.setState({
        DisplayRetry: Object.keys(retryItems).length > 0,
        RetryItems: retryItems,
      }, () => {
        if (this.state.DisplayRetry) {
          this.sendRequest(Constants.IPC_Show_Window);
        }
        stateCallback();
      });
    }
  };

  componentDidMount() {
    this.setRequestHandler(Constants.IPC_Detect_Mount_Reply, this.onDetectMountReply);
    this.setRequestHandler(Constants.IPC_Mount_Drive_Reply, this.onMountDriveReply);
    this.setRequestHandler(Constants.IPC_Unmount_Drive_Reply, this.onUnmountDriveReply);
    this.props.resetMountsState();
    this.detectMounts();
  }

  componentWillUnmount() {
    for (const provider in this.state.RetryItems) {
      if (this.state.RetryItems.hasOwnProperty(provider)) {
        this.cancelRetryMount(provider);
      }
    }

    this.props.resetMountsState();
    this.activeDetections = [];
    super.componentWillUnmount();
  };

  detectMount = provider => {
    this.addMountsBusy(provider);

    this.sendRequest(Constants.IPC_Detect_Mount, {
      RemoteMounts: this.props.RemoteMounts,
      S3Mounts: this.props.S3Mounts,
      Provider: provider,
      Version: this.props.InstalledVersion,
    });
  };

  detectMounts = ()=> {
    if (!this.state.DisplayRetry) {
      const providerList = this.getProviderList();
      providerList.forEach(provider => {
        this.detectMount(provider);
      });
    }
  };

  displayRetryMount = (provider, remote, s3, mountLocation, msg) => {
    if (!this.state.RetryItems[provider]) {
      let retryItems = {
        ...this.state.RetryItems
      };
      retryItems[provider] = {
        RetrySeconds: 10,
        RetryMessage: msg ? msg.toString() : null,
      };
      const mountState = {
        AllowMount: false,
        Mounted: false,
      };
      this.props.setMountState(provider, mountState);

      this.setState({
        DisplayRetry: true,
        RetryItems: retryItems,
      }, () => {
        this.sendRequest(Constants.IPC_Show_Window);
        this.retryIntervals[provider] = setInterval(() => {
          let retryItems = {
            ...this.state.RetryItems,
          };
          const retrySeconds = retryItems[provider].RetrySeconds - 1;
          if (retrySeconds === 0) {
            this.cancelRetryMount(provider, () => {
              this.handleMountUnMount(provider, remote, s3, true, mountLocation);
            });
          } else {
            retryItems[provider].RetrySeconds = retrySeconds;
            this.setState({
              RetryItems: retryItems,
            });
          }
        }, 1000);
      });
    }
  };

  handleBrowseLocation = (provider, location) => {
    location = this.sendSyncRequest(Constants.IPC_Browse_Directory, {
      Title: provider + ' Mount Location',
      Location: location,
    });
    if (location && (location.length > 0)) {
      this.handleMountLocationChanged(provider, location);
    }
  };

  handleMountLocationChanged = (provider, mountLocation) => {
    const state = {
      ...this.props.ProviderState[provider],
      MountLocation: mountLocation,
    };
    this.props.setProviderState(provider, state);
  };

  handleMountUnMount = (provider, remote, s3, mount, location) => {
    if (!location || (location.trim().length === 0)) {
      this.props.notifyError('Mount location is not set');
    } else {
      let allowAction = true;
      if (mount) {
        let result = remote || s3 || provider === 'Skynet' ?
          {Valid: true, Success: true} :
          this.sendSyncRequest(Constants.IPC_Check_Daemon_Version, {
            Provider: provider,
            Remote: remote,
            S3: s3,
            Version: this.props.InstalledVersion
          }).data;
        if (result.Success) {
          if (result.Valid) {
            if (this.props.Platform !== 'win32') {
              result = this.sendSyncRequest(Constants.IPC_Check_Mount_Location, {
                Location: location,
              });
              if (!result.Success) {
                allowAction = false;
                this.props.notifyError(result.Error.toString());
              }
            }
          } else {
            allowAction = false;
            if ((result.Code === new Uint32Array([-1])[0]) || (result.Code === new Uint8Array([-1])[0])) {
              this.displayRetryMount(provider, remote, s3, location, 'Failed to connect to ' + provider + ' daemon');
            } else if ((result.Code === new Uint32Array([-3])[0]) || (result.Code === new Uint8Array([-3])[0])) {
              this.displayRetryMount(provider, remote, s3, location, 'Incompatible ' + provider + ' daemon. Please upgrade ' + provider + '.');
            } else {
              this.displayRetryMount(provider, remote, s3, location, 'Version check failed: ' + result.Error);
            }
          }
        } else {
          allowAction = false;
          if (this.props.Platform === 'win32') {
            this.props.notifyError('Failed to launch repertory. Please install Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019.');
          } else {
            this.displayRetryMount(provider, remote, s3, location, 'Version check failed: ' + result.Error);
          }
        }
      }

      if (allowAction) {
        this.addMountsBusy(provider);
        this.props.setAllowMount(provider, false);

        if (mount) {
          this.sendRequest(Constants.IPC_Mount_Drive, {
            Location: location,
            Provider: provider,
            Remote: remote,
            S3: s3,
            Version: this.props.InstalledVersion,
          });
        } else {
          this.sendRequest(Constants.IPC_Unmount_Drive, {
            Location: location,
            Provider: provider,
            Remote: remote,
            S3: s3,
            Version: this.props.InstalledVersion,
          });
        }
      }
    }
  };

  getProviderList = providersOnly => {
    const providerList = Constants.PROVIDER_LIST.filter(i => {
      return ((i === 'Skynet') && this.props.skynetSupported) ||
        ((i === 'ScPrime') && this.props.scPrimeSupported) ||
        ((i === 'Sia') && this.props.siaSupported);
    });

    let remoteList = [];
    if (this.props.remoteSupported && !providersOnly) {
      remoteList = [...this.props.RemoteMounts];
    }

    let s3List = [];
    if (this.props.s3Supported && !providersOnly) {
      s3List = [...this.props.S3Mounts];
    }

    return [
      ...providerList,
      ...remoteList,
      ...s3List,
    ];
  };

  hasActiveMount = () => {
    for (const provider of Object.keys(this.props.MountState)) {
      if (this.props.MountState[provider].Mounted)
        return true;
    }

    return false;
  };

  onDetectMountReply = (event, arg) => {
    const provider = arg.data.Provider;
    if (!this.state.RetryItems[provider]) {
      if (arg.data.Success && (!arg.data.Active || (arg.data.Location && (arg.data.Location.length > 0)))) {
        const mountState = {
          AllowMount: true,
          DriveLetters: arg.data.DriveLetters,
          Mounted: arg.data.Active,
        };
        this.props.setMountState(provider, mountState);

        this.updateMountLocation(provider, arg.data.Location, mountState.Mounted, arg.data.DriveLetters);
        this.props.setAutoMountProcessed(provider, true);
        this.removeMountsBusy(provider);
      } else {
        this.detectMount(provider);
        this.removeMountsBusy(provider);
      }
    }
  };

  onMountDriveReply = (event, arg) => {
    this.props.setMounted(arg.data.Provider, arg.data.Success);
    this.detectMount(arg.data.Provider);
    this.removeMountsBusy(arg.data.Provider);
  };

  onUnmountDriveReply = (event, arg) => {
    if (arg && arg.data && !arg.data.Expected && arg.data.Location && this.props.ProviderState[arg.data.Provider].AutoRestart) {
      this.displayRetryMount(arg.data.Provider, arg.data.Remote, arg.data.Location);
    } else {
      this.detectMount(arg.data.Provider);
    }
    this.removeMountsBusy(arg.data.Provider);
  };

  removeMountsBusy = provider => {
    const idx = this.activeDetections.indexOf(provider);
    if (idx > -1) {
      this.activeDetections.splice(idx, 1);
    }
    this.props.setMountsBusy((this.activeDetections.length > 0) || this.hasActiveMount());
  };

  updateMountLocation = (provider, location, mounted, driveLetters) => {
    const providerState = this.props.ProviderState[provider];
    if (location.length === 0) {
      location = (this.props.Platform === 'win32') ?
        providerState.MountLocation || driveLetters[0] :
        providerState.MountLocation;
    }

    if (location !== providerState.MountLocation) {
      const value = (this.props.Platform === 'win32') ?
        driveLetters.indexOf(location) :
        location;
      this.handleMountLocationChanged(provider, value);
    }

    if (!this.props.AutoMountProcessed[provider] &&
      this.props.ProviderState[provider].AutoMount &&
      !mounted &&
      (location.length > 0)) {
      this.handleMountUnMount(provider, this.props.RemoteMounts.includes(provider),true, location);
    }
  };

  render() {
    let retryDisplay;
    if (this.state.DisplayRetry) {
      let retryList = [];
      let retryCount = 0;
      for (const provider in this.state.RetryItems) {
        if (this.state.RetryItems.hasOwnProperty(provider)) {
          if (this.state.RetryItems[provider].RetryMessage) {
            retryList.push(<p key={'rl_' + retryList.length}>{this.state.RetryItems[provider].RetryMessage}</p>);
          }
          retryList.push(<Button clicked={() => this.cancelRetryMount(provider, () => this.detectMounts())}
                                 key={'rl_' + retryList.length}>Cancel {provider} Remount
            ({this.state.RetryItems[provider].RetrySeconds}s)</Button>);
          if (++retryCount < Object.keys(this.state.RetryItems).length) {
            retryList.push(<div style={{paddingTop: 'var(--default_spacing)'}}
                                key={'rl_' + retryList.length}/>);
          }
        }
      }

      retryDisplay = (
        <Modal>
          <Box dxDark dxStyle={{padding: 'var(--default_spacing)', minWidth: '70vw'}}>
            <h1 style={{
              textAlign: 'center',
              paddingBottom: 'var(--default_spacing)',
              color: 'var(--text_color_error)'
            }}>Mount Failed</h1>
            {retryList}
          </Box>
        </Modal>
      )
    }

    let footerItems = [];
    if (this.props.remoteSupported || this.props.s3Supported) {
      footerItems.push(<AddMount remoteSupported={this.props.remoteSupported}
                                 s3Supported={this.props.s3Supported}
                                 key={'hi_' + footerItems.length}/>);
    } else {
      footerItems.push(<div key={'hi_' + footerItems.length}
                            style={{height: '27px'}}/>);
    }

    let items = [];
    for (const provider of this.getProviderList(true)) {
      items.push((
        <MountItem allowRemove={false}
                   browseClicked={this.handleBrowseLocation}
                   changed={e => this.handleMountLocationChanged(provider, e.target.value)}
                   clicked={this.handleMountUnMount}
                   key={'it_' + items.length}
                   provider={provider}/>
      ));
      items.push(<div key={'it_' + items.length}
                      style={{paddingTop: 'var(--default_spacing)'}} />)
    }

    if (this.props.remoteSupported) {
      for (const provider of this.props.RemoteMounts) {
        items.push((
          <MountItem allowRemove={true}
                     browseClicked={this.handleBrowseLocation}
                     changed={e => this.handleMountLocationChanged(provider, e.target.value)}
                     clicked={this.handleMountUnMount}
                     key={'it_' + items.length}
                     provider={provider}
                     remote/>
        ));
        items.push(<div key={'it_' + items.length}
                        style={{paddingTop: 'var(--default_spacing)'}}/>)
      }
    }

    if (this.props.s3Supported) {
      for (const provider of this.props.S3Mounts) {
        items.push((
          <MountItem allowRemove={true}
                     browseClicked={this.handleBrowseLocation}
                     changed={e => this.handleMountLocationChanged(provider, e.target.value)}
                     clicked={this.handleMountUnMount}
                     key={'it_' + items.length}
                     provider={provider}
                     s3/>
        ));
        items.push(<div key={'it_' + items.length}
                        style={{paddingTop: 'var(--default_spacing)'}}/>)
      }
    }

    items.splice(items.length - 1, 1);

    return (
      <div style={{margin: 0, padding: 0}}>
        {retryDisplay}
        <div
          className={this.props.remoteSupported || this.props.s3Supported ? 'MountItemsRemote' : 'MountItems'}>
          {items}
        </div>
        <div style={{paddingTop: 'var(--default_spacing)'}}/>
        {footerItems}
      </div>);
  }
}

const mapStateToProps = state => {
  return {
    AutoMountProcessed: state.mounts.AutoMountProcessed,
    InstalledVersion: state.relver.InstalledVersion,
    MountState: state.mounts.MountState,
    MountsBusy: state.mounts.MountsBusy,
    Platform: state.common.Platform,
    ProviderState: state.mounts.ProviderState,
    RemoteMounts: state.mounts.RemoteMounts,
    S3Mounts: state.mounts.S3Mounts,
  }
};

const mapDispatchToProps = dispatch => {
  return {
    notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
    resetMountsState: () => dispatch(resetMountsState()),
    setAllowMount: (provider, allow) => dispatch(setAllowMount(provider, allow)),
    setAutoMountProcessed: (provider, processed) => dispatch(setAutoMountProcessed(provider, processed)),
    setMounted: (provider, mounted) => dispatch(setMounted(provider, mounted)),
    setMountsBusy: busy => dispatch(setBusy(busy)),
    setMountState: (provider, state) => dispatch(setMountState(provider, state)),
    setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
  }
};

export default connect(mapStateToProps, mapDispatchToProps)(MountItems);
