#!/usr/bin/python
# -*- coding: utf-8 -*-

## FROM:
# https://github.com/cloudformdesign/cloudtb/blob/master/extra/PyQt/treeview.py
# ****** The Cloud Toolbox v0.1.2******
# This is the cloud toolbox -- a single module used in several packages
# found at <https://github.com/cloudformdesign>
# For more information see <cloudformdesign.com>
#
# This module may be a part of a python package, and may be out of date.
# This behavior is intentional, do NOT update it.
#
# You are encouraged to use this pacakge, or any code snippets in it, in
# your own projects. Hopefully they will be helpful to you!
#
# This project is Licenced under The MIT License (MIT)
#
# Copyright (c) 2013 Garrett Berg cloudformdesign.com
# An updated version of this file can be found at:
# <https://github.com/cloudformdesign/cloudtb>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# http://opensource.org/licenses/MIT

# import pdb
import os

from PyQt4 import QtCore, QtGui
import sys
#import icons_rc


# from cloudtb import dbe

class Node(object):
    '''A general node stucture to be used in treeview
the attrib_dict can store any information your overall treeview
needs it to store.
'''
    def __init__(self, name, parent=None, icon = None,
                 attrib_dict = None):
        
        self._name = name
        self._attrib = attrib_dict
        self._children = []
        self._parent = parent
        self.icon = icon
        
        if parent is not None:
            parent.addChild(self)

    def addChild(self, child):
        self._children.append(child)

    def insertChild(self, position, child):
        
        if position < 0 or position > len(self._children):
            return False
        
        self._children.insert(position, child)
        child._parent = self
        return True

    def removeChild(self, position):
        
        if position < 0 or position > len(self._children):
            return False
        child = self._children.pop(position)
        child._parent = None

        return True

    def name(self):
        return self._name

    def setName(self, name):
        self._name = name

    def child(self, row):
        return self._children[row]
    
    def childCount(self):
        return len(self._children)

    def parent(self):
        return self._parent
    
    def row(self):
        if self._parent is not None:
            return self._parent._children.index(self)


    def log(self, tabLevel=-1):

        output = ""
        tabLevel += 1
        
        for i in range(tabLevel):
            output += " "
        
        output += "|-" + self._name + "\n"
        
        for child in self._children:
            output += child.log(tabLevel)
        
        tabLevel -= 1
# output += "\n"
        
        return output

    def __repr__(self):
        return self.log()

