import logging
import numpy as np
import pandas as pd
from utils import printColors
import matplotlib.pyplot as plt

logger = logging.getLogger("appLogger")

class Interraction:

    __export = None
    __graph = None

    def __init__(self, databaseInstance):
        self.__export = Export(databaseInstance)
        self.__graph = Graph(databaseInstance)

    @staticmethod
    def getPredictionType():
        option = input("""Select option (1, 2, 3, 4):
        1) Daily 5-days
        2) Hourly
        3) Weekly 28-days
        4) 18 months\n> """)

        if (option == "1" or option == "2" or option == "3" or option == "4"):
            return (option)
        else:
            print(printColors.FAIL + "Invalid option \"{}\"".format(option) + printColors.ENDC)
        return (None)

    @staticmethod
    def getType():
        option = input("""Select option (1, 2, 3, 4):
        1) Real time data
        2) Interruptions
        3) Archives
        4) Forecast\n> """)

        if (option == "1" or option == "2" or option == "3"):
            return (option)
        elif (option == "4"):
            type = Interraction.getPredictionType()
            if (type == None):
                return (None)
            option += ".{}".format(type)
        else:
            print(printColors.FAIL + "Invalid option \"{}\"".format(option) + printColors.ENDC)
            return (None)
        return (option)

    def commands(self):
        command = None

        try:
            while True:
                command = input("> ").lower()
                if (command == "help"):
                    print(printColors.HEADER + "Availiable commands are:\n- Export\n- Graphic\n" + printColors.ENDC)
                elif (command == "export"):
                    self.__export.export()
                elif (command == "graphic"):
                    print(printColors.HEADER + "Les graphiques sont désactivés car étant sous Docker, il est impossible d'ouvrir des fenêtres avec de l'affichage. Se faisant, les fonctions n'ont pas été testées." + printColors.ENDC)
                    # self.__graph.graph()
                elif (not command and command != ""):
                    print(printColors.FAIL + "Invalid command \"{}\". Use help to get the list of the available commands".format(command) + printColors.ENDC)
        except:
            pass

class Export:
    __databaseInstance = None

    def __init__(self, databaseInstance):
        self.__databaseInstance = databaseInstance

    ## Private method to generate the file
    def __generateFile(self, type, year):
        filePath = None
        rows = None

        if (type == "1"):
            ## Real time data
            filePath = "realTimeData-{}.csv".format(year)
        elif (type == "2"):
            ## Interruptions
            filePath = "interruptions-{}.csv".format(year)
        elif (type == "3"):
            ## Archive
            filePath = "informationArchive-{}.csv".format(year)
        elif (type == "4.1"):
            ## Forecast daily
            filePath = "chargePrevisionDaily-{}.csv".format(year)
        elif (type == "4.2"):
            ## Forecast hourly
            filePath = "chargePrevisionHourly-{}.csv".format(year)
        elif (type == "4.3"):
            ## Forecast Weekly
            filePath = "chargePrevisionWeekly-{}.csv".format(year)
        elif (type == "4.4"):
            ## Forecast 18 months
            filePath = "chargePrevisionEighteenMonths-{}.csv".format(year)

        rows = self.__databaseInstance.getValuesFromYear(type, year)
        if (rows != None):
            data = pd.DataFrame(rows)
            data.to_csv(filePath, index=False)
            print(printColors.OKGREEN + "File generated: " + filePath + printColors.ENDC)
        else:
            print(printColors.FAIL + "Error while generating the file. Please consult the app.log file for details" + printColors.ENDC)

    ## Method to gather the necessary informations to export
    def export(self):
        year = None
        type = Interraction.getType()

        if (type == None):
            return
        year = input("Desired year: ")
        self.__generateFile(type, year)
        return

