import React from 'react';
import './Configuration.css';
import {connect} from 'react-redux';
import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button';
import ConfigurationItem from './ConfigurationItem/ConfigurationItem';
import Modal from '../../components/UI/Modal/Modal';
import IPCContainer from '../IPCContainer/IPCContainer';
import {displayConfiguration} from '../../redux/actions/mount_actions';
import {notifyError} from '../../redux/actions/error_actions';

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

class Configuration extends IPCContainer {
  _isMounted = false;

  state = {
    ChangedItems: [],
    ChangedObjectLookup: null,
    ObjectLookup: {},
    OriginalItemList: [],
    OriginalObjectLookup: {},
    IsRemoteMount: false,
    ItemList: [],
    Saving: false,
    ShowAdvanced: false,
    Template: {}
  };

  checkItemChanged = (itemA, itemB) => {
    if (itemA.type === 'string_array') {
      if (itemA.value.length !== itemB.value.length) {
        return true;
      }
      return itemA.value.filter(i => !itemB.value.includes(i)).length !== 0;
    }
    return itemA.value !== itemB.value;
  };

  checkSaveRequired = () => {
    const changedItems = [];
    let i = 0;
    for (const item of this.state.ItemList) {
      if (this.checkItemChanged(this.state.OriginalItemList[i++], item)) {
        changedItems.push(item);
      }
    }

    let changedObjectLookup = null;
    for (const key of Object.keys(this.state.ObjectLookup)) {
      const changedObjectItems = [];
      let j = 0;
      for (const item of this.state.ObjectLookup[key]) {
        if (this.checkItemChanged(this.state.OriginalObjectLookup[key][j++], item)) {
          changedObjectItems.push(item);
        }
      }

      if (changedObjectItems.length > 0) {
        if (changedObjectLookup === null) {
          changedObjectLookup = {};
        }
        changedObjectLookup[key] = changedObjectItems;
      }
    }

    if ((changedItems.length > 0) || changedObjectLookup) {
      this.setState({
        ChangedItems: changedItems,
        ChangedObjectLookup: changedObjectLookup,
      });
    } else {
      this.props.hideConfiguration();
    }
  };

  componentDidMount() {
    this._isMounted = true;
    this.setRequestHandler(Constants.IPC_Get_Config_Template_Reply, this.onGetConfigTemplateReply);
    this.setRequestHandler(Constants.IPC_Get_Config_Reply, this.onGetConfigReply);
    this.setRequestHandler(Constants.IPC_Set_Config_Values_Reply, this.onSetConfigValuesReply);
    this.sendRequest(Constants.IPC_Get_Config_Template, {
      Provider: this.props.DisplayConfiguration,
      Remote: this.props.DisplayRemoteConfiguration,
      S3: this.props.DisplayS3Configuration,
      Version: this.props.version,
    });
  }

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

  createItemList = (config, template) => {
    const objectList = [];
    const itemList = Object
      .keys(config)
      .map(key => {
        return {
          advanced: template[key] ? template[key].advanced : false,
          hide_remote: template[key] ? template[key].hide_remote : false,
          label: key,
          remote: template[key] ? template[key].remote : false,
          type: template[key] ? template[key].type : null,
          value: (template[key] && (template[key].type === 'object')) ?
            config[key] :
            (template[key] && (template[key].type === 'string_array')) ?
              config[key] :
              config[key].toString(),
        };
      })
      .filter(i=> {
        let ret = template[i.label];
        if (ret && (template[i.label].type === 'object')) {
          objectList.push(i);
          ret = false;
        }
        return ret;
      });
    return {
      ObjectList: objectList,
      ItemList: itemList,
    }
  };

  handleItemChanged = (target, idx) => {
    const itemList = [
      ...this.state.ItemList
    ];
    itemList[idx].value = target.type === 'textarea' ? target.string_array : target.value.toString();
    this.setState({
      ItemList: itemList
    });
  };

