
var classNames = require('classnames');
var AppActions = require('../actions/AppActions');
var Files = require('../stores/Files');
var React = require('react');
var ReactBootstrap = require('react-bootstrap');

var Glyphicon = ReactBootstrap.Glyphicon;

// The DepotTree is a React tree control that expects to interact with a
// server API that doesn't like to provide tree data, just lists of one
// level at a time.
//
// This control creates it's own cache of the available depot, since our 'store'
// doesn't really know much about the existing server. What this means, is that
// all directories are generally expandable, even if they don't have children,
// and we won't know if the dir contains anything until they are expanded.
//
// Properties:
//  - dirsOnly: when true, will not display files
//
var DepotTree = React.createClass({

  getInitialState: function() {
    return {
      depots: [],
      selected: null
    };
  },

  componentDidMount: function() {
    Files.addDepotsListedListener(this.handleDepotsListed);
    Files.addDirListedListener(this.handleDirListed);

    AppActions.listDepots();
  },

  componentWillUnmount: function() {
    Files.removeDepotsListedListener(this.handleDepotsListed);
    Files.removeDirListedListener(this.handleDirListed);
  },

  render: function() {
    var self = this;
    return (
      <div className='depot-tree'>
        {this.state.depots.map(function(x) { return renderItem(self, x); })}
      </div>
    );
  },

  handleDepotsListed: function(depots) {
    depots.sort(function(x, y) { return x.name.localeCompare(y.name); });
    this.setState({ depots: depots });
  },

  handleDirListed: function(dirPath, items) {
    var dir = findDir(this.state.depots, dirPath);
    items.forEach(function(i) { dir.insert(i) });
    dir.sortChildren();
    dir.loaded = true;
    this.setState({ depots: this.state.depots });
  },

  openFolder: function(dir, evt) {
    dir.expanded = true;
    if (!dir.loaded) {
      AppActions.listDir(dir.pathId.join('/'));
    }
    this.setState({ depots: this.state.depots });

    evt.stopPropagation();
  },

  closeFolder: function(dir, evt) {
    dir.expanded = false;
    this.setState({ depots: this.state.depots });

    evt.stopPropagation();
  },

  clickItem: function(dir, evt) {
    var cur = this.state.selected;

    if (cur && cur.pathId.join('/') == dir.pathId.join('/')) {
      // Same node
      dir.selected = false;
      this.state.selected = null;
    } else {
      if (cur) {
        cur.selected = false;
      }
      dir.selected = true;
      this.state.selected = dir;
    }

    if (this.state.selected && typeof this.props.onSelect == "function") {
      this.props.onSelect(this.state.selected);
    }

    this.setState({ depots: this.state.depots });

    evt.stopPropagation();
  }
});

//----------------------------------------------------------------------------
// Local (private) helper methods
//----------------------------------------------------------------------------

// Render a row of our depot tree, and all children
function renderItem(view, item) {
  if (view.props.dirsOnly && !item.children) {
    return null;
  }

  var children = null;

  if (item.children && item.expanded) {
    children = item.children.map(function(child) {
      return renderItem(view, child);
    });
  }

  var control = renderControl(view, item);

  var icon = renderIcon(item);

  var text = renderText(item);

  var classes = classNames({
    'depot-tree-item': true,
    'selected': item.selected
  });

  return (
    <div key={item.pathId.join('-')} className={classes}
      onClick={view.clickItem.bind(view, item)}>
      <div className='depot-tree-item-value'>
        {control}
        {icon}
        {text}
      </div>
      <div className='depot-tree-children'>
        {children}
      </div>
    </div>
  )
}

// The control depends on the 'expanded' attribute being set to 'true'.
function renderControl(view, item) {
  if (item.children) {
    if (item.expanded) {
      return <Glyphicon glyph='triangle-bottom' onClick={view.closeFolder.bind(view, item)} />;
    } else {
      return <Glyphicon glyph='triangle-right' onClick={view.openFolder.bind(view, item)} />;
    }
  }
}

function renderIcon(item) {
  if (item.children) {
    if (item.expanded) {
      return <Glyphicon glyph='folder-open' />
    } else {
      return <Glyphicon glyph='folder-close' />
    }
  } else {
    return <Glyphicon glyph='file' />
  }
}

// We *might* want to render folders in a different font than files. We'll see.
function renderText(item) {
  return <span>{ item.name }</span>;
}

function findDir(depots, dirPath) {
  if (dirPath.startsWith('//')) {
    dirPath = dirPath.substring(2);
  }
  var paths = dirPath.split('/');
  return findDirRecurse(depots, paths, 0);
}

function findDirRecurse(items, paths, level) {
  var curName = paths[level];

  var item = items.find(function(x) { return x.name == curName; });

  // Halting condition. Item can be null, in which case we've not found it.
  if (level == (paths.length - 1)) {
    return item;
  }

  if (item && item.children) {
    return findDirRecurse(item.children, paths, level + 1);
  }

  return null;
}

module.exports = DepotTree;
