#!/usr/bin/env python3

from urllib.request import urlopen
from datetime import date
from neopixel import *

import json, datetime, time, sys, random, colour, string
import threading

YourKey = ''

ledpin  = 18
numrows,ledsrow = 16, 18
numleds = numrows * ledsrow

#
# weather Underground data
#

def weatherForecast(YourKey):
    try:
        weatherUrl = urlopen('http://api.wunderground.com/api/' + YourKey + '/forecast/q/NL/Twenthe%20Air%20Base.json')
        json_string = weatherUrl.read()
        forecast_json = json.loads(json_string.decode('utf-8'))

        with open('forecast.txt', 'w') as jsonfile:
            json.dump(forecast_json, jsonfile)
        jsonfile.close()

        temp_high0 = forecast_json['forecast']['simpleforecast']['forecastday'][0]['high']['celsius']
        temp_low0  = forecast_json['forecast']['simpleforecast']['forecastday'][0]['low']['celsius']
        temp_high1 = forecast_json['forecast']['simpleforecast']['forecastday'][1]['high']['celsius']
        temp_low1  = forecast_json['forecast']['simpleforecast']['forecastday'][1]['low']['celsius']

        weatherUrl.close

        if str(temp_high0) != "":
            temp_high0 = int(float(temp_high0)*10)
        else:
            temp_high0 = int(float(temp_high1)*10)

        if str(temp_high1) != "":
            temp_high1 = int(float(temp_high1)*10)
        else:
            temp_high1 = int(float(temp_high0)*10)

        if str(temp_low0) != "":
            temp_low0  = int(float(temp_low0)*10)
        else:
            temp_low0  = int(float(temp_low1)*10)

        if str(temp_low1) != "":
            temp_low1  = int(float(temp_low1)*10)
        else:
            temp_low0  = int(float(temp_low0)*10)

    except Exception as diag:
        temp_high0, temp_high1, temp_low0, temp_low1 = 400, 400, -200, -200
        print(diag.__class__.__name__,':',diag)

    return min(temp_low0, temp_low1), max(temp_high0, temp_high1)

#
# weather Underground data
#

def weatherConditions(YourKey):
    try:
        weatherUrl = urlopen('http://api.wunderground.com/api/' + YourKey + '/conditions/q/NL/Twenthe%20Air%20Base.json')
        json_string = weatherUrl.read()
        conditions_json = json.loads(json_string.decode('utf-8'))

        with open('conditions.txt', 'w') as jsonfile:
            json.dump(conditions_json, jsonfile)
        jsonfile.close()

        temp_now = conditions_json['current_observation']['temp_c']
        wind_dir = conditions_json['current_observation']['wind_degrees']
        wind_kph = conditions_json['current_observation']['wind_kph']
        real_rain = conditions_json['current_observation']['precip_1hr_metric'] # actual rain

        weatherUrl.close

        temp_now  = int(float(temp_now)*10)
        wind_kph  = float(wind_kph)
        real_rain = float(real_rain)

    except Exception as diag:
        temp_now, wind_dir, wind_kph, real_rain = 0, 0, 0, 0
        print(diag.__class__.__name__,':',diag)

    return temp_now, wind_kph, wind_dir, real_rain

#
# weather Underground data
#

def weatherHourly(YourKey):
    try:
        weatherUrl = urlopen('http://api.wunderground.com/api/' + YourKey + '/hourly/q/NL/Twenthe%20Air%20Base.json')
        json_string = weatherUrl.read()
        hourly_json = json.loads(json_string.decode('utf-8'))

        with open('hourly.txt', 'w') as jsonfile:
            json.dump(hourly_json, jsonfile)
        jsonfile.close()

        hour_rain = hourly_json['hourly_forecast'][0]['qpf']['metric'] # Quantitative Precipitation Forecasts
        hour_snow = hourly_json['hourly_forecast'][0]['snow']['metric']

        weatherUrl.close()

    except Exception as diag:
        hour_rain, hour_snow = 0, 0
        print(diag.__class__.__name__,':',diag)

    return hour_rain, hour_snow

