#!/usr/bin/env python

import math
import random
import json
import requests
import datetime
import flask
import numpy
import pandas
import poobrains

from flask import redirect


app = poobrains.app

@app.route('/')
def front():
    return redirect(News.url())


class TestSubForm(poobrains.form.Fieldset):

    oink = poobrains.form.fields.Text(label="OMGWTF")
    foo = poobrains.form.fields.Text(label="SUBfoo")
    submit = poobrains.form.Button('submit', label="SUBSUBMIT") 


@app.expose('/form')
class TestForm(poobrains.form.Form):

    foo = poobrains.form.fields.Text()
    bar = TestSubForm()
    optin = poobrains.form.fields.Checkbox(label="Opt-in", default=False, required=True, choices=[(True, None)])
    radio = poobrains.form.fields.Radio(type=poobrains.form.types.INT, choices=[(1, 'One'), (5, 'Five'), (23, 'Twentythree'), (42, 'Fortytwo')])
    multicheck = poobrains.form.fields.Checkbox(label="Check 'em", type=poobrains.form.types.STRING, choices=[('dubs', 'dubs'), ('trips', 'TRIPS'), ('quads', 'QUADS!1!!!!')], multi=True)
    completeme = poobrains.form.fields.Text(label="Lookit me, I can autocomplete without JS!", choices=[('Mr. Foo', 'foo'), ('Mr. Florb', 'florb'), ('Ms. Bar', 'bar')])
    ranged = poobrains.form.fields.Range()
    trigger = poobrains.form.Button('submit', label='Hit me!')

    def process(self, submit):

        flask.flash('TestForm.process called!')
        return self


@app.expose('/news', mode='full')
class News(poobrains.commenting.Commentable):

    """ This is the news class docstring """

    class Meta:
        search_fields = ['title', 'name', 'text']

    title = poobrains.storage.fields.CharField()
    text = poobrains.md.MarkdownField()


@app.expose('/paste', mode='full', title='Copypasta')
class Paste(poobrains.tagging.Taggable):

    type = poobrains.storage.fields.CharField()
    text = poobrains.storage.fields.TextField()


@app.site.box('menu_main')
def menu_main():

    menu = poobrains.rendering.Menu('main')

    try:
        News.permissions['read'].check(flask.g.user)
        menu.append(News.url(), 'News')
    except poobrains.auth.AccessDenied:
        pass
    
    try:
        Paste.permissions['read'].check(flask.g.user)
        menu.append(Paste.url(), 'Pastes')
    except poobrains.auth.AccessDenied:
        pass

    for url, caption in poobrains.auth.Page.main_menu_entries():
        menu.append(url, caption)

    return menu


class NonExposed(poobrains.auth.Administerable):

    text = poobrains.storage.fields.TextField()


class NonExposedB(NonExposed):
    pass

class AVeryVeryLongNameToTestMenuPositioning(poobrains.auth.Administerable):
    
    florp = poobrains.storage.fields.BooleanField()


#class MultiPK(poobrains.auth.Administerable):
#
#    class Meta:
#        primary_key = peewee.CompositeKey('pk_a', 'pk_b')
#
#    pk_a = poobrains.storage.fields.IntegerField()
#    pk_b = poobrains.storage.fields.IntegerField()
#
#
#class NestedHandle(poobrains.auth.Administerable):
#
#    class Meta:
#        primary_key = peewee.CompositeKey('foreign', 'local')
#
#    foreign = poobrains.storage.fields.ForeignKeyField(MultiPK)
#    local = poobrains.storage.fields.CharField()


@app.site.listing(NonExposedB, '/custom', mode='full', title='Custom Listing')
def list_nonexposed(listing):

    return listing


