
import os
import twc
import twc.products
import twccommon
import wxscan.dataUtil as dataUtil
import twcWx.dataUtil as wxDataUtil
import twc.dsmarshal as dsm
import twccommon.Log


class AnimatedMap(twc.products.Product):
    """This class supports maps that "animate" (i.e. - support multiple data
       layer images that 'move' over the map. If we ever need a map that
       supports static images over a map (like in WxScan), we'll need a
       different class."""

    def __init__(self, params):
        twc.products.Product.__init__(self, params)

        # set the max number of images that can be missing before the
        # animation loop is cancelled (-1 = unlimited)
        self._maxAllowedImageGap = -1  # image file count

        # for debugging, show any image files present regardless of
        # expiration time
        self._ignoreImageExpiration = 0

        # for debugging, show any image files present regardless of
        # any gaps in the image animation
        self._ignoreTimeGaps        = 0

        # set image root
        TWCPERSDIR = os.environ['TWCPERSDIR']
        iRoot = TWCPERSDIR + '/data/volatile/images'

        data = self.getData()

        # setup the product config string name, we'll use this a lot
        prodString = 'Config.%s.%s.%s.%s.%s' % (dsm.getConfigVersion(),
                params.package, params.packageInst, params.product,
                params.productInst)

        # get all additional info about the map cut (especially the datacut type
        # and geographic location)
        mapDataString = prodString + '.MapData'
        mapCutData    = dsm.defaultedGet(mapDataString)

        dataCutType = mapCutData.datacutType
        dataCutInfo = dataCutType.split('.')

        self.updateData(
            productString = prodString,

            # figure out the data type so we know what directory to look in
            imageRoot = iRoot + '/%s/%s.cuts/' % (dataCutInfo[0], dataCutInfo[1]),

            mapLocation = dataCutInfo[1], # us, pr, ak, hi, etc

            # list of data images to display
            imageList = [],

            # image parameters (-1 = undefined)
            imageFrequency = -1, # in seconds
            maxImages      = -1, # image file count

            # display "timing" for each image: show image for 'x' frames
            # (-1 = undefined)
            imageDuration      = -1,  # in frames
            lastImageDuration  = -1)  # in frames (frame count)


    def _loadData(self): 
        params = twccommon.DefaultedData(self.getParams())
        data   = self.getData()

        # Assume there is no data
        data.noDataAvailable = 1

        # check for a map cut
        mapCut = '/twc/data/map.cuts/%s.map.tif' % (data.productString,)

        # if there's no map cut, we're not valid (regardless if there's data)
        if os.path.exists(mapCut) == 0:
            twccommon.Log.warning("no map cut found for %s. Can't display product." % (data.productString,))
            self._isValid = 0
            return

        # see if we have any data:

        data.imageList = dataUtil.getValidFileList(dataPath=data.imageRoot,
                                                   prefix=data.productString,
                                                   suffix='*[0-9].tif',
                                                   startTimeNdx=6, endTimeNdx=7, sortIndex=6)

        # IF we need to check for gaps AND we found valid images
        if (self._maxAllowedImageGap >= 0) and (len(data.imageList) > 0):
            # make sure there aren't any time gaps!
            # this needs to be the list of ALL valid images because this
            # function will clean up this product's valid images if there
            # is a time gap in the loop (per the reqs).
            data.imageList = dataUtil.checkImageListForGaps(
                params.product, data.imageList, data.imageFrequency,
                self._maxAllowedImageGap, self._ignoreTimeGaps)

        # per the requirements, restrict the time period to MAX_IMAGES
        data.imageList = data.imageList[0:data.maxImages]

        # If we still have images after checking for gaps
        if len(data.imageList) > 0:
            # Clear the noDataAvailable flag
            data.noDataAvailable = 0

        # SORT oldest to newest (IMPORTANT: we MUST slice THEN reverse! Not the
        # other way around!)
        data.imageList.reverse()

        # Update data with params map data which does not need resolving
        data.vector = params.vector
        data.textString = params.textString
        data.tiffImage = params.tiffImage
        data.labeledTiffImage = params.labeledTiffImage


