import time
import board
import displayio
import neopixel
import terminalio

from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
from adafruit_pm25.i2c import PM25_I2C
import adafruit_scd4x
import adafruit_displayio_ssd1306

from adafruit_display_text import label

# Set up pixel coloring
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.1, auto_write=False)
red = (80, 0, 0)
yellow = (75, 80, 0)
green = (0, 80, 0)

reset_pin = None
i2c = board.STEMMA_I2C()

# Set-up pm25 air sensor
pm25 = PM25_I2C(i2c, reset_pin)

# Set-up scd4x CO2 sensor
scd4x = adafruit_scd4x.SCD4X(i2c)
scd4x.start_periodic_measurement()

time.sleep(5)

# pm25 air sensor data set-up
try:
    aqdata = pm25.read()
    pm25Data = int(aqdata["pm25 standard"])
except RuntimeError:
    pm25Data = 0

co2Sensor = scd4x.CO2
tempSensor = scd4x.temperature
humiditySensor = scd4x.relative_humidity

def rate_pm25(pm25_data):
    if pm25_data <= 12:
        pm25_color = green
        pm25_extra = "OK"
    elif pm25_data <= 35:
        pm25_color = yellow
        pm25_extra = "MID"
    else:
        pm25_color = red
        pm25_extra = "BAD"
    return pm25_color, pm25_extra

def rate_co2(co2_data):
    if co2_data <= 500:
        co2_extra = "OK"
    elif co2_data <= 1000:
        co2_extra = "MEH"
    elif co2_data <= 1400:
        co2_extra = "-15%"
    elif co2_data <= 1600:
        co2_extra = "-50%"
    elif co2_data <= 2000:
        co2_extra = "VENT"
    else:
        co2_extra = "BAD!"
    return co2_extra

def rate_temperature(temp_data):
    if temp_data <= 30:
        temp_extra = "COLD"
    elif temp_data <= 80:
        temp_extra = "OK"
    elif temp_data <= 90:
        temp_extra = "WARM"
    else:
        temp_extra = "HOT"
    return temp_extra

def rate_humidity(humidity_data):
    if humidity_data < 55:
        humidity_extra = "DRY"
    elif humidity_data == 55 and humidity_data <= 65:
        humidity_extra = "OK"
    else:
        humidity_extra = "WET"
    return humidity_extra

def c_to_f(temp_data):
    temperature_celsius = temp_data
    temperature_fahrenheit = temperature_celsius * 9 / 5 + 32
    return temperature_fahrenheit

# display set-up
displayio.release_displays()
i2c = board.I2C()
display_bus = displayio.I2CDisplay(i2c, device_address=0x3C)
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32)

group = displayio.Group()

display.root_group = group

# Background set-up
color_bitmap = displayio.Bitmap(128, 32, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x000000  # black
bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
group.append(bg_sprite)

TOTAL_WIDTH = 128
TOTAL_HEIGHT = 32

SECTION_WIDTH = (TOTAL_WIDTH - 10) // 4
SECTION_HEIGHT = (TOTAL_HEIGHT // 3) - 1

# PM2.5 text set-up
pm25_title = label.Label(
    terminalio.FONT, text="PM25", color=0xFFFFFF, x=0, y=SECTION_HEIGHT
)
pm25_text = label.Label(
    terminalio.FONT, text="%d" % pm25Data, color=0xFFFFFF, x=0, y=(SECTION_HEIGHT * 2)
)
pm25_extra_text = label.Label(
    terminalio.FONT, text="", color=0xFFFFFF, x=0, y=(SECTION_HEIGHT * 3)
)
group.append(pm25_title)
group.append(pm25_text)
group.append(pm25_extra_text)

# CO2 text set-up
co2_title = label.Label(
    terminalio.FONT, text="CO2", color=0xFFFFFF, x=((SECTION_WIDTH * 2) - SECTION_WIDTH), y=(SECTION_HEIGHT)
)
co2_text = label.Label(
    terminalio.FONT, text="%d" % co2Sensor, color=0xFFFFFF, x=((SECTION_WIDTH * 2) - SECTION_WIDTH), y=(SECTION_HEIGHT * 2)
)
co2_extra_text = label.Label(
    terminalio.FONT, text="", color=0xFFFFFF, x=((SECTION_WIDTH * 2) - SECTION_WIDTH), y=(SECTION_HEIGHT * 3)
)
group.append(co2_title)
group.append(co2_text)
group.append(co2_extra_text)

# Temperature text set-up
temp_title = label.Label(
    terminalio.FONT, text="TEMP", color=0xFFFFFF, x=((SECTION_WIDTH * 3) - SECTION_WIDTH), y=(SECTION_HEIGHT)
)
temp_text = label.Label(
    terminalio.FONT, text="%d" % c_to_f(tempSensor), color=0xFFFFFF, x=((SECTION_WIDTH * 3) - SECTION_WIDTH), y=(SECTION_HEIGHT * 2)
)
temp_extra_text = label.Label(
    terminalio.FONT, text="", color=0xFFFFFF, x=((SECTION_WIDTH * 3) - SECTION_WIDTH), y=(SECTION_HEIGHT * 3)
)
group.append(temp_title)
group.append(temp_text)
group.append(temp_extra_text)

# Humidity text set-up
humidity_title = label.Label(
    terminalio.FONT, text="HUMD", color=0xFFFFFF, x=((SECTION_WIDTH * 4) - SECTION_WIDTH), y=(SECTION_HEIGHT)
)
humidity_text = label.Label(
    terminalio.FONT, text="%d" % humiditySensor, color=0xFFFFFF, x=((SECTION_WIDTH * 4) - SECTION_WIDTH), y=(SECTION_HEIGHT * 2)
)
humidity_extra_text = label.Label(
    terminalio.FONT, text="", color=0xFFFFFF, x=((SECTION_WIDTH * 4) - SECTION_WIDTH), y=(SECTION_HEIGHT * 3)
)
group.append(humidity_title)
group.append(humidity_text)
group.append(humidity_extra_text)

sensor_texts = [pm25_text, co2_text, temp_text, humidity_text]
sensor_data = [pm25Data, co2Sensor, tempSensor, humiditySensor]
sensor_extra_texts = [pm25_extra_text, co2_extra_text, temp_extra_text, humidity_extra_text]

sensor_clock = ticks_ms()
sensor_check = 5000
first_run = True

while True:
    if scd4x.data_ready:
        if first_run or ticks_diff(ticks_ms(), sensor_clock) > sensor_check:
            co2Sensor = scd4x.CO2
            tempSensor = c_to_f(scd4x.temperature)
            humiditySensor = scd4x.relative_humidity

            try:
                aqdata = pm25.read()
                pm25Data = int(aqdata["pm25 standard"])
            except RuntimeError:
                print("Unable to read from PM2.5 sensor, no new data...")
                continue
            
            pm25_color, pm25_extra = rate_pm25(pm25Data)
            pixel.fill(pm25_color)
            pixel.show()

            # updating the array of sensor data values
            sensor_data = [pm25Data, co2Sensor, tempSensor, humiditySensor]
            sensor_extra = [pm25_extra, rate_co2(co2Sensor), rate_temperature(tempSensor), rate_humidity(humiditySensor)]

            for s in range(4):
                sensor_texts[s].text = "%d" % sensor_data[s]
                sensor_extra_texts[s].text = sensor_extra[s]
                # print("updated %d data" % sensor_data[s])
                time.sleep(0.2)
            sensor_clock = ticks_add(sensor_clock, sensor_check)

        if first_run:
            sensor_clock = ticks_ms()
            first_run = False