# ----------------------------------------------------------------------------
# This software is in the public domain, furnished "as is", without technical
# support, and with no warranty, express or implied, as to its usefulness for
# any purpose.
#
# FWF_Local
#  Local customizations for FWF as Base class
#
# Author: Gibson
#
#  NEw version for RPP21.1  8/03
#
#-------------------------------------------------------------------------
# Edit Areas Needed:
#   Be sure to define these additional areas to run the FWF:
#   For local effects: AboveElev, BelowElev
#     Define edit areas with an appropriate elevation (e.g. in CO, it is 11000 for timberline).
#     This will be used to report local effects for temperature and RH.
#   FireArea:  An edit area for the summary extended forecast.
#-------------------------------------------------------------------------

import FWF
import TextRules
import string, time, re, os, types, copy, AFPS

class TextProduct(FWF.TextProduct):
    Definition = copy.deepcopy(FWF.TextProduct.Definition)
    
    # REQUIRED CONFIGURATION ITEMS
    Definition['displayName'] = "None"
##    Definition['displayName'] = "FWF"
    Definition["outputFile"] = "/ifps/rpp-21/products/TEXT/FWF.txt"
    Definition["defaultEditAreas"] = [
           ("Fire1Near5000", "FIRE AREA 1"),
           ("Fire2Near5000", "FIRE AREA 2"),
           ("Fire3Near3000", "FIRE AREA 3"),
           ]
    
    # Header configuration items
    Definition["productName"] = "FIRE WEATHER FORECAST"  # name of product
    Definition["fullStationID"] = "KSLC"  # full station identifier (4letter)
    Definition["wmoID"] = "FNUS55"        # WMO ID
    Definition["pil"] = "WRKFWF"          # product pil
    Definition["zoneCode"] = "UTZ001>021"     # Zone Code, such as "GAZ025-056"
    Definition["stateName"] = "UTAH" # Name of state, such as "GEORGIA"
    Definition["wfoCity"] = "SALT LAKE CITY"     # Location of WFO - city name
    Definition["wfoState"] = "UT"   # Location of WFO - state name

    # OPTIONAL CONFIGURATION ITEMS
    Definition["awipsProductID"] = ""     # Product ID for storing to AWIPS.
    Definition["includeHeader"] = 1       # If 1, include the product header.  
    Definition["useRegionLabel"] = 0      # If 1, use region label for headers.
    Definition["includeIssuanceType"] = 0 # If 1, an issuance type button will appear on dialog
                                           # to handle updates and corrections.
    #Definition["periodCombining"] = 1     # If 1, do period combining

    Definition["useRH"] = 0                # Use RH grids instead of MaxRH, MinRH
    Definition["summaryExtended"] = 0
    Definition["summaryArea"] = "CWA"
    Definition["individualExtended"] = 1
    Definition["extendedLabel"] = 1
    Definition["useHolidays"] = 0       # Will use holidays in time period labels
    Definition["includeTrends"] = 0     # Set to 1 to include Temp and RH trends
    
    # Set the following variable to 1 if you want Lightning Activity
    # reported with phrases like "1-8 STRIKES", "9-15 STRIKES", etc.
    #Definition["lightningPhrases"] = 1
    
    # The following variable sets a wind adjustment factor for surface
    # (20 ft) winds.  Wind speeds will be multiplied by this factor.
    # Winds reported by RAWS sites are frequently lower than ASOS winds
    # due to the fact that they use a 10-min average.  A common adjustment
    # factor is 80% (0.80).  If you want no adjustment to the winds
    # then set this variable to 1.00
    Definition["windAdjustmentFactor"] =  1.00

    Definition["includeMultipleElementTable"] = 0       # Will include a MultipleElementTable
    Definition["includeMultipleElementTable_perPeriod"] = 0 # Will include a MultipleElementTable
                                                         # per area per period.
                                                         # ("singleValueFormat" must be 1)
    # Uncomment just one elementList below
    #Definition["elementList"] = ["Temp", "PoP"]         # Default
    #Definition["elementList"] = ["Temp", "Humidity", "PoP"]
    #Definition["singleValueFormat"] = 1                 # Default is 0

    #Definition["areaDictionary"] = "AreaDictionary_FWF"     # For product headers
    #Definition["language"] = "english"                 

    # Sampling Performance
    #Definition["sampleFromServer"] = 1       # If 1, sample directly from server
    # Trouble-shooting items
    #Definition["passLimit"] = 20             # Limit on passes allowed through
                                              # Narrative Tree
    #Definition["trace"] = 1                   # Set to 1 to turn on trace through
                                              # Narrative Tree for trouble-shooting
    def __init__(self):
        FWF.TextProduct.__init__(self)

    wordsArray = []
    TempRHwordsArray = []
    noTrendsArray = []
    
    def null_nlValue_dict(self, tree, node):
        dict = TextRules.TextRules.null_nlValue_dict(self, tree, node)
        dict["Wind"] = 15 ## no specific winds below 15 mph.
        return dict

    #  NULL value phrases
    def first_null_phrase_dict(self, tree, node):
        # Phrase to use if values THROUGHOUT the period or
        # in the first period are Null (i.e. below threshold OR NoWx)
        # E.g.  LIGHT WINDS.    or    LIGHT WINDS BECOMING N 5 MPH.
        dict = TextRules.TextRules.first_null_phrase_dict(self, tree, node)

        dayNight = self.getPeriod(node.getTimeRange(),1)
        phrase = "light winds"
        if dayNight == self.DAYTIME():
            phrase = "upslope/upvalley 5-10 mph"
        else:
            phrase = "downslope/downvalley 5-10 mph"

        componentName = node.getComponent().get("name")
        
        if componentName == "FireExtendedShortTerm":
            phrase = "winds generally less than 15 mph"
            
        dict["Wind"] = phrase
            
        return dict

    def null_phrase_dict(self, tree, node):
        # Phrase to use for null values in subPhrases other than the first
        # Can be an empty string
        #  E.g.  "NORTH WINDS 20 to 25 KNOTS BECOMING LIGHT"
        dict = TextRules.TextRules.null_phrase_dict(self, tree, node)
        dayNight = self.getPeriod(node.getTimeRange(),1)
        phrase = ""
        if dayNight == self.DAYTIME():
            phrase = "upslope/upvalley 5-10 mph"
        else:
            phrase = "downslope/downvalley 5-10 mph"
        
        dict["Wind"] = phrase
        return dict

    def value_connector_dict(self, tree, node):
        dict = TextRules.TextRules.value_connector_dict(self, tree, node)
        dict["Wind"] = "-"

        return dict
    
    def units_descriptor_dict(self, tree, node):
        # Threshold for reporting null values
        # Units depend on the element and product
        dict = TextRules.TextRules.units_descriptor_dict(self, tree, node)
        unitsDict = dict["units"]
        unitDict = dict["unit"]
        # Uncomment these lines if you want the "F" label on the temperatures
        #unitsDict["F"] = "F"
        #unitDict["F"] = "F"
        #dict["units"] = unitsDict
        #dict["unit"] = unitDict
        return dict

    def includeSkyRanges_flag(self, tree, node):
        # Set to 0 if you do not want ranges reported with sky phrases:
        #   Partly cloudy (35-40 PERCENT)
        return 1

    def untilPhrasing_flag_dict(self, tree, node):
        # If set to 1, "until" time descriptor phrasing will be used.
        # E.g. "NORTH WINDS 20 MPH UNTIL 10 AM...THEN 35 MPH"
        #
        # NOTE: Be sure to increase the temporal resolution by 
        # overriding "getFirePeriod_analysisList" from the FWF standard file.
        # E.g.  ("MixHgt", self.minMax, [0]),

        dict = TextRules.TextRules.untilPhrasing_flag_dict(self, tree, node)
        dict["LAL"] = 1
        componentName = node.getComponent().get("name")
        if componentName == "FirePeriod":
            dict["Sky"] = 1
            dict["Wx"]  = 1
        return dict

    def ridgeValleyAreas(self, tree, node):
        # List of edit area names for which we want
        # ridge/valley winds reported:
        #
        # 20-FOOT WINDS...
        #     VALLEYS/LWR SLOPES...
        #     RIDGES/UPR SLOPES....       
        #
        # e.g.
        #return ["Area1"]
        return []

    def getFirePeriod_intersectAreas(self):
        return []
 
    # Configurable Weather Values
    def wxCoverageDescriptors(self):
         list = TextRules.TextRules.wxCoverageDescriptors(self)
         #list.append(("Chc", "*", "*", "a chance"))
         return list

    def wxTypeDescriptors(self):
         list = TextRules.TextRules.wxTypeDescriptors(self)
         #list.append( ("*", "T", "*", "Dry", "dry thunderstorms") )
         #list.append( ("*", "RW", "*", "*", "rain showers") )
         return list

    def wxAttributeDescriptors(self):
         list = TextRules.TextRules.wxAttributeDescriptors(self)
         #list.append( ("*", "T", "*", "Dry", "") )
         return list

    def wxIntensityDescriptors(self):
         list = TextRules.TextRules.wxIntensityDescriptors(self)
         #list.append(("*", "RW", "--", "*", "light"))
         return list

    def wxCombinations(self):
        # This is the list of which wxTypes should be combined into one.
        # For example, if ("RW", "R") appears, then wxTypes of "RW" and "R" will
        # be combined into one key and the key with the dominant coverage will
        # be used as the combined key.
        # You may also specify a method which will be
        #  -- given arguments subkey1 and subkey2 and
        #  -- should return
        #     -- a flag = 1 if they are to be combined, 0 otherwise
        #     -- the combined key to be used
        #  Note: The method will be called twice, once with (subkey1, subkey2)
        #  and once with (subkey2, subkey1) so you can assume one ordering.
        #  See the example below, "combine_T_RW"
        # 
        return [
                ("RW", "R"),
                ("SW", "S"),
  ##              self.combine_T_RW,
            ]
    
    def combine_T_RW(self, subkey1, subkey2):
        # Combine T and RW only if the coverage of T
        # is dominant over the coverage of RW
        wxType1 = subkey1.wxType()
        wxType2 = subkey2.wxType()
        if wxType1 == "T" and wxType2 == "RW":
            order = self.dominantCoverageOrder(subkey1, subkey2)
            if order == -1 or order == 0:
                return 1, subkey1
        return 0, None
    
    def _PoP_analysisMethod(self, componentName):
        # Alternative PoP analysis methods for consistency between PoP and Wx
        return self.stdDevMaxAvg
        #return maximum
        # Use "mode" if you have non-continuous PoP values
        #return mode

