import React from 'react';
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 '../../components/MountItem/MountItem';
import IPCContainer from '../IPCContainer/IPCContainer';
import {
  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 {
  constructor(props) {
    super(props);
    
    this.setRequestHandler(Constants.IPC_Detect_Mounts_Reply, this.onDetectMountsReply);
    this.setRequestHandler(Constants.IPC_Mount_Drive_Reply, this.onMountDriveReply);
    this.setRequestHandler(Constants.IPC_Unmount_Drive_Reply, this.onUnmountDriveReply);
  }

  retryIntervals = {};

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

  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.detectMounts();
  }

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

    super.componentWillUnmount();
  };

  detectMounts = ()=> {
    if (!this.state.DisplayRetry) {
      this.props.setMountsBusy(true);
      this.sendRequest(Constants.IPC_Detect_Mounts, {
        Version: this.props.InstalledVersion,
      });
    }
  };

  displayRetryMount = (provider, 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, 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, value) => {
    const location = (this.props.Platform === 'win32') ?
      this.props.MountState[provider].DriveLetters[value] :
      value;

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

  handleMountUnMount = (provider, mount, location) => {
    if (!location || (location.trim().length === 0)) {
      this.props.notifyError('Mount location is not set');
    } else {
      let allowAction = true;
      if (mount) {
        let result = this.sendSyncRequest(Constants.IPC_Check_Daemon_Version, {
          Provider: provider,
          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, location, 'Failed to connect to ' + provider + ' daemon');
            } else if ((result.Code === new Uint32Array([-3])[0]) || (result.Code === new Uint8Array([-3])[0])) {
              this.displayRetryMount(provider, location, 'Incompatible ' + provider + ' daemon. Please upgrade ' + provider + '.');
            } else {
              this.displayRetryMount(provider, location, 'Version check failed: ' + result.Error);
            }
          }
        } else {
          allowAction = false;
          this.displayRetryMount(provider, location, 'Version check failed: ' + result.Error);
        }
      }

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

        if (mount) {
          this.sendRequest(Constants.IPC_Mount_Drive, {
            Location: location,
            NoConsoleSupported: this.props.noConsoleSupported,
            Provider: provider,
            Version: this.props.InstalledVersion,
          });
        } else {
          this.sendRequest(Constants.IPC_Unmount_Drive, {
            Location: location,
            Provider: provider,
            Version: this.props.InstalledVersion,
          });
        }
      }
    }
  };

  onDetectMountsReply = (event, arg) => {
    if (!this.state.DisplayRetry && arg.data.Success) {
      let mountsBusy = false;
      let mountStates = {};
      for (const provider of Constants.PROVIDER_LIST) {
        const mountState = {
          AllowMount: true,
          DriveLetters: (arg.data.DriveLetters[provider]),
          Mounted: (arg.data.Locations[provider].length > 0),
        };
        this.props.setMountState(provider, mountState);
        mountsBusy = mountsBusy || mountState.Mounted;
        mountStates[provider] = mountState;
      }
      this.props.setMountsBusy(mountsBusy);
      
      const updateMountLocation = (data, provider) => {
        const providerState = this.props.ProviderState[provider];
        let location = data.Locations[provider];
        if (location.length === 0) {
          location = (this.props.Platform === 'win32') ?
            providerState.MountLocation || data.DriveLetters[provider][0] :
            providerState.MountLocation;
        }

        if (location !== providerState.MountLocation) {
          this.handleMountLocationChanged(provider, location);
        }

        if (!this.props.AutoMountProcessed &&
          this.props.ProviderState[provider].AutoMount &&
          !mountStates[provider].Mounted &&
          (location.length > 0)) {
          this.handleMountUnMount(provider, true, location);
        }
      };
      
      for (const provider of Constants.PROVIDER_LIST) {
        updateMountLocation(arg.data, provider);
      }

      this.props.setAutoMountProcessed(true);
    } else {
      this.props.notifyError(arg.data.Error);
    }
  };

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

  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.Location);
    } else {
      this.detectMounts();
    }
  };

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

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

    let items = [];
    for (const provider of Constants.PROVIDER_LIST) {
      items.push((
        <MountItem allowConfig={this.props.allowConfig}
                   browseClicked={this.handleBrowseLocation}
                   changed={e => this.handleMountLocationChanged(provider, e.target.value)}
                   clicked={this.handleMountUnMount}
                   key={'mi_' + items.length}
                   provider={provider}/>
      ));
      if (items.length !== this.state.length) {
        items.push(<div key={'di_' + items.length}
                        style={{paddingTop: '12px'}} />)
      }
    }
    
    return (
      <div className={'MountItems'}>
        {retryDisplay}
        {items}
      </div>);
  }
}

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

const mapDispatchToProps = dispatch => {
  return {
    notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
    setAllowMount: (provider, allow) => dispatch(setAllowMount(provider, allow)),
    setAutoMountProcessed: processed => dispatch(setAutoMountProcessed(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);