#
# Temperature Scale
#

def colorRange():
    today=date.today()

    if   (today.month == 1): minTempRange = -100 ; maxTempRange = 200 # Temperature values times 10
    elif (today.month == 2): minTempRange =  -50 ; maxTempRange = 250
    elif (today.month == 3): minTempRange =    0 ; maxTempRange = 250
    elif (today.month == 4): minTempRange =    0 ; maxTempRange = 300
    elif (today.month == 5): minTempRange =   50 ; maxTempRange = 300
    elif (today.month == 6): minTempRange =  100 ; maxTempRange = 350
    elif (today.month == 7): minTempRange =  100 ; maxTempRange = 300
    elif (today.month == 8): minTempRange =   50 ; maxTempRange = 300
    elif (today.month == 9): minTempRange =    0 ; maxTempRange = 300
    elif (today.month == 10):minTempRange =    0 ; maxTempRange = 300
    elif (today.month == 11):minTempRange =  -50 ; maxTempRange = 250
    else:                    minTempRange = -100 ; maxTempRange = 200

    return minTempRange, maxTempRange

#
# Lamp Class
#

class Lamp:
    def __init__(self, stick, minTemp, maxTemp):

        self.black = Color(0,0,0)
        self.white = Color(255,255,255)
        self.maxBrightBackground =  32
        self.maxWindBackground   =  96
        self.maxTempForeground   =  98
        self.maxRainForeground   = 192
        self.maxBrightForeground =  96
        self.xleds = 16
        self.yleds = 18

        self.minTempRange=minTemp
        self.maxTempRange=maxTemp
        self.stick=stick

        self.colorMapRed   = []
        self.colorMapGreen = []
        self.colorMapBlue  = []
        self.backArray     = [self.black] * self.yleds * self.xleds
        self.rainArray     = [self.black] * self.yleds * self.xleds
        self.windArray     = [self.black] * self.yleds * self.xleds
        self.windBackArray = [self.black] * self.yleds
        self.tempArray     = [self.black] * self.yleds
        self.temploop      = 0
        self.temprain      = float(0)
        self.temptemp      = float(0)

        self.hourRain    = 2
        self.windSpeed   = 2
        self.windDir     = 180
        self.temperature = 2

        self.ColorMap()
        self.BackGround()

    #
    # Creates an array with colors
    #

    def ColorMap(self):
        minRange   = abs(self.minTempRange)+1
        plusRange  = int(self.maxTempRange/2)+1
        minFactor  = abs(self.maxBrightBackground/minRange)
        plusFactor = self.maxBrightBackground/plusRange

        resultRed   = []
        resultGreen = []
        resultBlue  = []

        for x in range (plusRange):
                resultRed.append   (int(self.maxBrightBackground))
                resultGreen.append (int(self.maxBrightBackground-x*plusFactor))
                resultBlue.append  (int(0))

        for x in range (plusRange):
                resultRed.append   (int(self.maxBrightBackground-x*plusFactor/2))
                resultGreen.append (int(0))
                resultBlue.append  (int(x*plusFactor/2))

        for x in range (minRange):
                resultRed.append   (int(x*minFactor))
                resultGreen.append (int(x*minFactor))
                resultBlue.append  (int(self.maxBrightBackground-x*minFactor))

        self.colorMapRed   = list(resultRed)
        self.colorMapGreen = list(resultGreen)
        self.colorMapBlue  = list(resultBlue)

    def BackGround(self):
        for t in range (self.yleds):
            cindx = int (self.minTempRange + (self.maxTempRange-self.minTempRange)*t/self.yleds)

            r,g,b = self.colorMapGreen[cindx],self.colorMapRed[cindx],self.colorMapBlue[cindx]
            backcolor = Color(r,g,b)

            f=float(self.maxWindBackground/self.maxBrightBackground)
            windcolor = Color(int(r*f),int(g*f),int(b*f))

            tempcolor = [backcolor] * 4
            tempcolor[0] = Color(128,128,128)
            tempcolor[1] = Color(96,96,96)
            tempcolor[2] = Color(64,64,64)
            tempcolor[3] = Color(48,48,48)

            for r in range (self.xleds):
                self.backArray[numleds-1 -(t+r*self.yleds)] = backcolor
            self.windBackArray[self.yleds-1-t] = windcolor
            self.tempArray[t] = list(tempcolor)

    def rainMoveDown(self):
        for i in range (self.xleds):
            for j in range (self.yleds-1, 0, -1):
                self.rainArray [i*self.yleds+j] = self.rainArray [i*self.yleds+j-1]
            self.rainArray [i*self.yleds] = self.black

        self.temprain = self.temprain + self.hourRain
        for i in range (int(self.temprain)):
            x = random.randint(0,15)
            r = random.randint(self.maxBrightBackground,self.maxBrightForeground)
            g = random.randint(self.maxBrightBackground,self.maxBrightForeground)
            b = random.randint(self.maxBrightForeground,self.maxRainForeground)
            self.rainArray[x*18]=Color(r,g,b)
        self.temprain = self.temprain - int(self.temprain)

    def windMoveDir(self):

        if   (self.windSpeed < 1  ): insertLeds =  0
        elif (self.windSpeed < 6  ): insertLeds =  1
        elif (self.windSpeed < 12 ): insertLeds =  2
        elif (self.windSpeed < 20 ): insertLeds =  3
        elif (self.windSpeed < 29 ): insertLeds =  4
        elif (self.windSpeed < 39 ): insertLeds =  5
        elif (self.windSpeed < 50 ): insertLeds =  6
        elif (self.windSpeed < 62 ): insertLeds =  7
        elif (self.windSpeed < 75 ): insertLeds =  8
        elif (self.windSpeed < 89 ): insertLeds =  9
        elif (self.windSpeed < 103): insertLeds = 10
        elif (self.windSpeed < 117): insertLeds = 11
        else:                        insertLeds = 12

        if   (self.windDir <=  11.25): windLed = 0
        elif (self.windDir <=  33.75): windLed = 1
        elif (self.windDir <=  56.25): windLed = 2
        elif (self.windDir <=  78.75): windLed = 3
        elif (self.windDir <= 101.25): windLed = 4
        elif (self.windDir <= 123.75): windLed = 5
        elif (self.windDir <= 146.25): windLed = 6
        elif (self.windDir <= 168.75): windLed = 7
        elif (self.windDir <= 191.25): windLed = 8
        elif (self.windDir <= 213.75): windLed = 9
        elif (self.windDir <= 236.25): windLed = 10
        elif (self.windDir <= 258.75): windLed = 11
        elif (self.windDir <= 281.25): windLed = 12
        elif (self.windDir <= 303.75): windLed = 13
        elif (self.windDir <= 326.25): windLed = 14
        elif (self.windDir <= 348.75): windLed = 15
        else:                          windLed = 0

        windStartLed = (windLed+8)%self.xleds

        for j in range (self.yleds):
            for i in range (0, 8):
                self.windArray[(windLed*self.yleds+(i*self.yleds)+j)%288] = self.windArray[(windLed*self.yleds+(i*self.yleds)+j+self.yleds)%288]
                self.windArray[(windLed*self.yleds-(i*self.yleds)+j)%288] = self.windArray[(windLed*self.yleds-(i*self.yleds)+j-self.yleds)%288]

        for y in range (self.yleds):
            self.windArray[windStartLed*self.yleds+y] = self.black

        for i in range (insertLeds):
            y = random.randint(0,self.yleds-1)
            self.windArray[windStartLed*self.yleds+y]=self.windBackArray[y]

    # displays a single array
    def showLeds(self, colorArray):
        for j in range (self.yleds):
            for i in range (0, 8):
                self.stick.setPixelColor(i*self.yleds*2+j, colorArray [i*self.yleds*2+j])
                self.stick.setPixelColor(self.yleds+i*self.yleds*2+self.yleds-j-1, colorArray [self.yleds+i*self.yleds*2+j])
        self.stick.show()

    # displays all arrays
    def showLedsMerge(self):
        for j in range (self.yleds):
            for i in range (0, 8):
                if (self.rainArray [i*self.yleds*2+j] != self.black):
                    self.stick.setPixelColor(i*self.yleds*2+j, self.rainArray [i*self.yleds*2+j])
                elif (self.windArray [i*self.yleds*2+j] != self.black):
                    self.stick.setPixelColor(i*self.yleds*2+j, self.windArray [i*self.yleds*2+j])
                else:
                    self.stick.setPixelColor(i*self.yleds*2+j, self.backArray [i*self.yleds*2+j])

                if (self.rainArray [self.yleds+i*self.yleds*2+j] != self.black):
                    self.stick.setPixelColor(self.yleds+i*self.yleds*2+self.yleds-j-1, self.rainArray [self.yleds+i*self.yleds*2+j])
                elif (self.windArray [self.yleds+i*self.yleds*2+j] != self.black):
                    self.stick.setPixelColor(self.yleds+i*self.yleds*2+self.yleds-j-1, self.windArray [self.yleds+i*self.yleds*2+j])
                else:
                    self.stick.setPixelColor(self.yleds+i*self.yleds*2+self.yleds-j-1, self.backArray [self.yleds+i*self.yleds*2+j])

        self.temploop = (self.temploop + 1)%4

        f= 18 / (self.maxTempRange - self.minTempRange)
        t= int (-0.5+(self.temperature - self.minTempRange)*f)
        for x in range (self.xleds):
            a=self.tempArray[t]
            c=a[(self.temploop+x)%4]
            if x%2 == 0:
                self.stick.setPixelColor(x*self.yleds+17-t, c)
            else:
                self.stick.setPixelColor(x*self.yleds+18++18+t, c)

        self.stick.show()

