# vPresent is an immersive presentation creation and display application.
# Copyright (C) 2007-2008 by Infiscape Corporation
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

import types

from PyQt4 import QtCore, QtGui
import opendocument
import gui.util.environment as environ

class BackgroundModel(QtCore.QAbstractListModel):
   def __init__(self, document, parent=None):
      QtCore.QAbstractListModel.__init__(self, parent)

      self.mDocument = document
      self.mAutoStyles = self.mDocument.content.find('./office:automatic-styles')

      self.mErrorThumbnail = None

   def data(self, index, role=QtCore.Qt.DisplayRole):
      """ Returns the data representation of each page style in the presentation.
      """
      if not index.isValid():
         return QtCore.QVariant()

      # Handle the special 'None' page style case first.
      if 0 == index.row():
         if role == QtCore.Qt.UserRole or role == QtCore.Qt.UserRole+1:
            return None
         elif role == QtCore.Qt.DisplayRole:
            return QtCore.QVariant("None")
         return QtCore.QVariant()

      # NOTE: We need to subtract 1 from the row index to account for
      #       the special 'None' page style case.
      row = index.row() - 1
      # Get the page style that we want data for.
      page_style = self.mDocument.pageStyles[row]
      sphere_background = page_style.find('./vpresent:page3d-properties/vpresent:sphere-background')

      if role == QtCore.Qt.UserRole:
         return page_style
      if role == QtCore.Qt.UserRole+1:
         return sphere_background

      if sphere_background is not None:
         # Return an icon representing the page style.
         if role == QtCore.Qt.DecorationRole:
            thumbnail = None

            img_refs = opendocument.NodeFilter(sphere_background, 'vpresent:fill-image-ref')
            if len(img_refs) > 0:
               img_ref = img_refs[0]

               # Order from opendocument.vpresent: negx, posx, negy, posy, negz, posz.
               if 'cubic' == sphere_background.vpresent_type and len(img_refs) >= 5:
                  img_ref = img_refs = img_refs[4]

               tex = self.mDocument.getFillImage(img_ref.draw_fill_image_name)
               if tex is not None:
                  # XXX: Should this return a QPixmap or a QIcon?
                  thumbnail = tex._getThumbnail()

            # If we could not get a valid thumbnail.
            if thumbnail is None:
               # Lazely create an error thumbnail.
               if self.mErrorThumbnail is None:
                  pixmap = QtGui.QPixmap(":/vpresent/gui/images/help32.png")
                  self.mErrorThumbnail = pixmap.scaled(64, 64)
               thumbnail = self.mErrorThumbnail
            # Return the correct thumbnail.
            return QtCore.QVariant(thumbnail)
         # Only return text when debugging
         #if role == QtCore.Qt.DisplayRole:
         #   return QtCore.QVariant(page_style.style_name)
      return QtCore.QVariant()

   def addPageStyle(self, newPageStyle):
      """ Add a new page style to the end of the model. """
      self.insertPageStyle(self.rowCount(), newPageStyle)

   def insertPageStyle(self, row, newPageStyle):
      """
      Insert a page style into the presentation.
      @param row Index into the list of page styles, not office:automatic-styles children.
      """
      assert row >= 0

      # NOTE: We can not let the user add a page style before the special None style.
      if 0 == row:
         print "Can't insert a page style before 'None' page style."
         row = 1

      page_styles = self.mDocument.pageStyles[:]
      # If there are no page styles, insert at the beginning.
      if len(page_styles) == 0:
         idx = 0
      else:
         # If inserting in middle, insert at the position of the previous page style.
         if row < len(page_styles):
            prev = page_styles[row]
            idx = self.mAutoStyles._children.index(prev)
         # Append to the position of the last page style.
         else:
            prev = page_styles[-1]
            idx = self.mAutoStyles._children.index(prev) + 1

      # Update the model with our row, but the presentation with
      # the child index.
      self.beginInsertRows(QtCore.QModelIndex(), row, row)
      self.mDocument.addStyle(newPageStyle)
      self.mAutoStyles.insertChild(idx, newPageStyle)
      self.endInsertRows()

   def removePageStyle(self, rowOrPageStyle):
      """
      Remove a page style from the presentation.
      @param rowOrPageStyle Row index or a style:style object. If it is a row
                            index then it is an index into the list of page
                            styles, not office:automatic-styles children.
      """
      if rowOrPageStyle is None:
         return

      # Handle both cases of index and page style object.
      if type(rowOrPageStyle) == types.IntType:
         row = rowOrPageStyle
         # NOTE: We can not remove the special value of 'None' at row 0.
         if 0 == row:
            print "Can't remove the 'None' page style."
            return
         page_style = self.mDocument.pageStyles[row]
      else:
         page_style = rowOrPageStyle
         row = self.mDocument.pageStyles.index(page_style)

      # NOTE: We need to account for the special 'None' page style
      #       by adding one to the row.
      row += 1

      # Get the child index for the page style object.
      idx = self.mAutoStyles._children.index(page_style)

      # Update the model with our row, but the presentation with
      # the child index.
      # NOTE: We need to account for the special 'None' page style
      #       by adding one to the row.
      self.beginRemoveRows(QtCore.QModelIndex(), row, row)
      self.mDocument.removeStyle(page_style)
      self.mAutoStyles.subChild(idx)
      self.endRemoveRows()

   def rowCount(self, parent=QtCore.QModelIndex()):
      """ Returns the number of page styles in the presentation.
      """
      # If the parent is not valid, then we have no children.
      if parent.isValid():
         return 0
      else:
         # NOTE: Add one for the None background at index 0.
         return len(self.mDocument.pageStyles) + 1

   def indexByName(self, styleName):
      for (idx, ps) in enumerate(self.mDocument.pageStyles):
         if ps.style_name == styleName:
            # NOTE: Add one for the 'None' background at index 0.
            return self.index(idx+1)
      # Return the index for the special 'None' background.
      return self.index(0)


class AddPageStyle(QtGui.QUndoCommand):
   """ Adds a page style into the given presentation. """
   CUBIC = 0
   MERCATOR = 1

   def __init__(self, document, backgroundModel, pageStyle, parent=None):
      QtGui.QUndoCommand.__init__(self, "Add Page Style", parent)
      self.mDocument = document
      self.mBackgroundModel = backgroundModel
      self.mPageStyle = pageStyle

   def redo(self):
      # Add the page style to the presentation.
      self.mBackgroundModel.addPageStyle(self.mPageStyle)

   def undo(self):
      # Remove the page style from the presentation.
      self.mBackgroundModel.removePageStyle(self.mPageStyle)

class RemovePageStyle(QtGui.QUndoCommand):
   """ Adds a page style into the given presentation. """
   def __init__(self, backgroundModel, pageStyle, parent=None):
      QtGui.QUndoCommand.__init__(self, "Remove Page Style", parent)
      self.mBackgroundModel = backgroundModel
      self.mPageStyle = pageStyle

   def redo(self):
      # Remove the page style from the presentation.
      self.mBackgroundModel.removePageStyle(self.mPageStyle)

   def undo(self):
      # Add the page style to the presentation.
      self.mBackgroundModel.addPageStyle(self.mPageStyle)