  handleObjectItemChanged = (target, name, idx) => {
    const itemList = [
      ...this.state.ObjectLookup[name]
    ];
    const objectLookup = {
      ...this.state.ObjectLookup,
    };

    itemList[idx].value = target.type === 'textarea' ? target.string_array : target.value.toString();
    objectLookup[name] = itemList;
    this.setState({
      ObjectLookup: objectLookup,
    });
  };

  onGetConfigReply = (event, arg) => {
    if (arg.data.Success) {
      const list = this.createItemList(arg.data.Config, this.state.Template);
      const itemListCopy = JSON.parse(JSON.stringify(list.ItemList));

      let objectLookup = {};
      for (const obj of list.ObjectList) {
        const list2 = this.createItemList(obj.value, this.state.Template[obj.label].template);
        objectLookup[obj.label] = list2.ItemList;
      }

      const isRemoteMount = this.props.remoteSupported &&
        JSON.parse(objectLookup['RemoteMount'].find(s => s.label === 'IsRemoteMount').value);
      if (isRemoteMount) {
        for (const obj of list.ObjectList) {
          if (obj.hide_remote) {
            delete objectLookup[obj.label];
          }
        }
      }

      const objectLookupCopy = JSON.parse(JSON.stringify(objectLookup));
      this.setState({
        IsRemoteMount: isRemoteMount,
        ItemList: list.ItemList,
        ObjectLookup: objectLookup,
        OriginalItemList: itemListCopy,
        OriginalObjectLookup: objectLookupCopy,
      }, () => {

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

  onGetConfigTemplateReply = (event, arg) => {
    if (arg.data.Success) {
      this.setState({
        Template: arg.data.Template,
      }, ()=> {
        this.sendRequest(Constants.IPC_Get_Config, {
          Provider: this.props.DisplayConfiguration,
          Remote: this.props.DisplayRemoteConfiguration,
          S3: this.props.DisplayS3Configuration,
          Version: this.props.version,
        });
      });
    } else {
      this.props.notifyError(arg.data.Error, false, () => {
        if (this._isMounted) {
          this.props.hideConfiguration();
        }
      });
    }
  };

  onSetConfigValuesReply = () => {
    this.props.hideConfiguration();
  };

  saveAndClose = () => {
    this.setState({
      Saving: true,
    }, ()=> {
      const changedItems = [];
      for (const item of this.state.ChangedItems) {
        changedItems.push({
          Name: item.label,
          Value: item.type === 'string_array' ?
            item.value.join(';') :
            item.value,
        });
      }

      if (this.state.ChangedObjectLookup) {
        for (const key of Object.keys(this.state.ChangedObjectLookup)) {
          for (const item of this.state.ChangedObjectLookup[key]) {
            changedItems.push({
              Name: key + '.' + item.label,
              Value: item.type === 'string_array' ?
                item.value.join(';') :
                item.value,
            });
          }
        }
      }

      this.sendRequest(Constants.IPC_Set_Config_Values, {
        Items: changedItems,
        Provider: this.props.DisplayConfiguration,
        Remote: this.props.DisplayRemoteConfiguration,
        S3: this.props.DisplayS3Configuration,
        Version: this.props.version,
      });
    });
  };

  showRemoteConfigItem = (item, itemList) => {
    if (item.advanced &&
      item.remote &&
      this.props.remoteSupported &&
      (item.label !== 'IsRemoteMount')) {
      const isRemoteMount = JSON.parse(itemList.find(s => s.label === 'IsRemoteMount').value);
      const enableRemoteMount = !isRemoteMount &&
        JSON.parse(itemList.find(s => s.label === 'EnableRemoteMount').value);
      return (item.label === 'RemoteHostNameOrIp') || (item.label === 'RemoteMaxConnections') ?
        isRemoteMount :
        (item.label === 'RemoteReceiveTimeoutSeconds') || (item.label === 'RemoteSendTimeoutSeconds') || (item.label === 'RemotePort') || (item.label === 'RemoteToken') ?
          isRemoteMount || enableRemoteMount :
          (item.label === 'EnableRemoteMount') ?
            !isRemoteMount :
            enableRemoteMount;
    }
    return false;
  };

  render() {
    let confirmSave = null;
    if ((this.state.ChangedItems.length > 0) || this.state.ChangedObjectLookup) {
      confirmSave = (
        <Modal>
          <Box dxStyle={{width: '40vw', padding: 'var(--default_spacing)'}}>
            <h1 style={{width: '100%', textAlign: 'center'}}>Save Changes?</h1>
            <table width='100%'><tbody>
              <tr>
                <td align='center' width='50%'><Button clicked={this.saveAndClose} disabled={this.state.Saving}>Yes</Button></td>
                <td align='center' width='50%'><Button clicked={this.props.hideConfiguration} disabled={this.state.Saving}>No</Button></td>
              </tr>
            </tbody></table>
          </Box>
        </Modal>
      );
    }

    let autoFocus = true;

    let objectItems = [];
    for (const key of Object.keys(this.state.ObjectLookup)) {
      objectItems.push((
        <div key={key}>
          <h2>{key}</h2>
          <div>
            {
              this.state.ObjectLookup[key].map((k, i) => {
                const shouldFocus = autoFocus;
                autoFocus = false;
                return (
                  (!k.advanced || (this.state.ShowAdvanced && k.advanced && !k.remote) || this.showRemoteConfigItem(k, this.state.ObjectLookup[key])) ?
                    <ConfigurationItem advanced={k.advanced}
                                       autoFocus={shouldFocus}
                                       changed={e=>this.handleObjectItemChanged(e, key, i)}
                                       grouping={key}
                                       items={this.state.Template[key].template[k.label].items}
                                       key={i}
                                       label={k.label}
                                       readOnly={this.state.IsRemoteMount && ((k.label === 'RemoteHostNameOrIp') || (k.label === 'RemotePort'))}
                                       template={this.state.Template[key].template[k.label]}
                                       value={k.value}/> :
                    null)
              })
            }
          </div>
        </div>
      ));
    }

    const configurationItems = this.state.ItemList
      .map((k, i) => {
        const shouldFocus = autoFocus;
        autoFocus = false;
        return (
          ((!this.state.IsRemoteMount || !k.hide_remote) && (!k.advanced || (this.state.ShowAdvanced && k.advanced))) ?
            <ConfigurationItem advanced={k.advanced}
                               autoFocus={shouldFocus}
                               changed={e=>this.handleItemChanged(e, i)}
                               grouping={'Settings'}
                               items={this.state.Template[k.label].items}
                               key={i}
                               label={k.label}
                               template={this.state.Template[k.label]}
                               value={k.value}/> :
            null
        );
      });

    return (
      <div className={'Configuration'}>
        {confirmSave}
        <Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
          <div style={{float: 'right', margin: 0, padding: 0, marginTop: '-4px', boxSizing: 'border-box', display: 'block'}}>
            <a href={'#'}
               onClick={this.checkSaveRequired}
               style={{cursor: 'pointer'}}>X</a>
          </div>
          <h1 style={{width: '100%', textAlign: 'center'}}>{(
            this.props.DisplayRemoteConfiguration ?
              this.props.DisplayConfiguration.substr(6) :
              this.props.DisplayConfiguration) + ' Configuration'}</h1>
          <div style={{overflowY: 'auto', height: '90%'}}>
            {objectItems}
            {(configurationItems.length > 0) ? <h2>Settings</h2> : null}
            {configurationItems}
          </div>
        </Box>
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    DisplayConfiguration: state.mounts.DisplayConfiguration,
    DisplayRemoteConfiguration: state.mounts.DisplayRemoteConfiguration,
    DisplayS3Configuration: state.mounts.DisplayS3Configuration,
    Platform: state.common.Platform,
  }
};

const mapDispatchToProps = dispatch => {
  return {
    notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
    hideConfiguration: () => dispatch(displayConfiguration(null, false)),
  }
};

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