class Graph:
    __databaseInstance = None

    def __init__(self, databaseInstance):
        self.__databaseInstance = databaseInstance

    ## Helper function used for visualization in the following examples
    def __identify_axes(ax_dict, fontsize=30):
        kw = dict(ha="center", va="center", fontsize=fontsize, color="darkgrey")
        for i, ax in ax_dict.items():
            ax.text(0.5, 0.5, i, transform=ax.transAxes, **kw)

    def __createStackplots(self, graph, values, labels, xLabel="Weeks", yLabel="Charge"):
        graph.xlabel(xLabel)
        graph.ylabel(yLabel)
        fig, ax = graph.subplots()
        ax.stackplot(np.arange(1, 52), values, labels=labels)

    def __createPlots(self, graph, values, xRange=np.arange(1, 52), xLabel="Weeks", yLabel="Charge"):
        plt.xlabel(xLabel)
        plt.ylabel(yLabel)
        fig, ax = plt.subplots()
        ax.plot(xRange, values)

    def __generateComparison(self, type, years):
        ## Variables declaration
        values = np.array([])
        chargeNBVal = np.array([])
        demandeNBVal = np.array([])
        isoNeVal = np.array([])
        emecVal = np.array([])
        mpsVal = np.array([])
        quebecVal = np.array([])
        novaScotiaVal = np.array([])
        peiVal = np.array([])
        reserveMarginAsyncVal = np.array([])
        reserveMarginSyncVal = np.array([])
        reserveMarginVal = np.array([])

        ## Get the ressources from the database
        for year in years:
            if (type == "1"):
                chargeNBVal = np.vstack(chargeNBVal, this.__databaseInstance.getRealTimeDataValueFromYear("chargeNB", year))
                demandeNBVal = np.vstack(demandeNBVal, this.__databaseInstance.getRealTimeDataValueFromYear("demandeNB", year))
                isoNeVal = np.vstack(isoNeVal, this.__databaseInstance.getRealTimeDataValueFromYear("isoNe", year))
                emecVal = np.vstack(emecVal, this.__databaseInstance.getRealTimeDataValueFromYear("emec", year))
                mpsVal = np.vstack(mpsVal, this.__databaseInstance.getRealTimeDataValueFromYear("mps", year))
                quebecVal = np.vstack(quebecVal, this.__databaseInstance.getRealTimeDataValueFromYear("quebec", year))
                novaScotiaVal = np.vstack(novaScotiaVal, this.__databaseInstance.getRealTimeDataValueFromYear("novaScotia", year))
                peiVal = np.vstack(peiVal, this.__databaseInstance.getRealTimeDataValueFromYear("pei", year))
                reserveMarginAsyncVal = np.vstack(reserveMarginAsyncVal, this.__databaseInstance.getRealTimeDataValueFromYear("reserveMarginAsync10Min", year))
                reserveMarginSyncVal = np.vstack(reserveMarginSyncVal, this.__databaseInstance.getRealTimeDataValueFromYear("reserveMarginSync10Min", year))
                reserveMarginVal = np.vstack(reserveMarginVal, this.__databaseInstance.getRealTimeDataValueFromYear("reserveMargin30Min", year))
            elif (type == "2"):
                ## For the interruptions, we compare the number of interruptions
                values = np.vstack(values, len(this.__databaseInstance.getValuesFromYear(year)))
            else:
                values = np.vstack(values, this.__databaseInstance.getValuesFromYear(year))

        ##Creating the graphs:
        if (type == "1"):
            fig = plt.figure(layout="constrained")
            graphs = fig.subplot_mosaic([
                ["chargeNB", "demandeNB", "isoNe"],
                ["emec", "mps", "quebec"],
                ["reserveMarginAsync10Min", "reserveMarginSync10Min", "reserveMargin30Min"],
                ["novaScotia", "pei"]
            ])
            ## Parametting the graphs
            self.__createStackplots(graphs["chargeNB"], chargeNBVal, years)
            self.__createStackplots(graphs["demandeNB"], demandeNBVal, years)
            self.__createStackplots(graphs["isoNe"], isoNeVal, years)
            self.__createStackplots(graphs["emec"], emecVal, years)
            self.__createStackplots(graphs["mps"], mpsVal, years)
            self.__createStackplots(graphs["quebec"], quebecVal, years)
            self.__createStackplots(graphs["reserveMarginAsync10Min"], reserveMarginAsyncVal, years)
            self.__createStackplots(graphs["reserveMarginSync10Min"], reserveMarginSyncVal, years)
            self.__createStackplots(graphs["reserveMargin30Min"], reserveMarginVal, years)
            self.__createStackplots(graphs["novaScotia"], novaScotiaVal, years)
            self.__createStackplots(graphs["pei"], peiVal, years)
            self.__identify_axes(graphs)
            plt.show()
        elif (type == "2"):
            self.__createPlots(plt, values, xRange=len(values), yLabel="Interruption number")
            plt.show()
        else:
            self.__createStackplots(plt, values, years)
            plt.show()

    def __generateEvolution(self, type, year):
        ## Variables declaration
        values = None
        chargeNBVal = None
        demandeNBVal = None
        isoNeVal = None
        emecVal = None
        mpsVal = None
        quebecVal = None
        novaScotiaVal = None
        peiVal = None
        reserveMarginAsyncVal = None
        reserveMarginSyncVal = None
        reserveMarginVal = None

        ## Get the ressources from the database
        if (type == "1"):
            chargeNBVal = this.__databaseInstance.getRealTimeDataValueFromYear("chargeNB", year)
            demandeNBVal = this.__databaseInstance.getRealTimeDataValueFromYear("demandeNB", year)
            isoNeVal = this.__databaseInstance.getRealTimeDataValueFromYear("isoNe", year)
            emecVal = this.__databaseInstance.getRealTimeDataValueFromYear("emec", year)
            mpsVal = this.__databaseInstance.getRealTimeDataValueFromYear("mps", year)
            quebecVal = this.__databaseInstance.getRealTimeDataValueFromYear("quebec", year)
            novaScotiaVal = this.__databaseInstance.getRealTimeDataValueFromYear("novaScotia", year)
            peiVal = this.__databaseInstance.getRealTimeDataValueFromYear("pei", year)
            reserveMarginAsyncVal = this.__databaseInstance.getRealTimeDataValueFromYear("reserveMarginAsync10Min", year)
            reserveMarginSyncVal = this.__databaseInstance.getRealTimeDataValueFromYear("reserveMarginSync10Min", year)
            reserveMarginVal = this.__databaseInstance.getRealTimeDataValueFromYear("reserveMargin30Min", year)
        elif (type == "2"):
            ## For the interruptions, we compare the number of interruptions
            values = len(this.__databaseInstance.getValuesFromYear(year))
        else:
            values = this.__databaseInstance.getValuesFromYear(year)

        ##Creating the graphs:
        if (type == "1"):
            try:
                fig = plt.figure(layout="constrained")
                graphs = fig.subplot_mosaic([
                    ["chargeNB", "demandeNB", "isoNe"],
                    ["emec", "mps", "quebec"],
                    ["reserveMarginAsync10Min", "reserveMarginSync10Min", "reserveMargin30Min"],
                    ["novaScotia", "pei"]
                ])
                ## Parametting the graphs
                self.__createPlots(graphs["chargeNB"], chargeNBVal)
                self.__createPlots(graphs["demandeNB"], demandeNBVal)
                self.__createPlots(graphs["isoNe"], isoNeVal)
                self.__createPlots(graphs["emec"], emecVal)
                self.__createPlots(graphs["mps"], mpsVal)
                self.__createPlots(graphs["quebec"], quebecVal)
                self.__createPlots(graphs["reserveMarginAsync10Min"], reserveMarginAsyncVal)
                self.__createPlots(graphs["reserveMarginSync10Min"], reserveMarginSyncVal)
                self.__createPlots(graphs["reserveMargin30Min"], reserveMarginVal)
                self.__createPlots(graphs["novaScotia"], novaScotiaVal)
                self.__createPlots(graphs["pei"], peiVal)
                self.__identify_axes(graphs)
                plt.show()
            except Exception as error:
                logger.error("Error: %s", error)
        elif (type == "2"):
            self.__createPlots(plt, values, xRange=len(values), yLabel="Interruption number")
            plt.show()
        else:
            self.__createPlots(plt, values)
            plt.show()

    def graph(self):
        year = None
        type = None
        option = input("""Select graph style (1, 2)
        1) Evolution
        2) Comparison\n> """)

        if (option == "1"):
            print("Which data do you want to see the evolution?")
            type = Interraction.getType()
            if (type == None):
                return
            year = input("Desired year: ")
            self.__generateEvolution(type, year)
        elif (option == "2"):
            print("Which data do you want to compare?")
            type = Interraction.getType()
            if (type == None):
                return
            years = input("Please enter the differents years separated with spaces you want the comparison to be done: ")
            self.__generateComparison(type, years)
        return


