package com.unnamed.b.atv.view;
import android.content.Context;
import android.text.TextUtils;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import com.unnamed.b.atv.R;
import com.unnamed.b.atv.holder.SimpleViewHolder;
import com.unnamed.b.atv.model.TreeNode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Created by Bogdan Melnychuk on 2/10/15.
*/
public class AndroidTreeView {
private static final String NODES_PATH_SEPARATOR = ";";
private TreeNode mRoot;
private Context mContext;
private boolean applyForRoot;
private int containerStyle = 0;
private Class<? extends TreeNode.BaseNodeViewHolder> defaultViewHolderClass = SimpleViewHolder.class;
private TreeNode.TreeNodeClickListener nodeClickListener;
private boolean mSelectionModeEnabled;
public AndroidTreeView(Context context, TreeNode root) {
mRoot = root;
mContext = context;
}
public void setDefaultContainerStyle(int style) {
setDefaultContainerStyle(style, false);
}
public void setDefaultContainerStyle(int style, boolean applyForRoot) {
containerStyle = style;
this.applyForRoot = applyForRoot;
}
public void setDefaultViewHolder(Class<? extends TreeNode.BaseNodeViewHolder> viewHolder) {
defaultViewHolderClass = viewHolder;
}
public void setDefaultNodeClickListener(TreeNode.TreeNodeClickListener listener) {
nodeClickListener = listener;
}
public void expandAll() {
expandNode(mRoot, true);
}
public void collapseAll() {
for (TreeNode n : mRoot.getChildren()) {
collapseNode(n, true);
}
}
public View getView(int style) {
final ScrollView view;
if (style > 0) {
ContextThemeWrapper newContext = new ContextThemeWrapper(mContext, style);
view = new ScrollView(newContext);
} else {
view = new ScrollView(mContext);
}
Context containerContext = mContext;
if (containerStyle != 0 && applyForRoot) {
containerContext = new ContextThemeWrapper(mContext, containerStyle);
}
final LinearLayout viewTreeItems = new LinearLayout(containerContext);
viewTreeItems.setId(R.id.tree_items);
viewTreeItems.setOrientation(LinearLayout.VERTICAL);
view.addView(viewTreeItems);
mRoot.setViewHolder(new TreeNode.BaseNodeViewHolder(mContext) {
@Override
public View createNodeView(TreeNode node, Object value) {
return null;
}
@Override
public ViewGroup getNodeItemsView() {
return viewTreeItems;
}
});
expandNode(mRoot, false);
return view;
}
public View getView() {
return getView(-1);
}
public void expandLevel(int level) {
for (TreeNode n : mRoot.getChildren()) {
expandLevel(n, level);
}
}
private void expandLevel(TreeNode node, int level) {
if (node.getLevel() <= level) {
expandNode(node, false);
}
for (TreeNode n : node.getChildren()) {
expandLevel(n, level);
}
}
public void expandNode(TreeNode node) {
expandNode(node, false);
}
public void collapseNode(TreeNode node) {
collapseNode(node, false);
}
public String getSaveState() {
final StringBuilder builder = new StringBuilder();
getSaveState(mRoot, builder);
if (builder.length() > 0) {
builder.setLength(builder.length() - 1);
}
return builder.toString();
}
public void restoreState(String saveState) {
if (!TextUtils.isEmpty(saveState)) {
collapseAll();
final String[] openNodesArray = saveState.split(NODES_PATH_SEPARATOR);
final Set<String> openNodes = new HashSet<>(Arrays.asList(openNodesArray));
restoreNodeState(mRoot, openNodes);
}
}
private void restoreNodeState(TreeNode node, Set<String> openNodes) {
for (TreeNode n : node.getChildren()) {
if (openNodes.contains(n.getPath())) {
expandNode(n);
restoreNodeState(n, openNodes);
}
}
}
private void getSaveState(TreeNode root, StringBuilder sBuilder) {
for (TreeNode node : root.getChildren()) {
if (node.isExpanded()) {
sBuilder.append(node.getPath());
sBuilder.append(NODES_PATH_SEPARATOR);
getSaveState(node, sBuilder);
}
}
}
private void toggleNode(TreeNode node) {
if (node.isExpanded()) {
collapseNode(node, false);
} else {
expandNode(node, false);
}
}
private void collapseNode(TreeNode node, final boolean includeSubnodes) {
node.setExpanded(false);
TreeNode.BaseNodeViewHolder nodeViewHolder = getViewHolderForNode(node);
nodeViewHolder.getNodeItemsView().setVisibility(View.GONE);
nodeViewHolder.toggle(false);
if (includeSubnodes) {
for (TreeNode n : node.getChildren()) {
collapseNode(n, includeSubnodes);
}
}
}
private void expandNode(final TreeNode node, boolean includeSubnodes) {
if (node.size() == 0) {
return;
}
node.setExpanded(true);
final TreeNode.BaseNodeViewHolder parentViewHolder = getViewHolderForNode(node);
parentViewHolder.getNodeItemsView().setVisibility(View.VISIBLE);
parentViewHolder.toggle(true);
for (final TreeNode n : node.getChildren()) {
if (!parentViewHolder.childrenInitialized()) {
final TreeNode.BaseNodeViewHolder viewHolder = getViewHolderForNode(n);
final View nodeView = viewHolder.getView();
parentViewHolder.getNodeItemsView().addView(nodeView);
if (mSelectionModeEnabled) {
viewHolder.toggleSelectionMode(mSelectionModeEnabled);
}
nodeView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggleNode(n);
if (n.getClickListener() != null) {
n.getClickListener().onClick(n, n.getValue());
} else if (nodeClickListener != null) {
nodeClickListener.onClick(n, n.getValue());
}
}
});
}
if (n.isExpanded() || includeSubnodes) {
expandNode(n, includeSubnodes);
}
}
}
//------------------------------------------------------------
// Selection methods
public void setSelectionModeEnabled(boolean selectionModeEnabled) {
if (!selectionModeEnabled) {
// TODO fix double iteration over tree
deselectAll();
}
mSelectionModeEnabled = selectionModeEnabled;
for (TreeNode node : mRoot.getChildren()) {
toggleSelectionMode(node, selectionModeEnabled);
}
}
private void toggleSelectionMode(TreeNode parent, boolean mSelectionModeEnabled) {
toogleSelectionForNode(parent, mSelectionModeEnabled);
for (TreeNode node : parent.getChildren()) {
if (getViewHolderForNode(node).childrenInitialized()) {
toggleSelectionMode(node, mSelectionModeEnabled);
}
}
}
public List<TreeNode> getSelected() {
if (mSelectionModeEnabled) {
return getSelected(mRoot);
} else {
return new ArrayList<>();
}
}
// TODO Do we need to go through whole tree? Save references or consider collapsed nodes as not selected
private List<TreeNode> getSelected(TreeNode parent) {
List<TreeNode> result = new ArrayList<>();
for (TreeNode n : parent.getChildren()) {
if (n.isSelected()) {
result.add(n);
}
result.addAll(getSelected(n));
}
return result;
}
public void selectAll(boolean skipCollapsed) {
makeAllSelection(true, skipCollapsed);
}
public void deselectAll() {
makeAllSelection(false, false);
}
private void makeAllSelection(boolean selected, boolean skipCollapsed) {
if (mSelectionModeEnabled) {
for (TreeNode node : mRoot.getChildren()) {
selectNode(node, selected, skipCollapsed);
}
}
}
private void selectNode(TreeNode parent, boolean selected, boolean skipCollapsed) {
parent.setSelected(selected);
toogleSelectionForNode(parent, true);
boolean toContinue = skipCollapsed ? parent.isExpanded() : true;
if (toContinue) {
for (TreeNode node : parent.getChildren()) {
if (getViewHolderForNode(node).childrenInitialized()) {
selectNode(node, selected, skipCollapsed);
}
}
}
}
private void toogleSelectionForNode(TreeNode node, boolean makeSelectable) {
getViewHolderForNode(node).toggleSelectionMode(makeSelectable);
}
private TreeNode.BaseNodeViewHolder getViewHolderForNode(TreeNode node) {
TreeNode.BaseNodeViewHolder viewHolder = node.getViewHolder();
if (viewHolder == null) {
try {
final Object object = defaultViewHolderClass.getConstructor(Context.class).newInstance(new Object[]{mContext});
viewHolder = (TreeNode.BaseNodeViewHolder) object;
node.setViewHolder(viewHolder);
} catch (Exception e) {
e.printStackTrace();
}
}
if (viewHolder.getContainerStyle() <= 0) {
viewHolder.setContainerStyle(containerStyle);
}
return viewHolder;
}
}