// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

import PropTypes from 'prop-types';
import React from 'react';
import { Button } from 'react-bootstrap';
import FauxtonAPI from '../../../../core/api';
import FauxtonComponents from '../../../fauxton/components';
import GeneralComponents from '../../../components/react-components';
import AttachmentsPanelButton from './AttachmentsPanelButton';
import CloneDocModal from './CloneDocModal';
import PanelButton from './PanelButton';
import UploadModal from './UploadModal';
import PreferencesButton from './PreferencesButton';


export default class DocEditorScreen extends React.Component {
  static defaultProps = {
    database: {},
    isNewDoc: false
  };

  static propTypes = {
    isLoading: PropTypes.bool.isRequired,
    isSaving: PropTypes.bool.isRequired,
    isNewDoc: PropTypes.bool.isRequired,
    isDbPartitioned: PropTypes.bool.isRequired,
    doc: PropTypes.object,
    conflictCount: PropTypes.number.isRequired,
    previousUrl: PropTypes.string,
    saveDoc: PropTypes.func.isRequired,

    isCloneDocModalVisible: PropTypes.bool.isRequired,
    database: PropTypes.object,
    showCloneDocModal: PropTypes.func.isRequired,
    hideCloneDocModal: PropTypes.func.isRequired,
    cloneDoc: PropTypes.func.isRequired,

    isDeleteDocModalVisible: PropTypes.bool.isRequired,
    showDeleteDocModal: PropTypes.func.isRequired,
    hideDeleteDocModal: PropTypes.func.isRequired,
    deleteDoc: PropTypes.func.isRequired,

    isUploadModalVisible: PropTypes.bool.isRequired,
    uploadInProgress: PropTypes.bool.isRequired,
    uploadPercentage: PropTypes.number.isRequired,
    uploadErrorMessage: PropTypes.string,
    numFilesUploaded: PropTypes.number.isRequired,
    showUploadModal: PropTypes.func.isRequired,
    hideUploadModal: PropTypes.func.isRequired,
    cancelUpload: PropTypes.func.isRequired,
    resetUploadModal: PropTypes.func.isRequired,
    uploadAttachment: PropTypes.func.isRequired,

    wordWrapEnabled: PropTypes.bool.isRequired,
    toggleWordWrap: PropTypes.func.isRequired,
  };

  getCodeEditor = () => {
    if (this.props.isLoading) {
      return (<GeneralComponents.LoadLines />);
    }

    const docContent = this.props.doc.attributes;
    if (this.props.isDbPartitioned) {
      if (!docContent._id.includes(':') && !docContent._id.startsWith('_design')) {
        docContent._id = ':' + docContent._id;
      }
    }
    const code = JSON.stringify(docContent, null, '  ');
    const editorCommands = [{
      name: 'save',
      bindKey: { win: 'Ctrl-S', mac: 'Ctrl-S' },
      exec: this.saveDoc
    }];

    return (
      <GeneralComponents.CodeEditor
        id="doc-editor"
        ref={node => this.docEditor = node}
        defaultCode={code}
        disabled={this.props.isSaving}
        mode="json"
        autoFocus={true}
        editorCommands={editorCommands}
        notifyUnsavedChanges={true}
        stringEditModalEnabled={true}
        wordWrapEnabled={this.props.wordWrapEnabled}/>
    );
  };

  UNSAFE_componentWillUpdate(nextProps) {
    // Update the editor whenever a file is uploaded, a doc is cloned, or a new doc is loaded
    if (this.props.numFilesUploaded !== nextProps.numFilesUploaded ||
        this.props.doc && this.props.doc.hasChanged() ||
        (this.props.doc && nextProps.doc && this.props.doc.id !== nextProps.doc.id)) {
      const editor = this.getEditor();
      if (editor && nextProps.doc) {
        this.getEditor().setValue(JSON.stringify(nextProps.doc.attributes, null, '  '));
      }
      this.onSaveComplete();
    }
  }

  saveDoc = () => {
    this.props.saveDoc(this.props.doc, this.checkDocIsValid(), this.onSaveComplete, this.props.previousUrl);
  };

  onSaveComplete = () => {
    if (this.getEditor()) {
      this.getEditor().clearChanges();
    }
  };

  hideDeleteDocModal = () => {
    this.props.hideDeleteDocModal();
  };

  deleteDoc = () => {
    this.props.hideDeleteDocModal();
    this.props.deleteDoc(this.props.doc);
  };

  getEditor = () => {
    return (this.docEditor) ? this.docEditor.getEditor() : null;
  };

