"""
   Copyright 2016 University of Auckland

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
"""
import json

from opencmiss.zinc.status import OK as ZINC_OK
from opencmiss.argon.argonerror import ArgonError

SPECTRUM_GLYPH_NAME_FORMAT = 'colour_bar_{0}'


class ArgonSpectrums(object):
    """
    Manages and serializes Zinc Spectrums within Neon.
    Generates colour bar glyphs for spectrums, which is automatically done if if not found on loading.
    """

    def __init__(self, zincContext):
        self._zincContext = zincContext
        self._spectrummodule = zincContext.getSpectrummodule()
        self._findOrCreateAllSpectrumGlyphColourBars()

    def getZincContext(self):
        return self._zincContext

    def deserialize(self, dictInput):
        spectrumsDescription = json.dumps(dictInput)
        result = self._spectrummodule.readDescription(spectrumsDescription)
        if result != ZINC_OK:
            raise ArgonError("Failed to read spectrums")
        self._findOrCreateAllSpectrumGlyphColourBars()

    def serialize(self):
        spectrumsDescription = self._spectrummodule.writeDescription()
        dictOutput = json.loads(spectrumsDescription)
        return dictOutput

    def _findOrCreateAllSpectrumGlyphColourBars(self):
        """
        Ensures there exists a colour bar for each spectrum.
        """
        iterator = self._spectrummodule.createSpectrumiterator()
        spectrum = iterator.next()
        while spectrum.isValid():
            self.findOrCreateSpectrumGlyphColourBar(spectrum)
            spectrum = iterator.next()

    def _findSpectrumGlyphColourBar(self, spectrum):
        """
        Find or create a GlyphColourBar for spectrum in the glyph module.
        Newly created colour bar is set up for display in the normalised window coordinates at left.
        """
        glyphmodule = self._zincContext.getGlyphmodule()
        glyphName = SPECTRUM_GLYPH_NAME_FORMAT.format(spectrum.getName())

        # try to find by name first
        glyph = glyphmodule.findGlyphByName(glyphName)
        colourBar = glyph.castColourBar()
        if colourBar.isValid() and (colourBar.getSpectrum() == spectrum):
            return colourBar

        # attempt to find by matching spectrum but do not rename it
        glyphiterator = glyphmodule.createGlyphiterator()
        glyph = glyphiterator.next()
        while glyph.isValid():
            colourBar = glyph.castColourBar()
            if colourBar.isValid() and (colourBar.getSpectrum() == spectrum):
                return colourBar
            glyph = glyphiterator.next()

        return None

    def findOrCreateSpectrumGlyphColourBar(self, spectrum):
        """
        Find or create a GlyphColourBar for spectrum in the glyph module.
        Newly created colour bar is set up for display in the normalised window coordinates at left.
        """
        colourBar = self._findSpectrumGlyphColourBar(spectrum)
        if colourBar:
            return colourBar

        glyphmodule = self._zincContext.getGlyphmodule()
        glyphName = SPECTRUM_GLYPH_NAME_FORMAT.format(spectrum.getName())

        # create a new colour bar, matching Cmgui's defaults:
        glyphmodule.beginChange()
        colourBar = glyphmodule.createGlyphColourBar(spectrum)
        tmpName = glyphName
        i = 1
        while (colourBar.setName(tmpName) != ZINC_OK):
            tmpName = glyphName + str(i)
            i += 1
        colourBar.setManaged(True)
        colourBar.setCentre([-0.9, 0.0, 0.5])
        colourBar.setAxis([0.0, 1.6, 0.0])  # includes length
        colourBar.setSideAxis([0.06, 0.0, 0.0])  # includes radius
        colourBar.setExtendLength(0.06)
        colourBar.setTickLength(0.04)
        colourBar.setLabelDivisions(10)
        colourBar.setNumberFormat('%+.4e')
        glyphmodule.endChange()
        return colourBar

    def renameSpectrum(self, spectrum, name):
        """
        Renames spectrum and its glyph
        :return True on success, otherwise False (means name not set)
        """
        colourBar = self.findOrCreateSpectrumGlyphColourBar(spectrum)
        result = spectrum.setName(name)
        if result == ZINC_OK:
            glyphName = SPECTRUM_GLYPH_NAME_FORMAT.format(name)
            tmpName = glyphName
            i = 1
            while (colourBar.setName(tmpName) != ZINC_OK):
                tmpName = glyphName + str(i)
                i += 1
            return True
        return False

    def removeSpectrumByName(self, name):
        """
        Unmanages spectrum and its colour bar. Note spectrum is only removed if neither are in use.
        :return True if spectrum and colour bar removed, false if failed i.e. either are in use.
        """
        spectrum = self._spectrummodule.findSpectrumByName(name)
        colourBar = self._findSpectrumGlyphColourBar(spectrum)
        if colourBar:
            colourBarName = colourBar.getName()
            colourBar.setManaged(False)
            del colourBar
            colourBar = self._findSpectrumGlyphColourBar(spectrum)
            if colourBar and (colourBar.getName() == colourBarName):
                # colour bar is in use; do not remove spectrum
                colourBar.setManaged(True)
                return False
        spectrum.setManaged(False)
        del spectrum
        spectrum = self._spectrummodule.findSpectrumByName(name)
        if spectrum.isValid():
            # spectrum is in use so can't remove
            spectrum.setManaged(True)
            self.findOrCreateSpectrumGlyphColourBar(spectrum)
            return False
        return True
