import React from 'react';
import './App.css';
import Box from './components/UI/Box/Box';
import Configuration from './containers/Configuration/Configuration';
import {connect} from 'react-redux';
import DependencyList from './components/DependencyList/DependencyList';
import DownloadProgress from './components/DownloadProgress/DownloadProgress';
import {extractFileNameFromURL} from './utils';
import ErrorDetails from './components/ErrorDetails/ErrorDetails';
import Grid from './components/UI/Grid/Grid';
import IPCContainer from './containers/IPCContainer/IPCContainer';
import Loading from './components/UI/Loading/Loading';
import Modal from './components/UI/Modal/Modal';
import MountItems from './containers/MountItems/MountItems';
import {notifyError} from './redux/actions/error_actions';
import ReleaseVersionDisplay from './components/ReleaseVersionDisplay/ReleaseVersionDisplay';
import {setProviderState} from './redux/actions/mount_actions';
import Text from './components/UI/Text/Text';
import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon';
import UpgradeUI from './components/UpgradeUI/UpgradeUI';
import {
  downloadItem,
  setAllowDownload
} from './redux/actions/download_actions';
import {
  checkInstalled,
  installDependency,
  installRelease,
  installUpgrade
} from './redux/actions/install_actions';
import {
  detectUIUpgrade,
  loadReleases,
  setActiveRelease,
  setDismissUIUpgrade,
  setInstalledVersion,
  setReleaseUpgradeAvailable
} from './redux/actions/release_version_actions';

const Constants = require('./constants');
const Scheduler = require('node-schedule');

class App extends IPCContainer {
  checkVersionInstalled = () => {
    this.props.setAllowDownload(false);
    const selectedVersion = this.getSelectedVersion();
    if (selectedVersion !== 'unavailable') {
      let dependencies = [];
      if (this.props.LocationsLookup[selectedVersion] && this.props.LocationsLookup[selectedVersion].dependencies) {
        dependencies = this.props.LocationsLookup[selectedVersion].dependencies;
      }

      this.props.checkInstalled(dependencies, selectedVersion);
    } else {
      this.props.setInstalledVersion('none');
    }
  };

  _isMounted = false;

  componentDidMount() {
    this._isMounted = true;
    this.scheduledUpdateJob = Scheduler.scheduleJob('23 11 * * *', this.updateCheckScheduledJob);
    this.detectUpgrades();
  }

  componentDidUpdate(prevProps) {
    if ((prevProps.Release !== this.props.Release) ||
      (prevProps.ReleaseVersion !== this.props.ReleaseVersion) ||
      (prevProps.VersionLookup !== this.props.VersionLookup)) {
      this.saveState();
      this.checkVersionInstalled();
    } else if (Object.keys(this.props.ProviderState).filter(k=> {
        return this.props.ProviderState[k] !== prevProps.ProviderState[k];
      }).length > 0) {
      this.saveState();
    }
  }

  componentWillUnmount() {
    this._isMounted = false;
    Scheduler.cancelJob(this.scheduledUpdateJob);
    super.componentWillUnmount();
  }

  createModalConditionally = (condition, jsx, critical) => {
    const modalProps = {critical: critical};
    return condition ? (<Modal {...modalProps}>{jsx}</Modal>) : null;
  };

  detectUpgrades = () => {
    if (this.props.AppPlatform !== 'unknown') {
      this.props.loadReleases();
      this.props.detectUIUpgrade();
    }
  };

  getSelectedVersion = () => {
    return (this.props.ReleaseVersion === -1) ?
      'unavailable' :
      this.props.VersionLookup[Constants.RELEASE_TYPES[this.props.Release]][this.props.ReleaseVersion];
  };

  handleDownloadDependency = url => {
    this.props.downloadItem(extractFileNameFromURL(url), Constants.INSTALL_TYPES.Dependency, url, this.onDownloadFileComplete);
  };

  handleDownloadRelease = () => {
    const selectedVersion = this.getSelectedVersion();
    const fileName = selectedVersion + '.zip';
    this.props.downloadItem(fileName, Constants.INSTALL_TYPES.Release, this.props.LocationsLookup[selectedVersion].urls[0], this.onDownloadFileComplete);
  };

