# -*- coding: utf-8 -*-

"""model factory controler"""


from PyQt4 import QtCore, QtGui
import cPickle

from chartables_model_options import OptionsModel
from chartables_model_factory import ModelFactoryContainer

# NOTE:
# - currently state information is stored as a nested list
#[index-factory,
#    [index-Type(0),
#        [index-model(0),
#            [model(0), model(N)]
#        ]
#    ]
#]
#
#
#***********************************************************************
# empty model
#***********************************************************************
class EmptyModel(QtCore.QAbstractTableModel):

    def rowCount(self, parent, *args, **kwargs): return 0
    def columnCount(self, parent): return 0
    def getDelegate(self): return None


#***********************************************************************
# controls a model and associated comboboxes
#***********************************************************************
class ModelFactoryControler(QtCore.QObject):
    """model controler"""

        
    
    def __init__(
                    self,
                    table,
                    modelFactories,
                    cmbType,
                    cmbModel,
                    oldItemDelegate,
                    state,
                    options
                    ):
        """
        constructor

        @param table main table of the dialog
        @param factories factories to handle
        @param cmbType combobox to select type of model
        @param cmbModel combobox to select a model
        @param oldItemDelegate original QItemDelegate of the main table
        @param state list[index-type, [indexes-models]] for state storage
        @param options

        @signal: modelInserted(const QAbstractTableModel&) emited when a new model is inserted
        into the tree

        """


        QtCore.QObject.__init__(self, table)

        self.cmbType = cmbType
        self.cmbModel =cmbModel

        self.table = table
        self.oldItemDelegate = oldItemDelegate

        self.state = state
        self.options = options

        # init container
        self.factoryContainer = ModelFactoryContainer(
                                                self.cmbType.view(), 
                                                self.cmbModel.view(), 
                                                modelFactories,
                                                options=self.options
                                                )
        self.cmbType.setModel(self.factoryContainer)
        if self.factoryContainer:
            for factory in self.factoryContainer:
                #self.factories.append(factory(self.cmbModel, options=options))
                self.state[1].append(0)
        else:
            self.state[1].append(0)
            self.state[1].append(0)
            self.cmbType.setEnabled(False)
            self.cmbModel.setEnabled(False)
        
        self.connect(
                self.cmbType, 
                QtCore.SIGNAL("activated(int)"), 
                self._onTypeSelected
                )
        self.connect(
                self.cmbModel, 
                QtCore.SIGNAL("activated(int)"),
                self._onModelSelected
                )
    
    
    def retranslate(self):
        """retranslates the controler"""
        self.factoryContainer = ModelFactoryContainer(
                                                self.cmbType.view(), 
                                                self.cmbModel.view(), 
                                                self.factoryContainer.modelFactoryClasses(),
                                                options=self.options
                                                )
        self.cmbType.setModel(self.factoryContainer)

            
    
    def selectType(self, index):
        """
        public method to select a type of model

        @param index index of the type to select
        """
        self.cmbType.setCurrentIndex(index)
        self._selectType(index)


    def _onTypeSelected(self, index):
        """
        protected slot to select a type of model

        @param index index of the type to select
        @param indexModel index of the type to select
        """
        self._selectType(index)
        self.selectModel(self.state[1][index])


    def _selectType(self, index):
        """
        protected method to select a type of model

        @param index index of the type to select
        """
        if not self.cmbType.isEnabled():
            return
        self.cmbModel.clear()
        self.state[0] = index
        n = None
                
        factory = self.factoryContainer[index]
        self.cmbModel.setModel(factory)



        #for n in range(len(factory)):
        #    self.cmbModel.insertItem(n, factory.modelDisplayName(n))
        if not factory:
            self.cmbModel.setEnabled(False)
        
        
    def selectModel(self, index):
        """
        public method to select a model

        @param index index of the model to select
        """
        self.cmbModel.setCurrentIndex(index)
        self._selectModel(index)


    def _onModelSelected(self, index):
        """
        protected slot to select a model

        @param index index of the model to select
        """
        self._selectModel(index)


    def _selectModel(self, index):
        """
        protected method to select a model

        @param index index of the model to select
        """

        if not self.options.flags & self.options.CTF_MODELS:
            self.state[1] = [0 for i in self.state[1]]
        if self.options.flags & self.options.CTF_MODEL:
            self.state[1][self.state[0]] = index

        if self.cmbModel.isEnabled():
            modelClass = self.factoryContainer[self.cmbType.currentIndex()][index]
        else:
            modelClass = EmptyModel

        self.table.setUpdatesEnabled(False)
        
        # inform the delegate that it is about to be removed
        # NOTE: the correct way to process would be via the commented out 
        # statements. But they pretty much slow down model changes. 
        # We cheat here a bit to keep model changes fast. There is still a little
        # glitch when switching away from the "Options" tab. Mostly caused
        # by the OptionsWidget disapearing _very_ slowly while the next model
        # already has drawn its header.
        #
        ##self.table.setItemDelegate(None)
        ##self.table.setModel(None)
        oldDelegate = self.table.itemDelegate()
        if hasattr(oldDelegate, "aboutToBeRemoved"):
            oldDelegate.aboutToBeRemoved(self.table)
                
        model = modelClass(self.table, options=self.options)
        delegate = model.getDelegate()
        self.table.setModel(model)
        if delegate:
            self.table.setItemDelegate(delegate)
        else:
            self.table.setItemDelegate(self.oldItemDelegate)
                
        self.table.resizeColumnsToContents()
        self.table.setUpdatesEnabled(True)
        self.emit(
                QtCore.SIGNAL("modelInserted(const QAbstractTableModel&)"),
                model
                )