class TreeViewModel(QtCore.QAbstractItemModel):
    """INPUTS: Node, QObject"""
    def __init__(self, root, parent=None, header_title = None):
        super(TreeViewModel, self).__init__(parent)
        self.is_editable = False
        self.is_selectable = True
        self.is_enabled = True
        self.set_flags()
        self._rootNode = root
        self.header_title = header_title

    # The following are created functions called in "data" -- which is a
    # Qt defined funciton. This way of doing things is FAR more pythonic
    # and allows classes to inherit this one and not have to rewrite the
    # entire data method
    # all of them recieve an index and a node
    
    def role_display(self, index, node):
        if index.column() == 0:
                return node.name()
    
    def role_edit(self, index, node):
        return self.role_display(index, node)
    
    def role_tool_tip(self, index, node):
        return
        
    def role_check_state(self, index, node):
        return
    
    def role_decoration(self, index, node):
        if index.column() == 0:
            icon = node.icon
            if icon == None:
                return False
            else:
                return icon
    
    def role_flags(self, index, node):
        '''While not technically a "role" it behaves in much the same way.
This method is called by the "flags" method for all indexes with
a node'''
        return self.BASE_FLAGS
    
    def set_flags(self, is_editable = None, is_selectable = None,
                  is_enabled = None):
        ''' Sets new flags to the BASE_FLAGS variable'''
        if is_editable != None:
            self.is_editable = is_editable
        if is_selectable != None:
            self.is_selectable = is_selectable
        if is_enabled != None:
            self.is_enabled = is_enabled
        self.BASE_FLAGS = QtCore.Qt.ItemFlags(
              (QtCore.Qt.ItemIsEnabled * bool(self.is_enabled))
            | (QtCore.Qt.ItemIsSelectable * bool(self.is_selectable))
            | (QtCore.Qt.ItemIsEditable * bool(self.is_editable))
            )
    
    """INPUTS: QModelIndex"""
    """OUTPUT: int"""
    def rowCount(self, parent):
        if not parent.isValid():
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()

        return parentNode.childCount()

    """INPUTS: QModelIndex"""
    """OUTPUT: int"""
    def columnCount(self, parent):
        return 1
    
    """INPUTS: QModelIndex, int"""
    """OUTPUT: QVariant, strings are cast to QString which is a QVariant"""
    def data(self, index, role):
        '''index is an object that contains a pointer to the item inside
internPointer(). Note that this was set during the insertRows
method call, so you don't need to track them!
'''
        if not index.isValid():
            return None
        
        node = index.internalPointer()
        
        if role == QtCore.Qt.EditRole:
            return self.role_edit(index, node)
        
        if role == QtCore.Qt.ToolTipRole:
            return self.role_tool_tip(index, node)
        
        if role == QtCore.Qt.CheckStateRole:
            return self.role_check_state(index, node)
        
        if role == QtCore.Qt.DisplayRole:
            return self.role_display(index, node)
            
        if role == QtCore.Qt.DecorationRole:
            return self.role_decoration(index, node)

    """INPUTS: QModelIndex, QVariant, int (flag)"""
    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if index.isValid():
            if role == QtCore.Qt.EditRole:
                node = index.internalPointer()
                node.setName(value)
                return True
        return False
    
    """INPUTS: int, Qt::Orientation, int"""
    """OUTPUT: QVariant, strings are cast to QString which is a QVariant"""
    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            if self.header_title != None:
                return self.header_title
            if section == 0:
                return "Scenegraph"
            else:
                return "Typeinfo"
    
    """INPUTS: QModelIndex"""
    """OUTPUT: int (flag)"""
#    def flags(self, index):
#        return (QtCore.Qt.ItemIsEnabled |
#            QtCore.Qt.ItemIsSelectable #|
## QtCore.Qt.ItemIsEditable
#            )
    def flags(self, index):
        if not index.isValid():
            return self.BASE_FLAGS
        
        node = index.internalPointer()
        return self.role_flags(index, node)

    """INPUTS: QModelIndex"""
    """OUTPUT: QModelIndex"""
    """Should return the parent of the node with the given QModelIndex"""
    def parent(self, index):
        node = self.getNode(index)
        parentNode = node.parent()
        
        if parentNode == self._rootNode:
            return QtCore.QModelIndex()
        return self.createIndex(parentNode.row(), 0, parentNode)
        
    """INPUTS: int, int, QModelIndex"""
    """OUTPUT: QModelIndex"""
    """Should return a QModelIndex that corresponds to the given row,
column and parent node"""
    def index(self, row, column, parent):
        # This is how Qt creates the nested (tree) list. It knows how many
        # rows it has because of insertRows, and it uses index and
        # createIndex to build the tree.
# print 'Index called', row, column
        parentNode = self.getNode(parent)
        childItem = parentNode.child(row)
        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QtCore.QModelIndex()

    """CUSTOM"""
    """INPUTS: QModelIndex"""
    def getNode(self, index):
        if index.isValid():
            node = index.internalPointer()
            if node:
                return node
            
        return self._rootNode

    
    """INPUTS: int, List of Nodes, QModelIndex"""
    def insertRows(self, position, rows, parent=QtCore.QModelIndex()):
        parentNode = self.getNode(parent)
        
        self.beginInsertRows(parent, position, position + len(rows) - 1)
        
        for i, row in enumerate(rows):
# childCount = parentNode.childCount()
            childNode = row
            success = parentNode.insertChild(position + i, childNode)
        
        self.endInsertRows()

        return success
    
    """INPUTS: int, int, QModelIndex"""
    def removeRows(self, position, rows, parent=QtCore.QModelIndex()):
        if rows == 0:
            return
        
        parentNode = self.getNode(parent)
        self.beginRemoveRows(parent, position, position + rows - 1)
        
        for row in range(rows):
            success = parentNode.removeChild(position)
            # TODO: break if not success?
            
        self.endRemoveRows()
        
        return success
    
    def clear_rows(self):
        return self.removeRows(0, self._rootNode.childCount())