#
# display data (matrixThread)
#

def displayMatrix():
    while True:
        weatherLamp.rainMoveDown()
        weatherLamp.windMoveDir()
        weatherLamp.showLedsMerge()
        time.sleep (.2)
        weatherLamp.showLedsMerge()
        time.sleep (.2)

def getWeatherData():
    temp_low, temp_high = weatherForecast(YourKey)
    hour_rain, hour_snow = weatherHourly(YourKey)
    temp_now, wind_kph, wind_dir, real_rain = weatherConditions(YourKey)

    print (currmin)
    print (temp_low, temp_high)
    print (hour_rain, hour_snow)
    print (temp_now, wind_kph, wind_dir, real_rain)

    weatherLamp.minTempRange = min (temp_low, temp_now)
    weatherLamp.maxTempRange = max (temp_high, temp_now)
    weatherLamp.hourRain     = real_rain * 2
    weatherLamp.windSpeed    = wind_kph
    weatherLamp.windDir      = wind_dir
    weatherLamp.temperature  = temp_now

#
# Main
#

stick = Adafruit_NeoPixel(numleds, ledpin, 800000, 5, False, 128)
stick.begin()
minTempRange, maxTempRange = colorRange()
weatherLamp = Lamp (stick, minTempRange, maxTempRange)

# Actual

currmin=datetime.datetime.now()
nextmin=(currmin.minute+5)%60
getWeatherData()
weatherLamp.BackGround()

matrixThread = threading.Thread(name='displayMatrix', target=displayMatrix)
matrixThread.start()

while True:
    currmin=datetime.datetime.now()
    if currmin.minute >= nextmin:
        nextmin=(currmin.minute+10)%60
        getWeatherData()
        weatherLamp.BackGround()
    time.sleep (5)
S