#***********************************************************************
# controls the tab widget
#***********************************************************************
# NOTE: "Options" tab has to be the last tab in the dialog

class Controler(QtCore.QObject):
    """controler for the model factories"""

        
    def __init__(self, table, tab, options=None):
        """
        constructor

        @param table main table of the dialog
        @param tab main tab control
        @param options options to customize the controler
        @signal optionsDisplayed(const QWidget&, bool) emited when the options tab is
            displayed.
                Parameter is True if the tab is about to be displayed, False if it is about
                ti to be hinnden.
        """

        QtCore.QObject.__init__(self, table)

        self.table = table
        self.tab = tab
        self.modelFactoryControlers = []
        self.oldItemDelegate = table.itemDelegate()       # for later use
        self.flag = True
        self.state = [0, ]
        
        self.options = options and options or Options()
        
        self.connect(
            self.tab, 
            QtCore.SIGNAL("currentChanged(int)"), 
            self._onSelectFactory
            )
        

        

    def newFactory(self, factories, cmbType, cmbModel):
        """
        registers a new ModelFactory

        @param factories factories to handle
        @param cmbType combobox to select type of models
        @param cmbModel combobox to select models
        @return ModelControler instance wrapped around the factory
        """

        state = [0, []]
        self.state.append(state)
        m = ModelFactoryControler(
                    self.table,
                    factories,
                    cmbType,
                    cmbModel,
                    self.oldItemDelegate,
                    state,
                    self.options
                    )
        self.modelFactoryControlers.append(m)
        self.connect(
                    m,
                    QtCore.SIGNAL("modelInserted(const QAbstractTableModel&)"),
                    self._onModelInserted
                    )
        return m

    
    def _onModelInserted(self, model):
        self.emit(
            QtCore.SIGNAL("modelInserted(const QAbstractTableModel&)"),
            model
            )
            
    
    def selectFactory(self, index):
        """
        public method to select a factory

        @param index index of the factory to select
        """

        # lock here
        # QTabWidgets emit currentChanged() on setCurrentIndex() ?
        # combos do not. No idea...
        self.flag = True
        try:
            self.tab.setCurrentIndex(index)
        finally:
            self.flag = False
        self._selectFactory(index)

    
    def _onSelectFactory(self, index):
        """
        protected slot to select a factory

        @param index index of the factory to select
        """
        # display options tab
        if self.tab.count() -1 == index:
            self.table.setItemDelegate(self.oldItemDelegate)
            self._displayOptionsWidget()            
        else:
            if self.options.flags & self.options.CTF_FACTORY:
                self.state[0] = index
            if not self.flag:
                self._selectFactory(index)


    def _displayOptionsWidget(self):
        """sets up or tears down OptionsWidget"""
        optionsModel = OptionsModel(self.table, self.options)
        widget = optionsModel.getOptionsWidget()
        self.emit(
                QtCore.SIGNAL("optionsDisplayed(const QWidget&, bool)"),
                widget,
                True
                )
        
        
    def _selectFactory(self, index):
        """
        protected method to select a factory

        @param index index of the factory to select
        """
        model = self.modelFactoryControlers[index]

        indexType = self.state[index +1][0]
        indexModel = self.state[index +1][1][indexType]
        model.selectType(indexType)
        model.selectModel(indexModel)



    def saveState(self):
        """
        saves the current state of the controler

        @return state (string)
        """
        return cPickle.dumps(self.state, 0)


    # TODO: errorcheck and failsave
    def restoreState(self, state):
        """
        public method to restore a formerly saved state

        @param state (string) state to restore
        @return True if the state could be restored successfuly, False otherwise
        """

        success = False
        try:

            restoredState = cPickle.loads(state)

            def validate(i, maxIndex):
                assert isinstance(i, (int, long)) and i > -1 and i < maxIndex
                return i

            if self.options.flags & self.options.CTF_FACTORY:
                # restore factory selection
                self.state[0] = validate(restoredState[0], len(self.modelFactoryControlers))

            x = 1
            for model in self.modelFactoryControlers:

                modelFactory = model.factoryContainer
                if self.options.flags & self.options.CTF_TYPE:
                    # restore type selections
                    self.state[x][0] = validate(restoredState[x][0], len(modelFactory))

                if self.options.flags & self.options.CTF_MODEL:
                    # restore model selections
                    typeStates = restoredState[x][1]
                    y = 0
                    for n in range(len(modelFactory)):
                        maxItems = len(modelFactory[n])
                        self.state[x][1][y] = validate(typeStates[y], maxItems)
                        y += 1
                x += 1

            success = True

        except:
            pass

        self.selectFactory(self.state[0])
        return success


    def displaysOptions(self):
        """checks if the options tab is currently displayed"""
        return isinstance(self.table.model(), OptionsModel)
        #== self.optionsModel
    
    
    def retranslate(self):
        """refreses the currently selected factory"""
        for model in self.modelFactoryControlers:
            model.retranslate()
        if self.displaysOptions():
            self.table.setUpdatesEnabled(False)
            self._displayOptionsWidget()
            self.table.setUpdatesEnabled(True)
        else:
            self.selectFactory(self.tab.currentIndex())