# TODO: doesn't work. Not sure how to get icons
ICON_FOLDER = QtGui.QIcon.fromTheme('folder')

def _node_compare(a, b):
    return b.isdir - a.isdir
    
def get_file_folder_node(fdata, parent):
    '''return the node structure of the data.
[[(dir_name, path),
[dir_name, path),
[(file, path),
(file, path)]]
]
]
'''
    # TODO: set icons correctly
    
    nodes = []
    for fobj in fdata:
        path = fobj[0]
        name = os.path.split(path)[1]
        
        if len(fobj) == 1:
            fileobj = Node(name, parent = parent, icon = None)
            fileobj.full_path = path
            fileobj.isdir = False
            nodes.append(fileobj)
            continue
        folderobj = Node(name, parent = parent, icon = ICON_FOLDER,
                         )
        folderobj.full_path = path
        folderobj.isdir = True
        
        get_file_folder_node(fobj[1], parent = folderobj)
        nodes.append(folderobj)
    nodes.sort(cmp = _node_compare)
    return nodes
import itertools

def _get_filelist_nodes(iter_file_list, dir_path = ''):
    '''Takes a sorted file list iterator and returns the files in a
format that can be converted'''
    files = []
    dir_path = os.path.join(dir_path, '') # Put into directory syntax
    len_dp = len(dir_path)
    while True:
        try:
            fpath = next(iter_file_list)
        except StopIteration:
            break
        if dir_path != fpath[:len_dp]:
            iter_file_list = itertools.chain((fpath,), iter_file_list)
            break
        
        if os.path.isdir(fpath):
            iter_file_list, new_files = _get_filelist_nodes(iter_file_list,
                    dir_path = fpath)
            files.append((fpath, new_files))
        else:
            files.append((fpath,))
    return iter_file_list, files

def get_filelist_nodes(file_list, parent = None):
    file_list = sorted(file_list)
    file_tuples = _get_filelist_nodes(iter(file_list))[1]
    return get_file_folder_node(file_tuples, parent)

def dev_show_file_list(file_objects):
    '''For developemnet'''
    
    app = QtGui.QApplication(sys.argv)
    
    rootNode = Node("Rootdir")
    model = TreeViewModel(rootNode)
    
    treeView = QtGui.QTreeView()
    treeView.show()
    
    treeView.setModel(model)
    model.insertRows(0, file_objects, QtCore.QModelIndex())
    sys.exit(app.exec_())

    
if __name__ == '__main__':
    from pprint import pprint

    files = '''/home/user/Projects/Learning/LearningQt/LearningQt.pro.user
/home/user/Projects/Learning/LearningQt/LearningQt.pro
/home/user/Projects/Learning/LearningQt/qmlapplicationviewer/qmlapplicationviewer.h
/home/user/Projects/Learning/LearningQt/qmlapplicationviewer/qmlapplicationviewer.cpp
/home/user/Projects/Learning/LearningQt/qmlapplicationviewer/qmlapplicationviewer.pri
/home/user/Projects/Learning/LearningQt/qmlapplicationviewer
/home/user/Projects/Learning/LearningQt/LearningQt64.png
/home/user/Projects/Learning/LearningQt/LearningQt_harmattan.desktop
/home/user/Projects/Learning/LearningQt/LearningQt.svg
/home/user/Projects/Learning/LearningQt/main.cpp
/home/user/Projects/Learning/LearningQt/LearningQt.desktop
/home/user/Projects/Learning/LearningQt/qml/LearningQt/main.qml
/home/user/Projects/Learning/LearningQt/qml/LearningQt
/home/user/Projects/Learning/LearningQt/qml
/home/user/Projects/Learning/LearningQt/LearningQt80.png'''
    nodes = get_filelist_nodes(files.split('\n'))
    for n in nodes:
        print n
    dev_show_file_list(nodes)
    