  checkDocIsValid = () => {
    if (this.getEditor().hasErrors()) {
      return false;
    }
    const json = JSON.parse(this.getEditor().getValue());
    this.props.doc.clear().set(json, { validate: true });

    return !this.props.doc.validationError;
  };

  clearChanges = () => {
    if (this.docEditor) {
      this.docEditor.clearChanges();
    }
  };

  getExtensionIcons = () => {
    var extensions = FauxtonAPI.getExtensions('DocEditor:icons');
    return _.map(extensions, (Extension, i) => {
      return (<Extension doc={this.props.doc} key={i} database={this.props.database} />);
    });
  };

  getButtonRow = () => {
    if (this.props.isNewDoc || this.props.doc === null) {
      return false;
    }
    return (
      <div>
        <PreferencesButton
          wordWrapEnabled={this.props.wordWrapEnabled}
          toggleWordWrap={this.props.toggleWordWrap} />

        <AttachmentsPanelButton
          doc={this.props.doc}
          isLoading={this.props.isLoading}
          disabled={this.props.isSaving} />
        <div className="doc-editor-extension-icons">{this.getExtensionIcons()}</div>

        {this.props.conflictCount ? <PanelButton
          title={`Conflicts (${this.props.conflictCount})`}
          iconClass="fonticon-columns"
          className="conflicts"
          disabled={this.props.isSaving}
          onClick={() => { FauxtonAPI.navigate(FauxtonAPI.urls('revision-browser', 'app', this.props.database.safeID(), this.props.doc.id));}}/> : null}

        <PanelButton className="upload"
          title="Upload Attachment"
          iconClass="fonticon-up-circled"
          disabled={this.props.isSaving}
          onClick={this.props.showUploadModal} />
        <PanelButton title="Clone Document"
          iconClass="fonticon-cw"
          disabled={this.props.isSaving}
          onClick={this.props.showCloneDocModal} />
        <PanelButton title="Delete"
          iconClass="fonticon-trash"
          disabled={this.props.isSaving}
          onClick={this.props.showDeleteDocModal} />
      </div>
    );
  };

  render() {
    const saveButtonLabel = this.props.isSaving ?
      'Saving...' :
      (this.props.isNewDoc ? 'Create Document' : 'Save Changes');
    const endpoint = this.props.previousUrl ?
      this.props.previousUrl :
      FauxtonAPI.urls('allDocs', 'app', FauxtonAPI.url.encode(this.props.database.id));
    return (
      <div>
        <div id="doc-editor-actions-panel">
          <div className="doc-actions-left">
            <Button id="save-doc-btn" disabled={this.props.isSaving} variant="cf-primary" onClick={this.saveDoc}>
              <i className="fonticon-ok-circled"></i> {saveButtonLabel}
            </Button>
            <div>
              <Button href={this.props.isSaving ? undefined : `#/${endpoint}`}
                variant="cf-cancel" className="js-back cancel-button" disabled={this.props.isSaving}>
                  Cancel
              </Button>
            </div>
          </div>
          <div className="alignRight">
            {this.getButtonRow()}
          </div>
        </div>

        <div className="code-region">
          <div className="bgEditorGutter"></div>
          <div id="editor-container" className="doc-code">{this.getCodeEditor()}</div>

        </div>

        <UploadModal
          ref={node => this.uploadModal = node}
          visible={this.props.isUploadModalVisible}
          doc={this.props.doc}
          inProgress={this.props.uploadInProgress}
          uploadPercentage={this.props.uploadPercentage}
          errorMessage={this.props.uploadErrorMessage}
          cancelUpload={this.props.cancelUpload}
          hideUploadModal={this.props.hideUploadModal}
          resetUploadModal={this.props.resetUploadModal}
          uploadAttachment={this.props.uploadAttachment}/>
        <CloneDocModal
          doc={this.props.doc}
          database={this.props.database}
          visible={this.props.isCloneDocModalVisible}
          onSubmit={this.clearChanges}
          hideCloneDocModal={this.props.hideCloneDocModal}
          cloneDoc={this.props.cloneDoc}/>
        <span id='hey'>bb</span>
        <FauxtonComponents.ConfirmationModal
          title="Confirm Deletion"
          visible={this.props.isDeleteDocModalVisible}
          text="Are you sure you want to delete this document?"
          onClose={this.hideDeleteDocModal}
          onSubmit={this.deleteDoc}
          successButtonLabel="Delete Document" />
      </div>
    );
  }
}