###  Above is FWF_Local
###   Below are additions and overrides.
        
    def getFirePeriod_additionalAreas(self):
        return [
          ("Sky", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("Wx", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("LAL", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("CWR", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
            ]
    
    def FireExtendedShortTerm_additionalAreas(self):
        return [
          ("Sky", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("Wx", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("MaxT", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("MinT", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("Wind", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
            ]
    
    def FireExtended_additionalAreas(self):
        return [
          ("Sky", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("Wx",  ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("MaxT", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
          ("MinT", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]),
            ]

### added "additionalAreas"
    def FirePeriod(self):
        phraseList = self.getFirePeriod_phraseList()
        analysisList = self.getFirePeriod_analysisList()
        intersectAreas = self.getFirePeriod_intersectAreas()
        additionalAreas = self.getFirePeriod_additionalAreas()
        return {
            "type": "component",
            "methodList": [
                          self._assembleIndentedPhrases,
                          ],
            "analysisList": analysisList,
            "phraseList": phraseList,
            "intersectAreas": intersectAreas,
            "additionalAreas": additionalAreas,
            }

## removed unneeded elements
    def getFirePeriod_analysisList(self):
         analysisList = [
           ("Headlines", self.discreteTimeRangesByKey),
           ("Sky", self.minMax, [0]),
           ("PoP", self._PoP_analysisMethod("FirePeriod")),
           ("Wx", self.dominantWx, [0]),
           ("LAL", self.maximum, [0]),
           ("MaxT", self.stdDevMinMax),
           ("MinT", self.stdDevMinMax),
           ("MaxRH", self.stdDevMinMax),
           ("MinRH", self.stdDevMinMax),
           ("MaxT", self.mode),   # for trends
           ("MinT", self.mode),   # for trends
           ("MaxRH", self.mode),  # for trends
           ("MinRH", self.mode),  # for trends
           ("Ttrend", self.stdDevMinMax),  # for trends
           ("RHtrend", self.stdDevMinMax), # for trends
           ("Wind", self.vectorMedianRange, [6]),
           ("Haines", self.avg),
           ("CWR", self.maximum, [0]),
          ]
         return analysisList
         
    def getFirePeriod_phraseList(self):
        if self._useRH:
            dayRH = "RH"
            nightRH = "RH"
        else:
            dayRH = "MinRH"
            nightRH = "MaxRH"
        phraseList =  [
            self.wind_summary,
            self.skyWeather_byTimeRange_compoundPhrase, 
            (self.dayOrNight_phrase, ["MaxT", "MinT", 1, 1], self._tempLocalEffects_list()),
            (self.dayOrNight_phrase, [dayRH, nightRH, 1, 1], self._rhLocalEffects_list()),
            self.fireWind_compoundPhrase,
            self.fireWind_label_phrase,
            self.fireWind_Ridge_compoundPhrase,
            (self.lal_phrase,self._lal_localeffects_list),
            (self.cwr_phrase,self._cwr_localeffects_list),
            self.haines_phrase,             
            ]
        # Remove trend methods
        if self._includeTrends != 1:
            newList = []
            for phrase in phraseList:
                if type(phrase) is types.TupleType:
                    phraseMethod = phrase[0]
                    if phraseMethod == self.trend_DayOrNight_phrase:
                       continue
                newList.append(phrase)
            phraseList = newList
        # Add multipleElementTable
        if self._includeMultipleElementTable_perPeriod:
            phraseList.append(self.multipleElementTable_perPeriod_phrase)
        return phraseList

#### modified for compund local effects for sky/weather
    def skyWeather_byTimeRange_compoundPhrase(self):
        return {
            "phraseList": [
                (self.fireSky_phrase,self._sky_localeffects_list),
                (self.weather_phrase,self._wx_localeffects_list),
                ],
            "phraseMethods": [
                self.assembleSentences,
                self.skyWeather_finishUp, 
            ],
            }

### reduce detail in extended weather
    def FireExtendedShortTerm(self):
        return {
            "type": "component",
            "methodList": [
                          self.assemblePhrases,   
                          self.wordWrap,          
                          ],
            "analysisList": [
                       ("MinT", self.firstAvg),
                       ("MaxT", self.avg),
                       ("T", self.hourlyTemp),
                       ("Sky", self.avg, [24]),
##                       ("Wind", self.vectorAvg),
                        ("Wind",self.vectorMax),
##                       ("Wind20ft", self.vectorAvg),
                       ("Wx", self.dominantWx, [24]),
                       ("PoP", self._PoP_analysisMethod("FireExtendedShortTerm")),
                      ],
            "phraseList":[
                   self.reportTrends,
                   self.wind_summary,
##                   self.skyPopWx_phrase,
                   (self.sky_phrase,self._xtnd_sky_localeffects_list),
                   (self.weather_phrase,self._xtnd_wx_localeffects_list),
                   (self.lows_phrase, self._T_localeffects_list),
                   (self.highs_phrase, self._T_localeffects_list),
                   (self.wind_phrase, self._xtnd_Wind_localeffects_list),
                 ],
            "additionalAreas": self.FireExtendedShortTerm_additionalAreas(),
            }
    
 ### removed skyPopWx phrase
    def FireExtended(self):
        return {
            "type": "component",
            "methodList": [
                          self.assemblePhrases,   
                          self.wordWrap,          
                          ],
            "analysisList": [
                       ("MinT", self.firstAvg),
                       ("MaxT", self.avg),
                       ("T", self.hourlyTemp),
                       ("Sky", self.avg, [24]),
                       ("Wx", self.dominantWx, [24]),
                       ("PoP", self._PoP_analysisMethod("FireExtended")),
                      ],
            "phraseList":[
                   self.reportTrends,
##                   self.skyPopWx_phrase,
                   (self.sky_phrase,self._xtnd_sky_localeffects_list),
                   (self.weather_phrase,self._xtnd_wx_localeffects_list),
                   (self.lows_phrase,self._T_localeffects_list),
                   (self.highs_phrase,self._T_localeffects_list),
                 ],
            "additionalAreas": self.FireExtended_additionalAreas(),
            }
   
##### Short term Local effects
    def _wx_localeffects_list(self,tree,node):
        if self.currentAreaContains(tree, ["Fire1Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        elif self.currentAreaContains(tree, ["Fire2Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        elif self.currentAreaContains(tree, ["Fire3Near3000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        else:
            return[]
        
    def _sky_localeffects_list(self,tree,node):
        if self.currentAreaContains(tree, ["Fire1Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],30," and ")]
        elif self.currentAreaContains(tree, ["Fire2Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],30," and ")]
        elif self.currentAreaContains(tree, ["Fire3Near3000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],30," and ")]
            
        else:
            return[]

    def _lal_localeffects_list(self,tree,node):
        if self.currentAreaContains(tree, ["Fire1Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        elif self.currentAreaContains(tree, ["Fire2Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        elif self.currentAreaContains(tree, ["Fire3Near3000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        else:
            return[]
 
    def _cwr_localeffects_list(self,tree,node):
        if self.currentAreaContains(tree, ["Fire1Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],10," and ")]
        elif self.currentAreaContains(tree, ["Fire2Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],10," and ")]
        elif self.currentAreaContains(tree, ["Fire3Near3000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],10," and ")]
        else:
            return[]
    
    def _Wind_localeffects_list(self,tree,node):
        if self.currentAreaContains(tree, ["Fire1Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],5," and ")]
        elif self.currentAreaContains(tree, ["Fire2Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],5," and ")]
        elif self.currentAreaContains(tree, ["Fire3Near3000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],5," and ")]
        else:
            return[]
        
### Extended local effects -  Only need for minor spacing issues when the phrases are assembled.

    def _xtnd_wx_localeffects_list(self,tree,node):
        if self.currentAreaContains(tree, ["Fire1Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire1Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        elif self.currentAreaContains(tree, ["Fire2Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire2Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        elif self.currentAreaContains(tree, ["Fire3Near3000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire3Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        else:
            return[]
        
    def _xtnd_sky_localeffects_list(self,tree,node):
        if self.currentAreaContains(tree, ["Fire1Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire1Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],self.Sky_test," and ")]
        elif self.currentAreaContains(tree, ["Fire2Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire2Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],self.Sky_test," and ")]
        elif self.currentAreaContains(tree, ["Fire3Near3000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire3Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],self.Sky_test," and ")]
        else:
            return[]
        
    def _xtnd_Wind_localeffects_list(self,tree,node):
        if self.currentAreaContains(tree, ["Fire1Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire1Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],5," and ")]
        elif self.currentAreaContains(tree, ["Fire2Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire2Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],5," and ")]
        elif self.currentAreaContains(tree, ["Fire3Near3000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire3Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],5," and ")]
        else:
            return[]

####   T local effects are only needed in the extended
    def _T_localeffects_list(self,tree,node):
        if self.currentAreaContains(tree, ["Fire1Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire1Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        elif self.currentAreaContains(tree, ["Fire2Near5000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire2Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        elif self.currentAreaContains(tree, ["Fire3Near3000"]):
            leArea1 = self.LocalEffectArea("__Current__", "in the lower valleys", intersectFlag=0)
            leArea2 = self.LocalEffectArea("Fire3Near8000", "in the mountains", intersectFlag=0)
            return [self.LocalEffect([leArea1,leArea2],0," and ")]
        else:
            return[]

    def Sky_test(self, tree, node, localEffect, leArea1Label, leArea2Label):
        ## checks the current area and local effect sky condition WORDS to
        ## decide wether to fire local effects.  Only works for fire extended which
        ##  averages over entire 24 hours.
        
        ## background info
        statDict = node.getStatDict()
        timeRange = node.parent.getTimeRange()
        dayNight = self.getPeriod(timeRange, 1)
        ## get _current_ area sky words
        currentAreaStats = self.getStats(statDict, "Sky")
        currentAreasky = self.getValue(currentAreaStats)
        ## round to nearest 5
        currentAreasky = self.round(currentAreasky, "Nearest",5)
        currentSkyWords = self.sky_value(tree, node, currentAreasky, dayNight)
        ## get LE area sky words
        LEAreaStats = tree.stats.get("Sky", timeRange, leArea1Label)
        LEAreaskytuple = LEAreaStats[0]
        LESky = LEAreaskytuple[0]
        LESky = self.round(LESky, "Nearest",5)
        LEAreaWords = self.sky_value(tree, node, LESky, dayNight)
##        print "curr sky/words: ", currentAreasky, currentSkyWords
##        print "LE sky/words: ", LESky, LEAreaWords
        if currentSkyWords == LEAreaWords:
            return 0
        else:
            return 1

##################### end local effects setup
        
    def hainesDict(self):
        return {
            0:"..very low",
            1:"..very low",
            2:"..very low",
            3:"..very low",
            4:"..low",
            5:"..moderate",
            6:"..high",
            }

## section below overridden from ConfigVariables
    def units_descriptor_dict(self, tree, node):
        # Dictionary of descriptors for various units
       
        return {
            "units": {
                "percent":"%",
                "%":"%", ## changed this
                "mph":"mph",
                },
            "unit": {
                "percent":"%",
                "%":"%",  # and this
                "mph":"mph",
                },
        }
    
    def maximum_range_nlValue_dict(self, tree, node):
        # Maximum range to be reported within a phrase
        #   e.g. 5 to 10 mph
        # Units depend on the product
        dict = TextRules.TextRules.maximum_range_nlValue_dict(self,tree, node)
      ##  return {}
        dict["Wind"] = { 
                       (0, 5) : 0, 
                       (5, 13) : 5, 
                       (13, 30) : 10,
                       "default" : 15,
                       }
        return dict
    
    #  Descriptors: Phrase, Unit, Time periods
    #  Connecting phrases
    def phrase_descriptor_dict(self, tree, node):
##        # Dictionary of descriptors for various weather elements in phrases
##        # The value for an element may be a phrase or a method
##        # If a method, it will be called with arguments:
##        #   tree, node, key, element
##
        dict = TextRules.TextRules.phrase_descriptor_dict(self, tree, node)
        componentName = node.getComponentName()
        if componentName == "FirePeriod":
            dict["Wind"]=""
        else:
            dict["Wind"]="winds"
            
####        dict["longTermWind"]="Winds"
        dict["20-FOOT WINDS......."]="20-FOOT WINDS.......SLOPE/VALLEY..."
        dict["RIDGE WINDS......."]="                    RIDGETOPS......"
        dict["unchanged"]="no change"
        dict["CWR................."]="CWR................."
        dict["MinT_FireWx"]="" 
        dict["MaxT_FireWx"]=""
        dict["MinRH_FireWx"]=""
        dict["MaxRH_FireWx"]=""
        return dict
    
    def cwr_words(self, tree, node) :
        "Create phrase Probability of Precipitation"
        statDict = node.getStatDict()
        cwr = self.getStats(statDict, "CWR")
        if cwr is None:
            return self.setWords(node.parent, "MISSING")
        cwr = self.getValue(cwr)
        cwr = int(self.round(cwr, "Nearest",10))
        threshold = self.nlValue(self.null_nlValue(
            tree, node, "CWR", "CWR"), cwr)
        if int(cwr) < threshold:
            return self.setWords(node, "null")
        else:
            words =  `int(cwr)` + "%"
       #####################     words =  `int(cwr)` + " percent"
        return self.setWords(node, words)
    
    def wxCoverageDescriptors(self):
        # This is the list of coverages, wxTypes, intensities for which special
        # weather coverage wording is desired.  Wildcards (*) can be used to match any value.
        # If a weather subkey is not found in this list, default wording
        # will be used from the Weather Definition in the server.
        # The format of each tuple is:
        #    (coverage, wxType, intensity, weatherType descriptor)
        # For example:
        #return [
        #    ("Chc", "*", "*", "a chance"),
        #    ]
        return [("Chc", "*", "*", "*", "a chance of"),
                ("SChc", "*", "*", "*","a slight chance of")]
    
    def addSkyRange(self, tree, node, words, avg):

### added "cloud cover"
        
        # Add range if desired
        if self.includeSkyRanges_flag(tree, node):
            roundAvg = int(self.round(avg, "Nearest", 5))
            if roundAvg < 5:
                min = 0
            else:
                min = roundAvg-5
            if roundAvg >= 100:
                max = 100
            else:
                max = roundAvg + 5
            units = self.units_descriptor(tree, node, "units", "%")            
            words = words +  " (" + `min` + "-" + `max` + "% cloud cover)"
        return words
        
#################################  Patch 3056
        ############  pathc for RPP21.1
    def reportTrends_valueStr(self, tree, node, diff): 
        # Given a difference between current and 24-hour prior 
        # MaxT or MinT grids, report a trend. 
        if diff > 15 and diff < 25: 
            return "warmer" 
        elif diff >= 25: 
            return "much warmer" 
        elif diff < -15 and diff > -25: 
            return "cooler" 
        elif diff <= -25: 
            return "much colder" 
        else: 
            return ""
########### end patch
########################## patch 3118        
    def nlValue(self, nlValue, lookupValue):
        # Apply a non-linear value to the given value
        # nlValue might be a dictionary to be applied to value
        # OR it could be a simple constant
        if type(nlValue) is types.DictType:
            # Dictionary lookup
            result = None
            if nlValue.has_key('default'):
                result = nlValue['default']
            dictkeys = nlValue.keys()
            for key in dictkeys:
                if((lookupValue >= key[0]) and (lookupValue < key[1])):
                    return nlValue[key]
            if result is None:
                msgString = """ILLEGAL NON-LINEAR THRESHOLD dictionary.
                No dictionary entry for value: """ + `lookupValue` + """
                Make sure your non-linear threshold dictionaries do not
                have "gaps" in the ranges. For example, your dictionary
                should look like this:
                
                   def maximum_range_nlValue_dict(self, tree, node):
                        ### ConfigVariables
                        # Maximum range to be reported within a phrase
                        #   e.g. 5 to 10 mph
                        # Units depend on the product
                        dict = TextRules.TextRules.maximum_range_nlValue_dict(self,tree, node)
                        dict["Wind"] = {
                            (0, 5) : 0,
                            (5, 13) : 5,
                            (13, 28) : 10,
                            "default" : 15,
                            }
                        return dict
                    
                NOT this:
                    
                   def maximum_range_nlValue_dict(self, tree, node):
                        ### ConfigVariables
                        # Maximum range to be reported within a phrase
                        #   e.g. 5 to 10 mph
                        # Units depend on the product
                        dict = TextRules.TextRules.maximum_range_nlValue_dict(self,tree, node)
                        dict["Wind"] = {
                            (0, 4) : 0,
                            (5, 12) : 5,
                            (13, 27) : 10,
                            "default" : 15,
                            }
                        return dict
                    """                
                raise ValueError, msgString
            return result
        elif type(nlValue) is types.MethodType:
            return nlValue(lookupValue)
        else:
            # Constant value
            return nlValue

##################### end patch

############### override gen forecast to reset period counter to zero each pass
    def generateForecast(self, argDict):
        # Generate Text Phrases for a list of edit areas

        # Get variables
        error = self._getVariables(argDict)
        if error is not None:
            return error

        # Get the areaList -- derived from defaultEditAreas and
        # may be solicited at run-time from user if desired
        self._areaList = self.getAreaList(argDict)

        # Determine time ranges
        error = self._determineTimeRanges(argDict)
        if error is not None:
            return error

        # Sample the data
        error = self._sampleData(argDict)
        if error is not None:
            return error

        # Initialize the output string
        fcst = ""
        fcst = self._preProcessProduct(fcst, argDict)

        # Generate the product for each edit area in the list
        fraction = 0
        fractionOne = 1.0/float(len(self._areaList))
        percent = 50.0
        self.setProgressPercentage(percent)
        for editArea, areaLabel in self._areaList:
            ######   added next 2 lines
            self.AreaLabel = areaLabel
            self.period = 0  ## counter for getting ridge winds
            self.otherperiod = 0 ## counter for other data from FWFTable
            self.progressMessage(fraction, percent, "Making Product for " + areaLabel)
            fcst = self._preProcessArea(fcst, editArea, areaLabel, argDict)
            fcst = self._makeProduct(fcst, editArea, areaLabel, argDict)
            fcst = self._postProcessArea(fcst, editArea, areaLabel, argDict)
            fraction = fractionOne
        fcst = self._postProcessProduct(fcst, argDict)
        return fcst

###### adding a ghost period since chapter defines fire weather "outlook"
######  periods as midnight to midnight.
    
    def _issuance_list(self, argDict):
        narrativeDefAM = [
            ("FirePeriod", "period1"),
            ("FirePeriod", 12), ("FirePeriod", 12),("Phantom", 6)
           ]
        narrativeDefPM = [
            ("FirePeriod", "period1"),
            ("FirePeriod", 12), ("FirePeriod", 12), ("FirePeriod", 12),
            ("Phantom", 6)
            ]
        extended = [
            ("FireExtendedShortTerm", 24), ("FireExtendedShortTerm", 24),
            ("FireExtendedShortTerm", 24), 
            ("FireExtended", 24), ("FireExtended", 24),  
            ]
        if self._individualExtended == 1:
            if self._extendedLabel == 1:
                narrativeDefAM.append(("ExtendedLabel",0))
                narrativeDefPM.append(("ExtendedLabel",0))
            narrativeDefAM = narrativeDefAM + extended
            narrativeDefPM = narrativeDefPM + extended
        
        return [
            ("Morning", self.DAY(), self.NIGHT(), self.NIGHT(),
             ".TODAY...", "early in the morning", "late in the afternoon",
             1, narrativeDefAM), 
            ("Morning Update", "issuanceHour", self.NIGHT(), self.NIGHT(),
             ".REST OF TODAY...", "early in the morning", "late in the afternoon",
             1, narrativeDefAM), 
            ("Afternoon Update", "issuanceHour", self.NIGHT(), self.NIGHT(),
             ".REST OF TODAY...", "early in the morning","late in the afternoon",
             1, narrativeDefAM), 
            #  End times are tomorrow:
            ("Afternoon", self.NIGHT(), 24 + self.DAY(), 24 + self.DAY(),
             ".TONIGHT...", "late in the night", "early in the evening",
             1, narrativeDefPM), 
            ("Evening Update", "issuanceHour", 24 + self.DAY(), 24 + self.DAY(),
             ".REST OF TONIGHT...", "late in the night","early in the evening",
             1, narrativeDefPM),
            # For the early morning update, this produces:
            # REST OF TONIGHT:
            # MONDAY
            # MONDAY NIGHT
            ("Early Morning Update", "issuanceHour", self.DAY(), self.DAY(),
             ".REST OF TONIGHT...", "early in the morning","late in the afternoon",
             0, narrativeDefPM), 
            # Alternative
            # For the early morning update, this produces:
            # EARLY THIS MORNING:
            # TODAY
            # TONIGHT
            #("Evening Update", "issuanceHour", 24 + self.DAY(), 24 + self.DAY(),
            # ".REST OF TONIGHT...", "late in the night", "early in the evening",
            # 1, narrativeDefPM), 
            #("Early Morning Update", "issuanceHour", self.DAY(), self.DAY(),
            # ".EARLY THIS MORNING...", "early in the morning", "late in the afternoon",
            # 1, narrativeDefPM), 
            ]

##### I decide issuance type based on system time.
        
    def _getVariables(self, argDict):
        # Make argDict accessible
        self.__argDict = argDict

        # Get Definition variables
        self._definition = argDict["forecastDef"]
        for key in self._definition.keys():
            exec "self._" + key + "= self._definition[key]"

        # Display the dialog for the user to choose issuance        
        additionalItems = []
        if self._includeIssuanceType == 1:
            issuanceTypeEntry = ("Issuance Type" , "Routine", "radio",
                                 ["Routine", "Update", "Correction"]) 
            additionalItems = [issuanceTypeEntry]
##        status, varDict = self.displayIssuanceDialog(
##            self._definition["displayName"], self._issuance_list(argDict), additionalItems,
##            argDict)
##        if status != "Ok": 
##             return "Product Cancelled"
##        argDict["varDict"] = varDict        
        self._issuanceType = "Routine"
        if self._includeIssuanceType == 1:
            self._issuanceType = varDict["Issuance Type"]

        ## added this section
        time1 = time.gmtime()
        (year, month, day, h, m, s, w, day, dst) = time1
##        print "ZHour: ", h
        ## Adjust hours as needed in Zulu.
        if h > 17 or h < 6: # day shift
            self._productType = "Afternoon"
            self.totalPeriods = 4
        else:
            self._productType = "Morning"
            self.totalPeriods = 3
##        self._productType = "Morning"
##        self.totalPeriods = 3

##        print "product type: ", self._productType                    
        self._narrativeUtil = self._definition["defaultEditAreas"]
        return None

##### Section for our bullets at the top of the product.
    def _preProcessProduct(self, fcst, argDict):
        # Product header
        self.synopsis = self._getSynopsis()
        if self._areaName != "":
            self._areaName = "FOR " + self._areaName
        if self._issuanceType == "Update":
            self._areaName = self._areaName + "...UPDATED"
        elif self._issuanceType == "Correction":
            self._areaName = self._areaName + "...CORRECTED"
        fcst =  fcst + self._wmoID + " " + self._fullStationID + " " + \
               self._ddhhmmTime + "\n" + self._pil + "\n\n" +\
               self._productName + self._areaName + "\n" +\
               "NATIONAL WEATHER SERVICE " + self._wfoCity + " " + \
               self._wfoState + "\n" + self._timeLabel + "\n\n"+ \
               "...headline..." + "\n\n" + \
               ".BROADCAST DISCUSSION..." + self.synopsis + "\n"+ \
               ".DISCUSSION..." + "\n\n" + \
               "***THUNDERSTORMS IMPLY STRONG GUSTY AND ERRATIC WINDS***" + "\n" + \
               "***ALL WINDS ARE 20 FOOT...10-MINUTE AVERAGES***" + "\n\n"

        return fcst

##### Make SLC's 3 area headers.    
    def _preProcessArea(self, fcst, editArea, areaLabel, argDict):
        # This is the header for an edit area combination
        ## _makeFireAreaHeader defined below special for SLC.
        areaHeader = self._makeFireAreaHeader(
            argDict, areaLabel, self._issueTime, self._expireTime,
            self._areaDictionary, self._defaultEditAreas)
        fcst = fcst + areaHeader
        if self._issuanceType == "Update":
            fcst = fcst + "UPDATED\n\n"
        elif self._issuanceType == "Correction":
            fcst = fcst + "CORRECTED\n\n"

        # Headlines
        headlines = self.generateProduct("Headlines", argDict,
                                    area = editArea, areaLabel=areaLabel,
                                    timeRange = self._timeRange)
        fcst = fcst + headlines
        return fcst

    def _makeFireAreaHeader(self, argDict, areaLabel, issueTime, expireTime,
                   areaDictName, defaultEditAreas, cityDescriptor ="INCLUDING THE CITIES OF"):
        # Make a UGC area header for the given areaLabel
        # Determine list of areas (there could be more than one if we are using a combination)
        if areaDictName == None or areaDictName == "None":
            return areaLabel + "\n"
        areaList = self.getCurrentAreaNames(argDict, areaLabel)
        # Access the UGC information for the area(s) if available
    ##    print "areaList: ", areaList
        
        expireTimeRange = AFPS.TimeRange(expireTime, expireTime + 3600)
        expireTime = string.upper(self.timeDisplay(expireTimeRange, "", "", "%d%H%M", ""))
        #codeString = self.endline(codeString + "-" + expireTime + "-", breakStr="-")

        codeString = ""
        nameString = ""
        header = ""
        if areaList == ['FIRE AREA 1']:
            codeString = "UTZ001>011-"
            codeString = self.endline(codeString + "-" + expireTime + "-", breakStr="-")
            nameString = "FORECAST AREA 1...NORTHERN UTAH\nZONE 420-GREAT SALT LAKE DESERT " + \
                         "AND MOUNTAINS...ZONE 421-CACHE\nVALLEY...ZONE 422-NORTHERN WASATCH " + \
                         "FRONT...ZONE 423-SALT LAKE AND\nTOOELE VALLEYS...ZONE 424-SOUTHERN " + \
                         "WASATCH FRONT...ZONE 425-WASATCH\nMOUNTAIN VALLEYS...ZONE 426-WASATCH " + \
                         "MOUNTAINS NORTH OF I-80...ZONE\n427-WASATCH MOUNTAINS SOUTH OF I-80..." + \
                         "ZONE 428-WESTERN UINTA\nMOUNTAINS...ZONE 429-WEST TAVAPUTS PLATEAU " + \
                         "AND SURROUNDING\nRANGES...ZONE 430-WESTERN UINTA BASIN"
            header = codeString + nameString + "\n\n"
        if areaList == ['FIRE AREA 2']:
            codeString = "UTZ012>015-"
            codeString = self.endline(codeString + expireTime + "-", breakStr="-")
            nameString = "FORECAST AREA 2...CENTRAL UTAH\nZONE 431-CASTLE VALLEY...ZONE 432-" + \
                         "SAN RAFAEL SWELL AND DESERT...\nZONE 433-SANPETE AND SEVIER VALLEYS" + \
                         "...ZONE 434-WEST CENTRAL DESERTS\nAND MOUNTAINS...ZONE 436-CENTRAL " + \
                         "UTAH MOUNTAINS"
            header = codeString + nameString + "\n\n"
        if areaList == ['FIRE AREA 3']:
            codeString = "UTZ016>021-"
            codeString = self.endline(codeString + expireTime + "-", breakStr="-")
            nameString = "FORECAST AREA 3...SOUTHWEST UTAH\nZONE 435-SOUTHWEST DESERTS AND " + \
                         "MOUNTAINS...ZONE 437-SOUTHWEST AND\nSOUTH CENTRAL MOUNTAINS...ZONE " + \
                         "438-HENRY MOUNTAINS...ZONE 439-UTAHS\nDIXIE AND ZION CANYON...ZONE " + \
                         "440-441 SOUTH CENTRAL UTAH"
            header = codeString + nameString + "\n\n"
       
        return header

    def _postProcessArea(self, fcst, editArea, areaLabel, argDict):
        fcst = fcst + "=\n\n$$\n\n"   ## changed from + "=\n$$\n\n" 
        return fcst

    def _postProcessProduct(self, fcst, argDict):
        longRange = self._getlongRange()
        longRangeLabel = self._longRangeLabel()
        fcst = fcst + ".8-14 DAY OUTLOOK FOR "+ longRangeLabel + "\n" + longRange + "\n=\n$$\n"
        fcst = string.upper(fcst)
        fcst = string.replace(fcst, "MPH..", "MPH.")
        fcst = string.replace(fcst, " %.", ".")
        fcst = string.replace(fcst, "MAX TEMPERATURE", "\nMAX TEMPERATURE")
        fcst = string.replace(fcst, "MIN TEMPERATURE", "\nMIN TEMPERATURE")
        fcst = string.replace(fcst, "20-FOOT", "\n20-FOOT")
        self.storeAWIPS(fcst, self._awipsProductID)
        self.setProgressPercentage(100)
        self.progressMessage(0, 100, self._displayName + " Complete")
        return fcst

    def setLabel(self, tree, component):
        ## removed extra blank line before extended label.
        component.set("words", ".EXTENDED...\n")
        return self.DONE()

    def _getSynopsis(self):
        # pull synopsis in for brodcast discussion
        w,r = os.popen2('/ifps/rpp-21/getSynopsis.pl')
        w.close()
        phrase = r.read()
        return phrase

    def _getlongRange(self):
        # pull long range data from PMDMRD
        w,r = os.popen2('/ifps/rpp-21/getPMDMRD.pl')
        w.close()
        phrase = r.read()
        return phrase

    def _longRangeLabel(self):
        time1 = time.gmtime()
        (year, month, day, h, m, s, w, day, dst) = time1
        
        if self._productType == "Afternoon" and h>=6:
            timeRange = self.createTimeRange(192, 336, "Zulu")
            label= self.timeDisplay(timeRange, "Zulu", "","%A %B %d", \
                    " to %A %B %d")
        else:
            timeRange = self.createTimeRange(168, 312, "Zulu")
            label= self.timeDisplay(timeRange, "Zulu", "","%A %B %d", \
                    " to %A %B %d")

        return label
    
############## removed wind_summary
    def fireWind_compoundPhrase(self):
        return {
            "phraseList": [
##                self.wind_summary,
                self.wind_phrase,
                ],
            "phraseMethods": [
                self.assembleSentences,
                self.fireWind_finishUp
                ],
            }
            
######################## Next section does the ridge winds
    def fireWind_Ridge_compoundPhrase(self):
        self.period = self.period + 1
        self.ridgeWindWords = self._getRidgeWind(self.period)
        return {
            "phraseList": [
##                self.wind_summary,
                self._ridge_wind_phrase,
                ],
            "phraseMethods": [
                self.assembleSentences,
                self.fireWind_Ridge_finishUp,
                ],
            }
    
    def fireWind_Ridge_finishUp(self, tree, node):
        self.period = self.period + 1
        words = self.ridgeWindWords
        wordsArray = string.split(words, "#")
        if words is None:
            return
        if words == "":
            words = "MISSING"
        node.set("descriptor", "")
        node.set("indentLabel", "RIDGE WINDS.......")
        node.set("compound", 1)
        return self.setWords(node, wordsArray[self.period-4])
    
    def _getRidgeWind(self,period):
        print "getting ridge wind data"
        w,r = os.popen2('/ifps/rpp-21/getRidgeWind.pl')
        numperiod = str(self.totalPeriods)
        period = str(self.period)
        w.write(numperiod)
        w.write(period)
        w.write(self.AreaLabel)
        #w.write(element)
        w.close()
        phrase = r.read()
##        print "ridge wind phrase: ", phrase
        return phrase
    
    def _ridge_wind_phrase(self):
        ##self.period = self.period + 1
        return {
            "setUpMethod": self.wind_setUp,
            "wordMethod": self._ridge_vector_words,
            "phraseMethods": self.standard_vector_phraseMethods(),  
            }
 
    def _ridge_vector_words(self, tree, node):
        # Create words for a vector element
        elementInfo = node.getAncestor("firstElement")
        if elementInfo is None:
            return self.setWords(node, "")
        words = self.ridgeWindWords
        wordsArray = string.split(words, "#")
        if words == "null":
            return self.setWords(node, "null")
        gustPhrase = ""
        if words != "":
            # Add gusts
            gustFlag = node.getAncestor("gustFlag")
            if gustFlag == 1:
                maxMag, dir = tree.stats.get("Wind", node.getTimeRange(),
                                        node.getAreaLabel(), mergeMethod="Max")
                statDict = node.getStatDict()
                gustStats = self.getStats(statDict, "WindGust")
                subRange = node.get("timeRange")
                gustPhrase = self.embedded_gust_phrase(
                    tree, node, gustStats, maxMag, subRange)
        if self._productType == "Afternoon":
            return self.setWords(node, wordsArray[self.period-5] + gustPhrase)
        else:
            return self.setWords(node, wordsArray[self.period-4] + gustPhrase)
        
############## Next section for Temps/RH's and trends
   
    def _getOtherPhrase(self, elementName):
        if elementName == "MaxT" or elementName == "MinT":
            self.otherperiod = self.otherperiod + 1
        w,r = os.popen2('/ifps/rpp-21/getOtherFWFData.pl')
        numperiod = str(self.totalPeriods)
        period = str(self.otherperiod)
        w.write(numperiod)
        w.write(period)
        w.write(self.AreaLabel)
        w.write(elementName)
        w.close()
        phrase = r.read()
        return phrase
    
    def fire_dayOrNight_words(self, tree, node):
        ## get extra data from product/TEXT/FWFTable
        words = ""
        elementName = node.getAncestor("elementName")
        self.maxMinAndTrendWords = self._getOtherPhrase(elementName)
##        print elementName
##        print self.otherperiod
        TempRHwordsArray = string.split(self.maxMinAndTrendWords, "#")
        if self.AreaLabel == "FIRE AREA 1" or self.AreaLabel == "FIRE AREA 2":
            if self.otherperiod == 1 or self.otherperiod == 2: 
                if elementName == "MaxT":
                    words = "MAX TEMPERATURE.....NEAR 5000 FEET... " + TempRHwordsArray[0] + "." + \
                          "\n                    NEAR 8000 FEET... " + TempRHwordsArray[1]
                if elementName == "MinT":
                    words = "MIN TEMPERATURE.....NEAR 5000 FEET... " + TempRHwordsArray[0] + "." + \
                          "\n                    NEAR 8000 FEET... " + TempRHwordsArray[1]
                if elementName == "MaxRH":
                    words = "MAX HUMIDITY........NEAR 5000 FEET... " + TempRHwordsArray[0] + "." + \
                          "\n                    NEAR 8000 FEET... " + TempRHwordsArray[1]
                if elementName == "MinRH":
                    words = "MIN HUMIDITY........NEAR 5000 FEET... " + TempRHwordsArray[0] + "." + \
                          "\n                    NEAR 8000 FEET... " + TempRHwordsArray[1]
            else:
                ### no trend phrases for periods 3 or 4.
                noTrendsArray1 = string.split(TempRHwordsArray[0], "TREND...")
                noTrendsArray2 = string.split(TempRHwordsArray[1], "TREND...")
                
                if elementName == "MaxT":
                    words = "MAX TEMPERATURE.....NEAR 5000 FEET... " + noTrendsArray1[0].rstrip() + "." + \
                          "\n                    NEAR 8000 FEET... " + noTrendsArray2[0].rstrip()
                if elementName == "MinT":
                    words = "MIN TEMPERATURE.....NEAR 5000 FEET... " + noTrendsArray1[0].rstrip() + "." + \
                          "\n                    NEAR 8000 FEET... " + noTrendsArray2[0].rstrip()
                if elementName == "MaxRH":
                    words = "MAX HUMIDITY........NEAR 5000 FEET... " + noTrendsArray1[0].rstrip() + "." + \
                          "\n                    NEAR 8000 FEET... " + noTrendsArray2[0].rstrip()
                if elementName == "MinRH":
                    words = "MIN HUMIDITY........NEAR 5000 FEET... " + noTrendsArray1[0].rstrip() + "." + \
                          "\n                    NEAR 8000 FEET... " + noTrendsArray2[0].rstrip()
        else:
            if self.otherperiod == 1 or self.otherperiod == 2:
                if elementName == "MaxT":
                    words = "MAX TEMPERATURE.....NEAR 3000 FEET... " + TempRHwordsArray[0] + "." + \
                          "\n                    NEAR 5000 FEET... " + TempRHwordsArray[1] + "." + \
                          "\n                    NEAR 8000 FEET... " + TempRHwordsArray[2]
                if elementName == "MinT":
                    words = "MIN TEMPERATURE.....NEAR 3000 FEET... " + TempRHwordsArray[0] + "." + \
                          "\n                    NEAR 5000 FEET... " + TempRHwordsArray[1] + "." + \
                          "\n                    NEAR 8000 FEET... " + TempRHwordsArray[2]
                if elementName == "MaxRH":
                    words = "MAX HUMIDITY........NEAR 3000 FEET... " + TempRHwordsArray[0] + "." + \
                          "\n                    NEAR 5000 FEET... " + TempRHwordsArray[1] + "." + \
                          "\n                    NEAR 8000 FEET... " + TempRHwordsArray[2]
                if elementName == "MinRH":
                    words = "MIN HUMIDITY........NEAR 3000 FEET... " + TempRHwordsArray[0] + "." + \
                          "\n                    NEAR 5000 FEET... " + TempRHwordsArray[1] + "." + \
                          "\n                    NEAR 8000 FEET... " + TempRHwordsArray[2]
            else:
                    ### no trend phrases for periods 3 or 4.
                noTrendsArray1 = string.split(TempRHwordsArray[0], "TREND...")
                noTrendsArray2 = string.split(TempRHwordsArray[1], "TREND...")
                noTrendsArray3 = string.split(TempRHwordsArray[2], "TREND...")
                
                if elementName == "MaxT":
                    words = "MAX TEMPERATURE.....NEAR 3000 FEET... " + noTrendsArray1[0].rstrip() + "." + \
                          "\n                    NEAR 5000 FEET... " + noTrendsArray2[0].rstrip() + "." + \
                          "\n                    NEAR 8000 FEET... " + noTrendsArray3[0].rstrip()
                if elementName == "MinT":
                    words = "MIN TEMPERATURE.....NEAR 3000 FEET... " + noTrendsArray1[0].rstrip() + "." + \
                          "\n                    NEAR 5000 FEET... " + noTrendsArray2[0].rstrip() + "." + \
                          "\n                    NEAR 8000 FEET... " + noTrendsArray3[0].rstrip()
                if elementName == "MaxRH":
                    words = "MAX HUMIDITY........NEAR 3000 FEET... " + noTrendsArray1[0].rstrip() + "." + \
                          "\n                    NEAR 5000 FEET... " + noTrendsArray2[0].rstrip() + "." + \
                          "\n                    NEAR 8000 FEET... " + noTrendsArray3[0].rstrip()
                if elementName == "MinRH":
                    words = "MIN HUMIDITY........NEAR 3000 FEET... " + noTrendsArray1[0].rstrip() + "." + \
                          "\n                    NEAR 5000 FEET... " + noTrendsArray2[0].rstrip() + "." + \
                          "\n                    NEAR 8000 FEET... " + noTrendsArray3[0].rstrip()
                

##        outUnits = self.element_outUnits(tree, node, elementName, elementName)
##        print words
##        units = self.units_descriptor(tree, node,"units", outUnits)
##        words = words + units
##        print "after units: ", words
        dummy = self.setWords(node, words)
        return words
##        return self.setWords(node, words)

    def postProcessPhrase(self, tree, node):
        words = node.get("words")
        rval = None
        if words is not None:
            words =  words.replace("rain showers and thunderstorms", "showers and thunderstorms")
            words =  words.replace("of showers and snow showers", "of rain and snow showers")
            # To handle snow amt local effects
            words = words.replace("except of", "except")
            rval = self.setWords(node, words)
        return rval

####  Remove for RPP21.3+

###   begin label patch 3159
 ###########  Override for max/min phrase don;t remove
    def _assembleIndentedPhrases(self, tree, component):
        # Assemble and indent component phrases and add Label
        # Qualify the phrases with local effect qualifiers
        #  if present.
        #   e.g. "near the coast"

        for phrase in component.get("childList"):
            words = phrase.get("words")
            if words is None:
                return
        self.combineConjunctiveLocalEffects(tree, component)
        self.combineConjunctivePhrases(tree, component)
        fcst = ""
        lastQualifier = None
        lastPhrase = None
        for phrase in component.get("childList"):
            words = phrase.get("words")
            if words is None:
                return
 
            # Handle multiple element table phrase
            # that appears per period
            # No need to indent or qualify
            name = phrase.get("name")
            if name == "multipleElementTable_perPeriod_phrase":
                fcst = fcst + words
                continue
 
            if phrase.get("compound"):
                makeSentence = 0
            else:
                makeSentence = 1
            words, lastQualifier = self.qualifyWords(
                phrase, words, "conjunctiveQualifier", lastQualifier, lastPhrase,
                makeSentence=makeSentence)
            lastPhrase = phrase
            indentLabel = phrase.get("indentLabel")
            elementLabel = indentLabel
            indentLabel = self.phrase_descriptor(
                tree, phrase, indentLabel, indentLabel)
            if words == "":
                words = " "

#############  added this section to handle Max/Min labels and spacing.
            if elementLabel == "MaxT_FireWx" or elementLabel == "MinT_FireWx" \
               or elementLabel == "MinRH_FireWx" or elementLabel == "MaxRH_FireWx":
                fcst = fcst + words + "\n"
            else:
                words = self.labelIndent(words, indentLabel)
                fcst = fcst + words
            
        # Add label            
        curLocalTime, shift = self.determineTimeShift()
        issuanceInfo = tree.get("issuanceInfo")
        index = component.getIndex()
        label = self.createLabel(tree, component, component.get("timeRange"),
                                issuanceInfo, curLocalTime, shift, index)
        
        return self.setWords(component, label + "\n" + fcst + "\n")

 ####  remove for rpp21.3+
    def combineConjunctivePhrases(self, tree, component):
        # Check for Conjunctive Local Effects and make sure
        # we do not repeat the indented label.
        #
        #   For example:
        #   LAL.................IN THE VALLEYS ...1.
        #   LAL.................IN THE MOUNTAINS...3 UNTIL 2400...THEN 1.
        #
        #   Should be:
        #   LAL.................IN THE VALLEYS ...1.
        #                       IN THE MOUNTAINS...3 UNTIL 2400...THEN 1.
        #
        for phrase in component.get("childList"):
            words = phrase.get("words")
            if words is None:
                return
        newChildList = []
        lastName = ""
        lastPhrase = None
        for phrase in component.get("childList"):
            words = phrase.get("words")
            if words is None:
                return
            curName = phrase.get("name")
            if lastPhrase is None:
                lastPhrase = phrase
                lastName = curName
            else:
                # Look for a local effect phrase to be combined with
                # lastPhrase
                localEffect = phrase.get("localEffect")
                #print "phrase", curName, lastName, localEffect
                if localEffect is not None and curName == lastName:
                    # Combine this phrase words into last one
                    # Add conjunctive qualifier
                    #print "combining"
                    phraseWords = phrase.get("words")
                    qualifier = phrase.get("conjunctiveQualifier")
                    if qualifier is not None and qualifier != "":
                        phraseWords = qualifier + " "+ phraseWords
                    newWords = lastPhrase.get("words") + "." + \
                               phraseWords
                    lastPhrase.set("words", newWords)
                else:
                    # Add phrase to new list
                    #print "switching"
                    newChildList.append(lastPhrase)
                    lastPhrase = phrase
                    lastName = curName
        # Clean up lastPhrase
        if lastPhrase is not None:
            newChildList.append(lastPhrase)
        component.set("childList", newChildList)
      
#####   3159   END