text: /// ////////////////////////////////////////////////////////
  // DataManagement Viewer Extension
  // By Philippe Leefsma, Autodesk Inc, July 2017
  //
  /// ////////////////////////////////////////////////////////
  import MultiModelExtensionBase from 'Viewer.MultiModelExtensionBase'
  import FolderSearchPanel from './FolderSearchPanel'
  import DerivativesAPI from './Derivatives.API'
  import WidgetContainer from 'WidgetContainer'
  import { history as browserHistory } from 'BrowserContext'
  import { Tabs, Tab } from 'react-bootstrap'
  import DataTreeView from './DataTreeView'
  import { ServiceContext } from 'ServiceContext'
  import DMUploader from './DMUploader'
  import { ReactLoader } from 'Loader'
  import Measure from 'react-measure'
  import DOMPurify from 'dompurify'
  import ReactDOM from 'react-dom'
  import DMAPI from './DM.API'
  import Label from 'Label'
  import React from 'react'
  
  class DataManagementExtension extends MultiModelExtensionBase {
    /// //////////////////////////////////////////////////////
    // Class constructor
    //
    /// //////////////////////////////////////////////////////
    constructor (viewer, options) {
      super(viewer, options)
  
      this.onVersionSelected = this.onVersionSelected.bind(this)
      this.onItemNodeCreated = this.onItemNodeCreated.bind(this)
      this.onUploadComplete = this.onUploadComplete.bind(this)
      this.onUploadProgress = this.onUploadProgress.bind(this)
      this.onFolderSearch = this.onFolderSearch.bind(this)
      this.onFolderUpload = this.onFolderUpload.bind(this)
      this.onCreateFolder = this.onCreateFolder.bind(this)
      this.onLoadViewable = this.onLoadViewable.bind(this)
      this.onTabSelected = this.onTabSelected.bind(this)
      this.onInitUpload = this.onInitUpload.bind(this)
      this.onDeleteItem = this.onDeleteItem.bind(this)
  
      this.derivativesAPI = new DerivativesAPI({
        apiUrl: '/api/derivatives/3legged'
      })
  
      this.dmAPI = new DMAPI({
        apiUrl: '/api/dm'
      })
  
      this.react = options.react
    }
  
    /// //////////////////////////////////////////////////////
    // Load callback
    //
    /// //////////////////////////////////////////////////////
    load () {
      if (!this.viewer.model) {
        this.viewer.container.classList.add('empty')
      }
  
      this.react.setState({
  
        user: this.options.appState.user,
        activeTabKey: null,
        showTitle: false,
        tabsWidth: 0,
        hubs: null
  
      }).then(async () => {
        await this.react.pushRenderExtension(this)
  
        if (!this.options.appState.user) {
          try {
            const user = await this.forgeSvc.getUser()
  
            this.react.setState({
              user
            })
          } catch (ex) {
            return this.showLogin()
          }
        }
  
        this.options.loader.show(false)
  
        const hubsRes = await this.dmAPI.getHubs()
  
        this.react.setState({
          hubs: hubsRes.data
        })
      })
  
      this.socketSvc.on(
        'dm.upload.complete',
        this.onUploadComplete)
  
      console.log('Viewing.Extension.DataManagement loaded')
  
      return true
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    get className () {
      return 'data-management'
    }
  
    /// //////////////////////////////////////////////////////
    // Extension Id
    //
    /// //////////////////////////////////////////////////////
    static get ExtensionId () {
      return 'Viewing.Extension.DataManagement'
    }
  
    /// //////////////////////////////////////////////////////
    // Unload callback
    //
    /// //////////////////////////////////////////////////////
    unload () {
      console.log('Viewing.Extension.DataManagement loaded')
  
      this.socketSvc.off(
        'dm.upload.complete',
        this.onUploadComplete)
  
      super.unload()
  
      return true
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    async setNodeViewerUrn (node, urn) {
      try {
        if (urn && urn != 'null') {
          const manifest =
            await this.derivativesAPI.getManifest(urn)
  
          if (this.derivativesAPI.hasDerivative(
            manifest, { type: 'geometry' })) {
            node.setViewerUrn(urn)
          }
        }
      } catch (ex) {
        console.log(ex)
      }
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    async setNodeThumbnail (node, urn) {
      try {
        if (urn && urn != 'null') {
          const thumbnail =
          await this.derivativesAPI.getThumbnail(urn, {
            size: 200, base64: true
          })
  
          const base64 = `data:image/png;base64,${thumbnail}`
  
          node.setThumbnail(base64)
  		  }
      } catch (ex) {
        console.log(ex)
      }
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    async onDeleteItem (node) {
      const onClose = (result) => {
        this.dialogSvc.off('dialog.close', onClose)
  
        if (result == 'OK') {
          this.dmAPI.deleteItem(
            node.projectId,
            node.itemId)
        }
      }
  
      this.dialogSvc.on('dialog.close', onClose)
  
      this.dialogSvc.setState({
        className: 'item-delete-dlg',
        title: 'Delete Item ...',
        content:
    <div>
            Are you sure you want to delete
      <br />
      {node.name} ?
    </div>,
        open: true
      })
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    async onItemNodeCreated (node) {
      node.showLoader(true)
  
      const versionsRes =
        await this.dmAPI.getItemVersions(
          node.props.projectId,
          node.props.itemId)
  
      const versions = versionsRes.data
  
      if (versions.length) {
        const version = versions[0]
  
        node.setActiveVersion(version)
  
        const urn = encodeURIComponent(
          this.dmAPI.getVersionURN(version))
  
        // fix for BIM Docs:
        // displayName doesn't appear in item
  
        if (!node.name.length) {
          const { displayName } = version.attributes
  
          node.setName(displayName)
        }
  
        await this.setNodeViewerUrn(node, urn)
  
        node.setVersions(versions)
  
        await this.setNodeThumbnail(node, urn)
  
        this.emit('item.created', node)
      }
  
      node.showLoader(false)
  
      node.setLoaded(true)
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    async onVersionNodeCreated (event) {
      event.node.showLoader(true)
  
      const versionRes =
        await this.dmAPI.getVersion(
          event.projectId,
          event.versionId)
  
      const version = versionRes.data
  
      event.node.setActiveVersion(version)
  
      const urn = this.dmAPI.getVersionURN(version)
      // fix for BIM Docs:
      // displayName doesn't appear in item
  
      if (!event.name.length) {
        const { displayName } = version.attributes
  
        event.node.setName(displayName)
      }
  
      await this.setNodeViewerUrn(event.node, urn)
  
      await this.setNodeThumbnail(event.node, urn)
  
      event.showLoader(false)
  
      event.setLoaded(true)
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    onVersionSelected (node) {
  
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    getVersionFileType (version) {
      if (version.attributes.fileType) {
        return version.attributes.fileType
      } else if (version.relationships.storage) {
        const fileId = version.relationships.storage.data.id
  
        return fileId.split('.').pop(-1)
      } else if (version.attributes.name) {
        return version.attributes.name.split('.').pop(-1)
      }
  
      return 'unknown'
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
  
    async onLoadViewable (event) {
      try {
        event.node.showLoader(true)
  
        const loader = this.viewer.getExtension('Viewing.Extension.ModelLoader')
  
        const version = event.node.activeVersion
        const options = {
          parentControl: 'modelTools',
          forceMaterialProp: true,
          materialCategories: [
            'Material'
          ],
          properties: [
            'Date Created',
            'Density',
            'Description',
            'Material',
            'Part Number'
          ],
          modelUrn: event.viewerUrn,
          react: this.react,
          database: 'rcdb'
        }
        await Promise.all([this.viewer.loadDynamicExtension('Viewing.Extension.Database.Table', Object.assign(options, {
          apiUrl: '/api/materials',
          flex: 0.4
        })),
  
        this.viewer.loadDynamicExtension('Viewing.Extension.Database.CostBreakdown', options),
  
        this.viewer.loadDynamicExtension('Viewing.Extension.ViewerProperties', options),
        this.viewer.loadDynamicExtension('Viewing.Extension.VisualReport', options),
        this.viewer.loadDynamicExtension('Viewing.Extension.Markup3D', options),
        this.viewer.loadDynamicExtension('Viewing.Extension.Transform', options)])
        await loader.unloadModel(true)
        await loader.loadModel({
          fileType: this.getVersionFileType(version),
          name: event.name.split('.')[0],
          _id: this.options.dbModel._id,
          env: 'AutodeskProduction',
          database: 'configurator',
          model: {
            proxy: 'lmv-proxy-3legged',
            urn: event.viewerUrn
          }
        })
      } catch (ex) {
        console.log(ex)
      } finally {
        event.node.showLoader(false)
      }
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    onInitUpload (data) {
      this.dialogSvc.setState({
        open: false
      })
  
      const notification = this.notifySvc.add({
        title: 'Uploading ' + data.file.name,
        message: 'progress: 0%',
        dismissible: false,
        status: 'loading',
        id: data.uploadId,
        dismissAfter: 0,
        position: 'tl'
      })
  
      notification.buttons = [{
        name: 'Hide',
        onClick: () => {
          notification.dismissAfter = 1
          this.notifySvc.update(notification)
        }
      }]
  
      this.notifySvc.update(notification)
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    onUploadProgress (data) {
      const notification =
        this.notifySvc.getNotification(data.uploadId)
  
      if (!notification.forgeUpload) {
        const progress = data.percent * 0.5
  
        notification.message =
          `progress: ${progress.toFixed(2)}%`
  
        this.notifySvc.update(notification)
      }
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    onUploadComplete (data) {
      this.emit('upload.complete', data)
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    onFolderSearch (data) {
      const panel = new FolderSearchPanel(data, {
        menuContainer: this.options.appContainer,
        derivativesAPI: this.derivativesAPI,
        dmAPI: this.dmAPI
      })
  
      panel.on('panel.close', (panelId) => {
        this.react.popViewerPanel(panelId)
      })
  
      panel.on('version.created', (node) => {
        this.onVersionNodeCreated(node)
      })
  
      panel.on('item.created', (node) => {
        this.onItemNodeCreated(node)
      })
  
      panel.on('load.viewable', (node) => {
        this.onLoadViewable(node)
      })
  
      this.react.pushViewerPanel(panel, {
        className: panel.className,
        minWidth: 435,
        height: 480,
        width: 450
      })
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    onFolderUpload (data) {
      const onClose = (result) => {
        this.dialogSvc.off('dialog.close', onClose)
  
        if (result === 'OK') {
  
        }
      }
  
      this.dialogSvc.on('dialog.close', onClose)
  
      this.dialogSvc.setState({
        className: 'folder-upload-dlg',
        title: 'Upload to Folder ...',
        content:
    <DMUploader
      onProgress={this.onUploadProgress}
      onInitUpload={this.onInitUpload}
      projectId={data.projectId}
      folderId={data.folderId}
      hubId={data.hubId}
      nodeId={data.id}
      api={this.dmAPI}
    />,
        showOK: false,
        open: true
      })
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    onCreateFolder (data) {
      const onClose = (result) => {
        this.dialogSvc.off('dialog.close', onClose)
  
        if (result === 'OK') {
  
        }
      }
  
      this.dialogSvc.on('dialog.close', onClose)
  
      this.dialogSvc.setState({
        className: 'folder-create-dlg',
        title: 'Create Folder ...',
        content:
    <div />,
        open: true
      })
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    async setDocking (docked) {
      const id = DataManagementExtension.ExtensionId
  
      if (docked) {
        await this.react.popRenderExtension(id)
  
        this.react.pushViewerPanel(this, {
          height: 250,
          width: 350
        })
      } else {
        await this.react.popViewerPanel(id)
  
        this.react.pushRenderExtension(this)
      }
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    showLogin () {
      const onClose = (result) => {
        this.dialogSvc.off('dialog.close', onClose)
  
        if (result === 'OK') {
          this.forgeSvc.login()
          return
        }
  
        browserHistory.push('/configurator')
      }
  
      this.dialogSvc.on('dialog.close', onClose)
  
      this.dialogSvc.setState({
        onRequestClose: () => {},
        className: 'login-dlg',
        title: 'Forge Login required ...',
        content:
    <div>
            Press OK to login ...
    </div>,
        open: true
      })
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    renderTitle (docked) {
      const spanClass = docked
        ? 'fa fa-chain-broken'
        : 'fa fa-chain'
  
      return (
        <div className='title'>
          <label>
            Data Management
          </label>
          <div className='data-management-controls'>
            {/* <button onClick={() => this.setDocking(docked)}
              title="Toggle docking mode">
              <span className={spanClass}/>
            </button> */}
          </div>
        </div>
      )
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    onTabSelected (tabKey) {
      this.react.setState({
        activeTabKey: tabKey
      })
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    renderHubs (hubs) {
      const { activeTabKey, tabsWidth } = this.react.getState()
  
      const tabs = hubs.map((hub) => {
        const hubHeader = this.dmAPI.getHubHeader(hub)
  
        const style = {
          width:
            `${Math.floor((tabsWidth - 8) / hubs.length - 15)}px`
        }
  
        const title =
          <label style={style}>
            {`${hubHeader}: ${hub.attributes.name}`}
          </label>
  
        return (
          <Tab
            className='tab-container'
            eventKey={hub.id}
            title={title}
            key={hub.id}
          >
            {
              <DataTreeView
                onItemNodeCreated={this.onItemNodeCreated}
                menuContainer={this.options.appContainer}
                onFolderUpload={this.onFolderUpload}
                onFolderSearch={this.onFolderSearch}
                onCreateFolder={this.onCreateFolder}
                derivativesAPI={this.derivativesAPI}
                onLoadViewable={this.onLoadViewable}
                onDeleteItem={this.onDeleteItem}
                dmAPI={this.dmAPI}
                dmEvents={this}
                hub={hub}
              />
            }
          </Tab>
        )
      })
  
      return (
        <Measure
          bounds onResize={(rect) => {
            this.react.setState({
              tabsWidth: rect.bounds.width
            })
          }}
        >
          {
            ({ measureRef }) =>
              <div ref={measureRef} className='tabs-container'>
                <Tabs
                  activeKey={activeTabKey || hubs[0].id}
                  onSelect={this.onTabSelected}
                  className='tabs'
                  id='hubs-tab'
                >
                  {tabs}
                </Tabs>
              </div>
          }
        </Measure>
      )
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    renderContent () {
      const { hubs } = this.react.getState()
  
      const showLoader = !hubs
  
      return (
        <div className='content'>
          <ReactLoader show={showLoader} />
          {hubs && hubs.length && this.renderHubs(hubs)}
        </div>
      )
    }
  
    /// //////////////////////////////////////////////////////
    //
    //
    /// //////////////////////////////////////////////////////
    render (opts) {
      const { showTitle } = this.react.getState()
  
      return (
        <WidgetContainer
          renderTitle={() => this.renderTitle(opts.docked)}
          className={this.className}
          showTitle={showTitle}
        >
  
          {this.renderContent()}
  
        </WidgetContainer>
      )
    }
  }
  
  Autodesk.Viewing.theExtensionManager.registerExtension(
    DataManagementExtension.ExtensionId,
    DataManagementExtension)
  
  export default 'Viewing.Extension.DataManagement'
  