class ObservationMap(twc.products.Product):
    # Just inherit the base class __init__
    #def __init__(self, params):
    #    Product.__init__(self, params)

    def _getObservationData(self, obsStation, dataType):
        # if we haven't heard of it or there's a datastore problem,
        # return None
        value = None

        if dataType == 'waterTemp':
            # key = waterBuoy.{obsStation} i.e. - waterBuoy.B41008
            obsKey = 'waterBuoy.%s' % (obsStation,)
            observation = dsm.defaultedGet(obsKey, None)

            if observation is not None:
                observation = twccommon.DefaultedData(observation)
                value = observation.waterTemp
        else:
            # key = obs.{obsStation} i.e. - obs.KATL
            obsKey = 'obs.%s' % (obsStation,)
            observation = dsm.defaultedGet(obsKey, None)

            if observation is not None:
                observation = twccommon.DefaultedData(observation)

                # TODO: add ALL other types to be supported
                if dataType == 'temp':
                    value = observation.temp
                elif dataType == 'skyCondition':
                    value = wxDataUtil.formatSkyCondition(observation.skyCondition,
                                   'Observation').iconFile
        return value

    def _loadData(self): 
        params = twccommon.DefaultedData(self.getParams())
        data = self.getData()

        mapCut = '/twc/data/map.cuts/%s.map.tif' % (data.productString,)

        # if there's no map cut, we're not valid (regardless if there's data)
        if os.path.exists(mapCut) == 0:
            twccommon.Log.warning("no map cut found for %s. Can't display product." % (data.productString,))
            self._isValid = 0
            data.noDataAvailable = 1
            return

        # obsValue
        obsData = []
        validTempList = []
        # If params has a obsValue field (obsValue != None)
        # and it is not empty (obsValue != [])
        if (params.obsValue):
            for properties,elements in params.obsValue:
                # what data value are we looking for in the Ob?
                dataType = properties[5]

                updatedElements = []
                for item in elements:
                    station = item[0]
                    position   = item[1]

                    # getObsData
                    value = self._getObservationData(station, dataType)
                    # Note: zero is a valid temp, so check for None
                    validTempList.append(value != None)
                    updatedElements.append((value,position))
     
                obsData.append((properties, tuple(updatedElements)))

        data.obsValue = obsData

        # obsIcon
        iconData = []
        validIconList = []
        # If params has a obsIcon field (obsIcon != None)
        # and it is not empty (obsIcon != [])
        if (params.obsIcon):
            for properties,elements in params.obsIcon:
                # icons don't have properties, skip to elements
                updatedElements = []
                for item in elements:
                    station = item[0]
                    position   = item[1]

                    # getObsData
                    iconFile = self._getObservationData(station, 'skyCondition')
                    validIconList.append(iconFile != None)
                    updatedElements.append((iconFile,position))

                iconData.append((properties, tuple(updatedElements)))

        data.obsIcon = iconData

        # Now create a list defining if the data is valid for each location
        # This list can be used to avoid displaying the city name if the data isn't valid.
        data.validLocList = map((lambda a,b: a or b), validTempList, validIconList)

        # Now combine the valid flag for all locations into a single value.
        # If any location is valid, then clear the noDataAvailable flag.
        if ( reduce(lambda a, b: a or b, data.validLocList, 0) ):
            data.noDataAvailable = 0
        else:
            data.noDataAvailable = 1


class ForecastMap(twc.products.Product):
    # Just inherit the base class __init__
    #def __init__(self, params):
    #    Product.__init__(self, params)

    def _getDailyForecastData(self, coopId, timePeriod, dataType):
        value = None

        # key = dailyFcst.{coopId}.time_period
        # i.e. - dailyFcst.7219000.1060719026
        fcstKey  = 'dailyFcst.%s.%d' % (coopId,timePeriod,)
        fcstData = dsm.defaultedGet(fcstKey)

        if fcstData is not None:
            fcstData = twccommon.DefaultedData(fcstData)

            # TODO: add all other types to be supported
            if dataType == 'highTemp':
                value = fcstData.highTemp
            elif dataType == 'lowTemp':
                value = fcstData.lowTemp
            elif dataType == 'daySkyCondition':
                value = wxDataUtil.formatSkyCondition(
                fcstData.daySkyCondition, 'Forecast').iconFile
            elif dataType == 'eveningSkyCondition':
                value = wxDataUtil.formatSkyCondition(
                fcstData.eveningSkyCondition, 'Forecast').iconFile
            elif dataType == 'golfIndex':
                value = fcstData.golfIndex

        return value

    def _loadDataTimePeriod(self, timePeriod):
        params = twccommon.DefaultedData(self.getParams())
        # we can't fill in the 'data' member variable here like the
        # other _loadData() methods since products like
        # RegionalForecastConditions call this function several
        # times with different time periods. Each call would
        # would overwrite 'data' each time. SO we used 'timePeriodData'.
        timePeriodData = twccommon.Data()
        # assume no data (until we see some)
        timePeriodData.noDataAvailable = 1

        # fcstValue
        fdata = []
        validValueList = []
        # If params has a fcstValue field (fcstValue != None)
        # and it is not empty (fcstValue != [])
        if (params.fcstValue):
            for properties,elements in params.fcstValue:
                # what data value are we looking for in the Fcst?
                dataType = properties[5]

                updatedElements = []
                for item in elements:
                    station = item[0]
                    position   = item[1]

                    # getFcstData
                    value = self._getDailyForecastData(station, timePeriod, dataType)
                    # Note: zero is a valid temp, so check for None
                    validValueList.append(value != None) # Update a flag for each element
                    updatedElements.append((value,position))

                fdata.append((properties, tuple(updatedElements)))

            timePeriodData.fcstValue = fdata

            # fcstIcon
            idata = []
            validIconList = []
            # If params has a fcstIcon field (fcstIcon != None)
            # and it is not empty (fcstIcon != [])
            if (params.fcstIcon):
                for properties,elements in params.fcstIcon:
                    # icons don't have properties, skip to elements
                    updatedElements = []
                    for item in elements:
                        station = item[0]
                        position   = item[1]

                        # getFcstData
                        iconFile = self._getDailyForecastData(station, timePeriod, 'daySkyCondition')
                        validIconList.append(iconFile != None) # Update a flag for each element
                        updatedElements.append((iconFile,position))

                    idata.append((properties, tuple(updatedElements)))

                timePeriodData.fcstIcon = idata

            # Now create a list defining if the data is valid for each location
            # This list can be used to avoid displaying the city name if the data isn't valid.
            # The current rule is the location is valid if any part (temp, icon, ...) is valid
            timePeriodData.validLocList = map((lambda a,b: a or b), validValueList, validIconList)

            # Now combine the valid flag for all locations into a single value.
            # If any location is valid, then clear the noDataAvailable flag.
            if ( reduce(lambda a, b: a or b, timePeriodData.validLocList, 0) ):
                timePeriodData.noDataAvailable = 0
            else:
                timePeriodData.noDataAvailable = 1

        return timePeriodData