import cv2

from camera import CameraThread

def get_cameras():
    max_tested = 100
    for i in range(max_tested):
        temp_camera = cv2.VideoCapture(i)
        if temp_camera.isOpened():
            temp_camera.release()
            continue
        return i

class CameraEnv:
	def __init__(self, env):
		self.env = env
		self.camera_list = [CameraThread() for i in range(10)] #example

	def get_observation(self):
		camera_obs = [c.get_image() for c in self.camera_list]

		obs = self.env.get_observation() #{ee_pos, ee_vel, etc}


from dataclasses import dataclass
from time import time

# each social channel has a type
# and the current number of followers
# SocialChannel = tuple[str, int]
@dataclass
class SocialChannel:
    channel_type: str
    followers: int

# each post has a message and the timestamp when it should be posted
# Post = tuple[str, int]
@dataclass
class Post:
    message: str
    timestamp: int

def post_to_channel(channel: SocialChannel, message: str) -> None:
    type, followers = channel.channel_type, channel.followers
    print(f"{type} channel: {message}")

def post_to_facebook(channel: SocialChannel, message: str) -> None:
    type, followers = channel
    print(f"{type} channel: {message}")

def post_to_twitter(channel: SocialChannel, message: str) -> None:
    type, followers = channel
    print(f"{type} channel: {message}")