class NASAApproaches(poobrains.svg.plot.Dataset):

    def fill(self, spkid: int = 2099942) -> None: # default spkid for apophis

        try:
            spkid = int(spkid)
        except ValueError:
            raise poobrains.errors.ValidationError("Invalid SPK ID; must be an integer.")

        response = requests.get('https://api.nasa.gov/neo/rest/v1/neo/%d?api_key=DEMO_KEY' % spkid)

        if response.status_code != 200:
            raise poobrains.errors.ValidationError("NASA API responded with error code %d." % response.status_code)

        data = json.loads(response.text)

        self.title = "Close approaches of %s" % (data['name_limited'] if 'name_limited' in data else data['name'])
        self.description = "**%s** belongs to orbit class **%s**; %s" % (data['name'], data['orbital_data']['orbit_class']['orbit_class_type'], data['orbital_data']['orbit_class']['orbit_class_description'])


        rawindex = []
        rawdata = {}
        #rawdata = []
        for approach in data['close_approach_data']:

            x = float(approach['epoch_date_close_approach'])
            y = float(approach['miss_distance']['kilometers'])

            #self.add_datapoint(x, y)
            rawdata[x] = y
            rawindex.append(x)
            #rawdata.append(y)

        #app.logger.debug('Approach dataset size: %d' % len(rawdata))
        #rawdata = [rawdata]

        #self.data = pandas.DataFrame(data=rawdata, index=[spkid])
        #self.data = pandas.DataFrame([pandas.Series(rawdata, index=rawindex)])
        self.data = pandas.DataFrame(pandas.Series(rawdata))


class StockWeekly(poobrains.svg.plot.Dataset):

    def fill(self, symbol='FB'):

        response = requests.get('https://www.alphavantage.co/query?function=TIME_SERIES_WEEKLY&symbol=%s&apikey=%s' % (symbol, app.config['ALPHAVANTAGE_API_KEY']))

        if response.status_code != 200:
            raise poobrains.errors.ValidationError("AlphaVantage API responded with error code %d." % response.status_code)

        data = json.loads(response.text)

        dates = [x for x in data['Weekly Time Series'].keys()]
        first_date = dates[-1]
        last_date = dates[0]

        self.title = symbol
        self.description = f"Data from {first_date} to {last_date}"

        rawdata = {}
        for datestring, datapoint in data['Weekly Time Series'].items():

            y, m, d = [int(x) for x in datestring.split('-')]
            date = datetime.datetime(y, m, d)

            x = date.timestamp()
            #x = date
            #y = (float(datapoint['1. open']) + float(datapoint['4. close'])) / 2
            y = float(datapoint['4. close'])
            #error_lower = y - float(datapoint['3. low'])
            #error_upper = y - float(datapoint['2. high'])

            #self.add_datapoint(x, y, error_lower=error_lower, error_upper=error_upper)
            rawdata[x] = y

        self.data = pandas.DataFrame(pandas.Series(data=rawdata))



class ConstrainedRandom(poobrains.svg.plot.Dataset):

    def fill(self, magnitude: int = 2, length: int = 16) -> None:

        magnitude, length = int(magnitude), int(length)

        ranges = []
        for i in range(0, magnitude):
            ranges.append(sorted((random.random(), random.random())))

        #rawdata = pandas.Series(name=self.__class__.__name__, index=[0])
        rawdata = []
        for i in range(0, length):

            y = random.random()
            for r in ranges:
                y = r[0] + (r[1] - r[0]) * y

            r = ranges[round((len(ranges) - 1) * random.random())] # select random range
            y = r[0] + (r[1] - r[0]) * y

            #y %= (i + 1)
            #y = i % y
            
            y *= math.sin(2 * math.pi * float(i) / length)

            #self.add_datapoint(i, y) #, error_lower=y - r[0], error_upper=r[1] - y)
            #rawdata.iat[i] = y
            rawdata.append(y)

        #self.data.iat[0] = rawdata
        self.data = pandas.DataFrame(data=rawdata)


class Sine(poobrains.svg.plot.Dataset):

    def fill(self, length: int=10):

        inc = (2 * math.pi) / (length - 1)
        #inc = 1.0 / (length - 1)

        rawdata = {}
        x = 0

        for _ in range(0, length):

            rawdata[x] = math.sin(x)
            x += inc

        self.data = pandas.DataFrame(columns=['sine'], index=rawdata.keys(), data=rawdata.values())


class RandomMap(poobrains.svg.geo.GeoData):

    def fill(self):

        data = poobrains.svg.geo.geojson.MultiPolygon()

        for poly_num in range(0, random.randrange(1, 8)):

            points = []

            for point_num in range(0, random.randrange(3, 7)):
                points.append((random.randrange(-180, 181),random.randrange(-75, 76)))

            data.coordinates.append([points])

        self.data = poobrains.svg.geo.geojson.dumps(data)


if __name__ == '__main__':

    #app.run()
    poobrains.app.main()