  handleDownloadUpgrade = () => {
    const url = this.props.UpgradeData.urls[0];
    const name = this.props.Platform === 'win32' ? 'upgrade.exe' : extractFileNameFromURL(url);
    this.props.downloadItem(name, Constants.INSTALL_TYPES.Upgrade, url, this.onDownloadFileComplete);
  };

  installDependency = result => {
    if (result.Success) {
      this.props.installDependency(result.Destination, result.URL, this.onInstallDependencyComplete);
    }
  };

  installRelease = result => {
    if (result.Success) {
      const selectedVersion = this.getSelectedVersion();
      this.props.installRelease(result.Destination, selectedVersion, () => {
        if (this._isMounted) {
          this.checkVersionInstalled();
        }
      });
    }
  };

  installUpgrade = result => {
    if (result.Success) {
      const info = this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]];
      const sha256 = info.sha256;
      const signature = info.sig;
      this.props.installUpgrade(result.Destination, sha256, signature, !!result.SkipVerification, this.onInstallUpgradeComplete);
    }
  };
  
  onDownloadFileComplete = (name, type, url, result) => {
    if (this._isMounted) {
      switch (type) {
        case Constants.INSTALL_TYPES.Dependency:
          this.installDependency(result);
          break;
        case Constants.INSTALL_TYPES.Release:
          this.installRelease(result);
          break;
        case Constants.INSTALL_TYPES.Upgrade:
          this.installUpgrade(result);
          break;
        default:
          this.props.notifyError('Unknown download type: ' + type);
          break;
      }
    }
  };
  
  onInstallDependencyComplete = () => {
    if (this._isMounted) {
      this.checkVersionInstalled();
    }
  };
  
  onInstallUpgradeComplete = (source, result) => {
    if (this._isMounted && !result.Success) {
      this.props.notifyError(result.Error, false,() => {
        if (this._isMounted) {
          // TODO Prompt to verify
          if (result.AllowSkipVerification) {
            this.installUpgrade({
              SkipVerification: true,
              Source: source,
              Success: true,
            });
          }
        }
      }, false);
    }
  };
  
  saveState = () => {
    if (this.props.AppReady) {
      let state = {
        Release: this.props.Release,
        Version: this.props.ReleaseVersion,
      };

      for (const provider of Constants.PROVIDER_LIST) {
        state[provider] = this.props.ProviderState[provider];
      }

      this.sendRequest(Constants.IPC_Save_State, {
        State: state
      });
    }
  };

  updateCheckScheduledJob = () => {
    if (this.props.AppPlatform !== 'unknown') {
      this.detectUpgrades();
    }
  };

  render() {
    const selectedVersion = this.getSelectedVersion();

    const downloadEnabled = this.props.AllowDownload &&
      !this.props.MountsBusy &&
      !this.props.DownloadActive &&
      (selectedVersion !== 'unavailable') &&
      (selectedVersion !== this.props.InstalledVersion);

    const missingDependencies = (this.props.MissingDependencies.length > 0);
    const allowMount = this.props.InstalledVersion !== 'none' &&
      !missingDependencies &&
      !this.props.InstallActive;

    const allowConfig = this.props.LocationsLookup[selectedVersion] &&
      this.props.LocationsLookup[selectedVersion].config_support;

    const allowSiaPrime =  this.props.LocationsLookup[selectedVersion] &&
      this.props.LocationsLookup[selectedVersion].siaprime_support;

    const noConsoleSupported =  this.props.LocationsLookup[selectedVersion] &&
      this.props.LocationsLookup[selectedVersion].no_console_supported;

    const showConfig = !missingDependencies &&
      this.props.DisplayConfiguration &&
      allowConfig;

    const showUpgrade = this.props.UpgradeAvailable &&
      !this.props.DisplayError &&
      !showConfig &&
      !this.props.DownloadActive &&
      !this.props.UpgradeDismissed;

    const showDependencies = !showUpgrade &&
      missingDependencies &&
      !this.props.DownloadActive;

    const configDisplay = this.createModalConditionally(showConfig, <Configuration version={selectedVersion} />);
    const dependencyDisplay = this.createModalConditionally(showDependencies, <DependencyList onDownload={this.handleDownloadDependency}/>);
    const downloadDisplay = this.createModalConditionally(this.props.DownloadActive, <DownloadProgress/>);
    const errorDisplay = this.createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true);
    const upgradeDisplay = this.createModalConditionally(showUpgrade, <UpgradeUI upgrade={this.handleDownloadUpgrade}/>);

    let mainContent = [];
    if (this.props.AppReady) {
      let key = 0;
      mainContent.push((
        <div key={'rvd_' + key++}
             style={{height: '32%'}}>
          <ReleaseVersionDisplay downloadClicked={this.handleDownloadRelease}
                                 downloadDisabled={!downloadEnabled}/>
        </div>
      ));

      if (allowMount) {
        mainContent.push((
          <div key={'md_' + key++}>
            <MountItems allowConfig={allowConfig}
                        allowSiaPrime={allowSiaPrime}
                        noConsoleSupported={noConsoleSupported}/>
          </div>
        ));
      }
    } else {
      mainContent = <Loading/>
    }

    return (
      <div className={'App'}>
        {errorDisplay}
        {dependencyDisplay}
        {upgradeDisplay}
        {downloadDisplay}
        {configDisplay}
        <div className={'AppContainer'}>
          <div className={'AppHeader'}>
            <Box>
              <Grid>
                <Text col={0}
                      colSpan={'remain'}
                      row={0}
                      rowSpan={'remain'}
                      text={'Repertory UI v' + this.props.Version}
                      textAlign={'center'}
                      type={'Heading1'}/>
                <UpgradeIcon
                  available={this.props.UpgradeAvailable}
                  clicked={()=>this.props.setDismissUIUpgrade(false)}
                  col={dimensions => dimensions.columns - 6}
                  colSpan={5}
                  row={1}
                  rowSpan={remain=>remain - 1}/>
              </Grid>
            </Box>
          </div>
          <div className={'AppContent'}>
            <Box dxStyle={{padding: '8px 8px 0px 8px'}}>
              {mainContent}
            </Box>
          </div>
        </div>
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    AllowDownload: state.download.AllowDownload,
    AppPlatform: state.common.AppPlatform,
    AppReady: state.common.AppReady,
    DisplayConfiguration: state.mounts.DisplayConfiguration,
    DisplayError: state.error.DisplayError,
    DownloadActive: state.download.DownloadActive,
    DownloadType: state.download.DownloadType,
    InstallActive: state.install.InstallActive,
    InstallType: state.install.InstallType,
    InstalledVersion: state.relver.InstalledVersion,
    LocationsLookup: state.relver.LocationsLookup,
    MissingDependencies: state.install.MissingDependencies,
    MountsBusy: state.mounts.MountsBusy,
    Platform: state.common.Platform,
    ProviderState: state.mounts.ProviderState,
    Release: state.relver.Release,
    ReleaseVersion: state.relver.Version,
    UpgradeAvailable: state.relver.UpgradeAvailable,
    UpgradeData: state.relver.UpgradeData,
    UpgradeDismissed: state.relver.UpgradeDismissed,
    Version: state.common.Version,
    VersionLookup: state.relver.VersionLookup,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    checkInstalled: (dependencies, version) => dispatch(checkInstalled(dependencies, version)),
    detectUIUpgrade: () => dispatch(detectUIUpgrade()),
    downloadItem: (name, type, url, completedCallback) => dispatch(downloadItem(name, type, url, completedCallback)),
    installDependency: (source, url, completedCallback) => dispatch(installDependency(source, url, completedCallback)),
    installRelease: (source, version, completedCallback) => dispatch(installRelease(source, version, completedCallback)),
    installUpgrade: (source, sha256, signature, skipVerification, completedCallback) => dispatch(installUpgrade(source, sha256, signature, skipVerification, completedCallback)),
    loadReleases: ()=> dispatch(loadReleases()),
    notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
    setActiveRelease: (release, version) => dispatch(setActiveRelease(release, version)),
    setAllowDownload: allow => dispatch(setAllowDownload(allow)),
    setDismissUIUpgrade: dismiss => dispatch(setDismissUIUpgrade(dismiss)),
    setInstalledVersion: version => dispatch(setInstalledVersion(version)),
    setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
    setReleaseUpgradeAvailable: available => dispatch(setReleaseUpgradeAvailable(available)),
  };
};

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