def post_a_message(channel: SocialChannel, message: str) -> None:
    type, followers = channel.channel_type, channel.followers
    if type in ["youtube", "facebook", "twitter"]:
        print(f"{type} channel: {message}")
    # elif type == "facebook":
    #     post_to_facebook(channel, message)
    # elif type == "twitter":
    #     post_to_twitter(channel, message)

def process_schedule(posts: list[Post], channels: list[SocialChannel]) -> None:
    for post in posts:
        message, timestamp = post.message, post.timestamp
        for channel in channels:
            if timestamp <= time():
                post_a_message(channel, message)

def main() -> None:
    posts = [
        Post(
            "Grandma's carrot cake is available again (limited quantities!)!",
            1568123400,
        ),
        Post("Get your carrot cake now, the promotion ends today!", 1568133400),
    ]
    channels = [
        SocialChannel("youtube", 100),
        SocialChannel("facebook", 100),
        SocialChannel("twitter", 100),
    ]

    process_schedule(posts, channels)

if __name__ == "__main__":
    main()


from .. import models,schema
from fastapi import FastAPI, HTTPException,Depends,status,APIRouter
from sqlalchemy.orm import Session
from ..database import get_db

router=APIRouter(
    tags=['post']
)
#read all posts
@router.get("/posts")
def view_posts(db: Session=Depends(get_db)):
    posts = db.query(models.Post).all()
    return posts

#create a new post
@router.post("/posts",status_code=status.HTTP_201_CREATED)
def create_post(post:schema.Post, db: Session=Depends(get_db)):
    new_post = models.Post(title=post.title,content=post.content,image_url=post.image_url)
    db.add(new_post)
    db.commit()
    db.refresh(new_post)
    return new_post

#delete a new post
@router.delete("/posts/{id}")
def delete_post(id:int,db: Session = Depends(get_db)):
    post = db.query(models.Post).filter(models.Post.post_id == id).first()
    if post is None:
        raise HTTPException(status_code=404, detail="Post not found")
    db.delete(post)
    db.commit()
    return {"status_code": 200, "detail":"Post deleted successfully"}

#update a certain post
@router.put("/posts/{id}")
def update_post(id:int, post:schema.Post, db: Session = Depends(get_db)):
    db_post = db.query(models.Post).filter(models.Post.post_id == id).first()
    if db_post is None:
        raise HTTPException(status_code=404, detail="Post not found")
    db_post.title = post.title
    db_post.content = post.content
    db.commit()
    return {"status_code": 200, "detail":"Post updated successfully"}