import smbus2
import bme280
import datetime, time
import pygame
from pygame.locals import *
import sys, os
import csv
from enum import Enum
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import matplotlib.font_manager as mfontm
import matplotlib.dates as mdates
import concurrent.futures as cf

class CONST():
    BME280_PORT = 1 # BME280のポート
    BME280_ADDRESS = 0x76 # BME280のアドレス 
    PYGAME_SCREEN_SIZE = (480320# ウィンドウのサイズ
    LOG_INTERVAL = 5 # ログに記録する間隔(分)(60の約数で設定する)
    LOG_DIR = "./BME280log/" # ログを保存するディレクトリ
    LOG_NAME = " BME280.log" # 出力ログファイル名
    PIC_DIR = "./BME280log/" # 画像を出力するディレクトリ
    PIC_NAME = " BME280.png" # 出力画像ファイル名
    PIC_SIZE = (4.83.2# 出力画像サイズ(480 x 320)
    LINE_WIDTH = 2 # 折れ線グラフの線の太さ
    FONT_FILENAME = "ipag.ttf" # 日本語フォント
    DATE_FONT_SIZE = 35 # 日時の文字サイズ
    DATA_FONT_SIZE = 62 # 温度・湿度・気圧の文字サイズ
    DATA_BACKGROUND_COLOR = (000# リアルタイムデータの背景色
    DATA_FONT_COLOR = (255255255# リアルタイムデータの文字色
    MESSAGE_FONT_SIZE = 20 # メッセージの文字サイズ
    MESSAGE_CORNER = 10 # 角丸四角の丸み
    MESSAGE_BACKGROUND_COLOR = (255250240# メッセージの背景色
    MESSAGE_FONT_COLOR = (000# メッセージの文字色

class MODE(Enum):
    REALTIME = 1
    WAITING = 2
    GRAPH = 3
    ERR_LOG_NOT_FOUND = 4

def main():
    # BME280初期設定
    bus = smbus2.SMBus(CONST.BME280_PORT)
    calibration_params = bme280.load_calibration_params(bus, CONST.BME280_ADDRESS)
    # pygame初期設定
    pygame.init()
    #screen = pygame.display.set_mode((CONST.PYGAME_SCREEN_SIZE), FULLSCREEN)
    screen = pygame.display.set_mode((CONST.PYGAME_SCREEN_SIZE))
    pygame.display.set_caption("温度・湿度・気圧計")
    executor = cf.ThreadPoolExecutor(max_workers=2)
    # メインループ
    mode = MODE.REALTIME
    old_time = time.perf_counter()
    while True:
        new_time = time.perf_counter()
        if new_time - old_time >= 1:
            old_time = new_time
            # BME280からデータを読み出す
            data = bme280.sample(bus, CONST.BME280_ADDRESS, calibration_params)
            screen.fill(CONST.DATA_BACKGROUND_COLOR)
            if mode != MODE.GRAPH:
                show_realtime_data(data, screen)
            if mode == MODE.WAITING:
                show_message_box("グラフ作成中...", screen)
                if future.done():
                    mode = MODE.GRAPH
            elif mode == MODE.GRAPH:
                pic_filename = get_pic_filename()
                image = pygame.image.load(pic_filename)
                screen.blit(image,(0,0))
            elif mode == MODE.ERR_LOG_NOT_FOUND:
                show_message_box("ログがありません", screen)
            pygame.display.update()
            # LOG_INTERVAL分毎にCSVに保存する
            if data.timestamp.second == 0 and data.timestamp.minute % CONST.LOG_INTERVAL == 0:
                log_filename = get_log_filename()
                f = open(log_filename, "a")
                writer = csv.writer(f)
                csvrow = []
                csvrow.append("{0:%Y/%m/%d %H:%M:%S}".format(data.timestamp))
                csvrow.append("{0:.1f}".format(data.temperature))
                csvrow.append("{0:.1f}".format(data.humidity))
                csvrow.append("{0:.1f}".format(data.pressure))
                writer.writerow(csvrow)
                f.close()
        # イベント処理
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            if event.type == MOUSEBUTTONDOWN:
                if mode == MODE.REALTIME:
                    log_filename = get_log_filename()
                    if os.path.exists(log_filename):
                        future = executor.submit(create_graph)
                        mode = MODE.WAITING
                    else:
                        mode = MODE.ERR_LOG_NOT_FOUND
                elif mode == MODE.GRAPH or mode == MODE.ERR_LOG_NOT_FOUND:
                    mode = MODE.REALTIME
                    
        time.sleep(0.1)

# ログファイル名を取得する
def get_log_filename(days_ago=0):
    date = "{0:%Y.%m.%d}".format(datetime.date.today() - datetime.timedelta(days=days_ago))
    return CONST.LOG_DIR + date + CONST.LOG_NAME

# グラフ画像のファイル名を取得する
def get_pic_filename(days_ago=0):
    date = "{0:%Y.%m.%d}".format(datetime.date.today() - datetime.timedelta(days=days_ago))
    return CONST.PIC_DIR + date + CONST.PIC_NAME

# リアルタイムのデータを表示する
def show_realtime_data(data, screen):
    data_str = []
    data_str.append("{0:%Y年%m月%d日 %H時%M分%S秒}".format(data.timestamp))
    data_str.append("温度:{0:>6.1f} ℃".format(data.temperature))
    data_str.append("湿度:{0:>6.1f} %".format(data.humidity))
    data_str.append("気圧:{0:>6.1f} hPa".format(data.pressure))
    myfont1 = pygame.font.Font(CONST.FONT_FILENAME, CONST.DATE_FONT_SIZE)
    myfont2 = pygame.font.Font(CONST.FONT_FILENAME, CONST.DATA_FONT_SIZE)
    render_str = []
    render_str.append(myfont1.render(data_str[0], True, CONST.DATA_FONT_COLOR))
    render_str.append(myfont2.render(data_str[1], True, CONST.DATA_FONT_COLOR))
    render_str.append(myfont2.render(data_str[2], True, CONST.DATA_FONT_COLOR))
    render_str.append(myfont2.render(data_str[3], True, CONST.DATA_FONT_COLOR))                       
    screen.blit(render_str[0], (010))
    screen.blit(render_str[1], (070))
    screen.blit(render_str[2], (0155))
    screen.blit(render_str[3], (0240))

# メッセージを表示する
def show_message_box(text, screen):
    bgc = CONST.MESSAGE_BACKGROUND_COLOR
    fc = CONST.MESSAGE_FONT_COLOR
    cn = CONST.MESSAGE_CORNER
    info = pygame.display.Info()
    scr_w, scr_h = info.current_w, info.current_h
    myfont = pygame.font.Font(CONST.FONT_FILENAME, 20)
    mes = myfont.render(text, True, (000))
    mes_w, mes_h = mes.get_size()
    mes_l = (scr_w - mes_w) // 2
    mes_t = (scr_h - mes_h) // 2
    rect_w, rect_h = int(mes_w * 1.5), mes_h * 3
    rect_l, rect_t = (scr_w - rect_w) // 2, (scr_h - rect_h) // 2
    rect_r, rect_b = rect_l + rect_w, rect_t + rect_h 
    in_rect_l, in_rect_t, in_rect_r, in_rect_b = rect_l + cn, rect_t + cn, rect_r - cn, rect_b - cn
    in_rect_w, in_rect_h = in_rect_r - in_rect_l, in_rect_b - in_rect_t
    pygame.draw.circle(screen, bgc, (in_rect_l, in_rect_t), cn)
    pygame.draw.circle(screen, bgc, (in_rect_r, in_rect_t), cn)
    pygame.draw.circle(screen, bgc, (in_rect_l, in_rect_b), cn)
    pygame.draw.circle(screen, bgc, (in_rect_r, in_rect_b), cn)
    pygame.draw.rect(screen, bgc, (in_rect_l, rect_t, in_rect_w, rect_h))
    pygame.draw.rect(screen, bgc, (rect_l, in_rect_t, rect_w, in_rect_h))
    screen.blit(mes, (mes_l, mes_t))

# グラフを描画して保存する
def create_graph():
    log_filename = get_log_filename()
    pic_filename = get_pic_filename()        
    # データを読み込む
    data = []
    with open(log_filename, "r"as f:
        reader = csv.reader(f)
        for row in reader:
            timestamp = datetime.datetime.strptime(row[0] ,"%Y/%m/%d %H:%M:%S")
            data.append([timestamp, float(row[1]), float(row[2]), float(row[3])])
    data = [list(x) for x in zip(*data)] # 転置する
    # グラフ作成
    # フォントファイルの指定
    fp = mfontm.FontProperties(fname="./ipag.ttf")
    # 3つ重ね合わせたグラフ作成
    fig = plt.figure(figsize=CONST.PIC_SIZE)
    plt.subplots_adjust(left=0.07, bottom=0.15, top=0.9, right=1)
    ax1 = fig.add_subplot(111)
    ax2 = ax1.twinx()
    ax3 = ax1.twinx()
    ax1.plot(data[0], data[1], color='orange', alpha=1.0, lw=CONST.LINE_WIDTH, antialiased=True)
    ax2.plot(data[0], data[2], color='deepskyblue', alpha=0.6, lw=CONST.LINE_WIDTH, antialiased=True)
    ax3.plot(data[0], data[3], color='green', alpha=0.8, lw=CONST.LINE_WIDTH, antialiased=True)
    # Y軸の目盛り設定
    ax1.set_ylim(040)
    ax2.set_ylim(0100)
    ax3.set_ylim(9601020)
    ax1.yaxis.set_major_locator(ticker.MultipleLocator(5))
    ax2.yaxis.set_major_locator(ticker.MultipleLocator(10))
    ax3.yaxis.set_major_locator(ticker.MultipleLocator(10))
    # 気圧の目盛りを外側に表示する
    fig.subplots_adjust(right=0.80)
    ax3.spines["right"].set_position(("axes"1.13))
    # X軸のラベル(時分)
    hoursfmt = mdates.DateFormatter("%H:%M")
    ax1.xaxis.set_major_formatter(hoursfmt)
    xlabels = ax1.get_xticklabels()
    plt.setp(xlabels, rotation=45)
    # Y軸のラベル(単位)
    ax1.yaxis.set_label_coords(-0.0451.03)
    ax2.yaxis.set_label_coords(1.071.09)
    ax3.yaxis.set_label_coords(1.211.09)
    ax1.set_ylabel("(℃)", fontproperties=fp, rotation=0)
    ax2.set_ylabel("(%)", fontproperties=fp, rotation=0)
    ax3.set_ylabel("(hPa)", fontproperties=fp, rotation=0)
    # 凡例
    fig.legend(["温度""湿度""気圧"], bbox_to_anchor=(00), bbox_transform=ax1.transAxes, loc="lower left", prop=fp)
    # 保存
    plt.savefig(pic_filename)

if __name__ == "__main__":
    main()